Geschicktes Begrenzen des BeautifulSoup Parsers?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Niun
User
Beiträge: 7
Registriert: Mittwoch 15. November 2006, 20:29

Geschicktes Begrenzen des BeautifulSoup Parsers?

Beitragvon Niun » Mittwoch 15. November 2006, 22:15

Hallo,

Zusammenfassung:
Ich suche einen Weg, den SoupStrainer von BeautifulSoup mit verschiedenen Ausschlussbedingungen auszustatten, so dass bei dem ersten Parsen nur die Elemente, die ich auch brauche in den Parse-tree aufgenommen werden.

Problembeschreibung:
ich bin dabei, ein Programm zu schreiben, mit dem ich verschiedene Informationen aus Wikipedia-Artikeln sammeln kann.
Wenn ich nur den unformatierten Text des Artikels auf einer Wikipedia Seite haben will, reicht es, wenn ich mich mit dem div-Element mit der id bodyContent beschäftige, da dieser den Artikel beinhaltet.
Dazu beschränke ich den Parser mit dem SoupStrainer (Klasse von BeautifulSoup) auf diesen div:

Code: Alles auswählen

bodyContent = SoupStrainer('div',id="bodyContent")
soup = BeautifulSoup(wikiseite, parseOnlyThese=bodyContent)

Jetzt habe ich alle Unterelemente des div-Elements im parse-tree. Elemente, die nur aus Whitspaces oder Kommentaren bestehen interessieren mich aber nicht. Also entferne ich diese aus dem parse-tree mit ihren extract Methoden. Dazu muss ich aber zwei Suchen mit verschiedenen Kriterien durchführen, da das "<!--...-->" nicht durch die RegExp gematcht wird:

Code: Alles auswählen

# Kommentarelemente suchen:
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
# Whitespaceelemente suchen:
lonelyWhitespaces = soup.findAll(text=re.compile(r"^\s+$"))
# Kommentare entfernen:
for comment in comments: comment.extract()
# Whitespaces entfernen:
for ws in lonelyWhitespaces: ws.extract()

Was mich daran stört ist, dass ich erstmal Zeug in den Baum übernehme, was ich nicht brauche, und dann den Baum zweimal mit unterschiedlichen Kriterien traversieren muss, um die überflüssigen Elemente zu finden. Ich glaube, dass das viel Zeit und Ressourcen kostet, wenn man das für viele Seiten macht.

Fragestellung:
Kennt jemand einen Weg, wie ich die Kriterien kombinieren kann?
Am schönsten wär es, einen SoupStrainer aus Kombination dieser Drei Bedingungen (nur Unterelemente von <div id="bodyContent">, die nicht Instanz von Comment sind und nicht nur aus Whitspaces bestehen) zu erstellen, damit schon beim Parsen, also beim ersten erstellen des parse-trees das Überflüssige weggelassen wird.

Gruß Niun
BlackJack

Re: Geschicktes Begrenzen des BeautifulSoup Parsers?

Beitragvon BlackJack » Mittwoch 15. November 2006, 23:40

Niun hat geschrieben:Was mich daran stört ist, dass ich erstmal Zeug in den Baum übernehme, was ich nicht brauche, und dann den Baum zweimal mit unterschiedlichen Kriterien traversieren muss, um die überflüssigen Elemente zu finden. Ich glaube, dass das viel Zeit und Ressourcen kostet, wenn man das für viele Seiten macht.


Du glaubst, oder Du weisst? Und wenn Du das für viele Seiten machst die Du direkt aus dem Web abrufst, dann hältst Du Dich hoffentlich an den Wunsch von Wikipedia das mindestens eine Sekunde Pause zwischen zwei automatisierten Seitenabrufen gemacht werden soll.

Kennt jemand einen Weg, wie ich die Kriterien kombinieren kann?


Die beiden die sich auf Text beziehen kannst Du ganz einfach kombinieren. Im Grunde weisst Du auch schon wie: Du kannst wie im Kommentarfall eine Funktion angeben, die den Textknoten auf Eigenschaften prüft. Das muss ja nicht nur eine Eigenschaft sein:

Code: Alles auswählen

def is_junk_text(text):
    return isinstance(text, Comment) or not text.strip()


Am schönsten wär es, einen SoupStrainer aus Kombination dieser Drei Bedingungen (nur Unterelemente von <div id="bodyContent">, die nicht Instanz von Comment sind und nicht nur aus Whitspaces bestehen) zu erstellen, damit schon beim Parsen, also beim ersten erstellen des parse-trees das Überflüssige weggelassen wird.


