Need for Speed: HTML-Links filtern

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich arbeite gerade an einem Programm, dass mir aus aus Routen und Templates alle Seiten generiert, die durch die Routen abgedeckt werden.
Das Ganze basiert darauf, dass die die Links in den generierten Seiten gesammelt werden, mit den Routen verglichen werden und alle abgedeckten Seiten mit dem Handler in eine Queue packt.
Funktioniert auch wunderbar und ist einigermassen flott, aber die Filterung und Zuordnung sehr haeufig passiert, hab ich mir ueberlegt, ob da nicht mehr zu holen ist.

http://paste.pocoo.org/show/131008/

Das ganze natuerlich mit der Stdlib ;)
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Ungetestet:

Code: Alles auswählen

# Der re-Ausdruck war nicht ganz eindeutig. Der hier is sicherer.
# Außerdem ausgelagert, damit er nur einmal kompiliert werden muss
link_re = re.compile(r'''<a[^>]+href\s*=\s*['"]([^'"]+)''', re.U)
    
def gather_links(fname, encoding, links=None):
    # Benutze set() statt list(), dann bekommst du das 'unique' umsonst dazu
    links = links and set(links) or set()
    # Lese alles auf einmal. Mehr Speicherverbrauch aber weniger Rechenzeit.
    # Gibt Datei auch schneller wieder frei.
    with codecs.open(fname, encoding=encoding) as fobj:
        html = fobj.read()
    # Wir brauchen keine match-objekte. findall ist da schneller.
    links.update(link_re.findall(html))
    # set() ist immer unique. Wir sind also fertig. Da du aber ne liste haben willst, casten wir noch
    return list(links)

def pair_links(links, routing_table):
    # Dict statt list
    pairs = dict()
    for link in links:
        for pattern in routing_table:
            match = pattern.match(link)
            if match:
                fname = match.groupdict().get('fname', False)
                if fname:
                    pairs.setdefault(fname,set()).add(routing_table[pattern])
                    break
    # Und das ganze wieder auseinander falten, da die Ausgabe ja ne tuple-liste sein soll
    out = list()
    for fname, hset in pairs.iteritems():
        out.extend([(fname, handler) for handler in hset])
    return out
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Danke fuers drueberschaun, die Verbesserungen bei den Links geben einen netten Boost. Beim pairen hab ich aber nur das dict uebernommen, da ich keine mehreren Handler zulassen moechte ;)

Das ganze generiert jetzt ca. 150 Seiten in 2,5s (knappe 10% kuerzer als davor). Ich habe aber das Gefuehl, dass das Schreiben und wieder einlesen zum Bottleneck wird, darum Versuch ichs mal damit die durchsuchten Seiten im RAM zu lassen und cache die schon gefundenen Seiten (bisher wurden der ``links``-Parameter nicht benutzt.

Btw: Ich bin mir recht sicher, dass ``re`` die benutzten Patterns cached und darum nicht wieder kompiliert wird - vllt kann das jemand bestaetigen?
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Da liegst du richtig. Wenn du nur wenige Patterns verwendest, werden diese gecached - ich weiß nur leider nicht, wo da das Limit ist.
lunar

cofi hat geschrieben:Btw: Ich bin mir recht sicher, dass ``re`` die benutzten Patterns cached und darum nicht wieder kompiliert wird - vllt kann das jemand bestaetigen?
Die Dokumentation ... sich darauf zu verlassen, ist aber schlechter Stil. Einmalige Ausdrücke sollten als konstante Modul- oder Klassenattribute abgelegt werden.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

defnull: In Zeile 31 kann man die eckigen Klammern weglassen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ja, da ist was dran, allerdings, wollte ich die RE nah an der Verwendung halten, da er sonst nicht gebraucht wird.

Die schon gematchten Links werden jetzt gechached und so die Ausgangsmenge verkleinert. ``gather_links`` ist ebenfalls weggefallen, das hab ich in ``pair_links`` untergebracht, allerdings mit der IMHO unschoenen Unterscheidung zwischen Strings und anderen Iterables. Letzteres brauch ich fuer die Startpunkte.

http://paste.pocoo.org/show/131108/

Edit: An Performance ist leider nicht mehr viel rauszuholen, 2,2 - 2,4s
Lag wohl falsch mit meiner Idee oder die Dateimenge ist einfach zu klein ;)
Antworten