Seite 1 von 1
Need for Speed: HTML-Links filtern
Verfasst: Montag 27. Juli 2009, 13:56
von cofi
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

Verfasst: Montag 27. Juli 2009, 18:43
von Defnull
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
Verfasst: Montag 27. Juli 2009, 19:16
von cofi
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?
Verfasst: Montag 27. Juli 2009, 19:33
von nemomuk
Da liegst du richtig. Wenn du nur wenige Patterns verwendest, werden diese gecached - ich weiß nur leider nicht, wo da das Limit ist.
Verfasst: Montag 27. Juli 2009, 19:37
von 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.
Verfasst: Montag 27. Juli 2009, 20:04
von derdon
defnull: In Zeile 31 kann man die eckigen Klammern weglassen.
Verfasst: Montag 27. Juli 2009, 20:06
von cofi
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