Das geht nicht. Wie in der Doku beschrieben, hast Du beim Parsen noch keine Knoten, wenn Du den hättest dann wären ja schon alle Kindelemente in Knoten umgewandelt worden und dann ist es zu spät um sie nicht umzuwandeln.
Niun
User
Beiträge: 7
Registriert: Mittwoch 15. November 2006, 20:29

Beitragvon Niun » Donnerstag 16. November 2006, 11:33

Erstmal vielen Dank BlackJack, ich glaub ich wär nicht so schnell auf die strip() methode gekommen.
Das mit der Sekunde zwischen den Wikipediaaufrufen ist ein guter Hinweis, ich glaube aber, dass mein Programm sowiso nicht so schnell zum Einsatz kommt. Es soll eher ein "proof of concept" werden, was ich meinem Prof vorstellen will. Der will dann auch sehen, dass ich mir über die Performance Gedanken gemacht habe.
Meine Wenigkeit hat geschrieben:Ich glaube, dass das viel Zeit und Ressourcen kostet, wenn man das für viele Seiten macht.

BlackJack hat geschrieben:Du glaubst, oder Du weisst?

Ich weiss, dass Einmal Parsen und danach das Unnötige entfernen mehr Ressourcen braucht als das Unnötige beim Parsen gleich wegzulassen, da ich ja für kurze Zeit mehr Daten im Baum speichern muss. Den Rest, speziell die Signifikanz in der Realität, glaube ich nur.

Gruß Niun
BlackJack

Beitragvon BlackJack » Donnerstag 16. November 2006, 12:43

Niun hat geschrieben:Es soll eher ein "proof of concept" werden, was ich meinem Prof vorstellen will. Der will dann auch sehen, dass ich mir über die Performance Gedanken gemacht habe.


Aber doch nicht auf so einer Ebene. Vorher macht man sich Gedanken in O-Notation. Ob man sich hinterher Gedanken um konstante Leistungsgewinne machen muss, sollte man möglichst nicht nach Vermutungen entscheiden sondern nach Testläufen und Laufzeitmessungen. Testläufe um zu sehen ob man überhaupt Zeit investieren muss oder möchte, und Laufzeitmessungen um zu sehen wo man am meisten gewinnen würde.

Meine Wenigkeit hat geschrieben:Ich glaube, dass das viel Zeit und Ressourcen kostet, wenn man das für viele Seiten macht.

BlackJack hat geschrieben:Du glaubst, oder Du weisst?

Ich weiss, dass Einmal Parsen und danach das Unnötige entfernen mehr Ressourcen braucht als das Unnötige beim Parsen gleich wegzulassen, da ich ja für kurze Zeit mehr Daten im Baum speichern muss. Den Rest, speziell die Signifikanz in der Realität, glaube ich nur.


Und trotzdem verwendest Du Zeit darauf? Insbesondere das nachträgliche entfernen kostet *zusätzlich* Zeit. Ich würde erst einmal das Programm so schreiben das es möglichst einfach das korrekte Ergebnis liefert und dabei darauf achten Datenstrukturen und Algorithmen zu verwenden die keine "üblen" O-Laufzeiten haben. Hier und da versuchen ein bisschen Speicherplatz oder Laufzeit einzusparen kann man später immer noch. Falls das überhaupt notwendig ist.

Wenn Du Dir um unnötige Ressourcen Gedanken machst, dann dürftest Du eigentlich kein Python verwenden. ;-)
Niun
User
Beiträge: 7
Registriert: Mittwoch 15. November 2006, 20:29

Beitragvon Niun » Donnerstag 16. November 2006, 15:50

Also erstmal: Ich mache mir Gedanken, über jeden kleinen Mist, um Python zu lernen (hab grad erst angefangen) damit ich micht halt mit möglichst vielen Details auseinandersetze.
Zur O-Notation hatte ich mir schon gedanken gemacht. Das einmalige Parsen sollte einen Aufwand von O(c)=c haben (c ist Anzahl der Zeichen im Dokument) und Das Entfernen sollte Aufwand O(n)=n sein (n ist Anzahl geparster Elemente). Das Parsen mit zweimaligem Entfernen hinterher wär dann ungefähr O(n,c)=c+2*n. Das ist also alles linear, also eher unkritisch. Aber ich dachte es wäre einfach unelegant, das Überflüssige erst in den Baum zu übernehmen, um es hinterher wieder zu entfernen. Naja egal, ich hatte einfach das Gefühl, etwas übersehen zu haben, damit das ganze eleganter wird. Die strip() Methode war ja schon mal nen guter Schritt.
Also kein weiteres Blabla mehr von mir zu diesem Thema...

Gruß Niun

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder