BeautifulSoup: Große HTML Zweige parsen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
microkernel
User
Beiträge: 271
Registriert: Mittwoch 10. Juni 2009, 17:27
Wohnort: Frankfurt
Kontaktdaten:

Hallo,

da ich mich zur Zeit in Sachen XML-Parsing etwas weiterbilden möchte (ich hab das Thema früher immer gemieden), versuche ich zur Zeit eine HTML-Kopie meiner Facebook-Nachrichten (es ist möglich, eine Kopie deiner Facebook-Daten als Archiv herunterzuladen) zu parsen. Alle Nachrichten, die ich je auf Facebook verschickt habe sind in dieser Datei enthalten - dementsprechend ist die Datei etwa 10MB groß.
Der Aufbau einer Unterhaltung sieht dort in etwa so aus:

Code: Alles auswählen

<div class="thread"><div class="border">
<div class="header">

<span class="profile fn">Chatpartner1</span>, <span class="profile fn">Chatpartner 2</span> <!-- ggf. noch mehr !-->

<abbr class="time published" title="2012-12-15T23:31:17+0000">15. Dezember 2012 um 16:31</abbr> <!-- Datum des letzten Kontakts !-->

</div>

<!-- Selbsterklärend !-->
<div class="message">
<div class="from"><span class="profile fn">Name Absender</span></div>
<abbr class="time published" title="2010-09-18T09:17:53+0000">18. September 2010 um 3:17</abbr>
<div class="msgbody">
NACHRICHT
</div>
</div>

<div class="message">
<div class="from"><span class="profile fn">Name Absender</span></div>
<abbr class="time published" title="2010-09-18T09:17:53+0000">18. September 2010 um 3:17</abbr>
<div class="msgbody">
NACHRICHT
</div>
</div>

<!-- weitere Nachrichten !-->

</div></div>
So wie ich das sehe, müsste sich das doch eigentlich ganz nett parsen lassen, oder? Mein derzeitiger Ansatz sind folgende Funktionen (,welche allerdings erstaunlich langsam sind):

Code: Alles auswählen

def process_threads(html_source):
    data = []
    print "[*] Initializing soup..."
    soup = BeautifulSoup(html_source) # dauert sehr lange!
    print "[*] Intialized soup!" 
    threads = soup.find_all("div", {"class" : "border"})
    print "[*] Processing threads..."
    for thread in threads:
        data.append(process_thread(thread))
    print "[*] Threads created"
    return data

def process_thread(thread):
    data = {"messages" : []}
    # Get partners name
    header = thread.find("div", {"class" : "header"})
    partners = header.find_all("span", {"class" :
                                           "profile fn"})
    data["partners"] = frozenset(map(lambda obj: obj.getText(), partners))
    # Get last contact
    last_contact = header.find("abbr", {"class" :
                                        "time published"})
    data["last contact"] = datetime.datetime.strptime(last_contact.get("title"),
                                             "%Y-%m-%dT%H:%M:%S+0000")
    # Extract messages
    messages = thread.find_all("div", {"class" : "message"})
    for message in messages:
        msg = {}
        msg["from"] = message.find("span", {"class" : "profile fn"}).getText()
        msg["time"] = datetime.datetime.strptime(message.find("abbr").get("title"),
                                        "%Y-%m-%dT%H:%M:%S+0000")
        msg["text"] = message.find("div", {"class" : "msgbody"}).getText()
        data["messages"].append(msg)

    return data
Was mich allerdings an der ganzen Sache stört, ist dass es furchtbar lange dauert die Html-Datei zu parsen. Ich bin jetzt wirklich keiner, der ständig nur am optimieren ist, aber ich denke, dass man das bestimmt schneller parsen kann. Zur Zeit benötigt die Funktion process_threads(htmlsource) bei einer 13MB großen Datei etwa 75 Sekunden. Ich jemand Optimierungsvorschläge? Vielleicht gehe ich beim parsen auch ganz "falsch" vor. Vielleicht sollte ich nicht die ganze zeit .find(...) benutzen.
lunar

@microkernel BeautifulSoup per se ist langsam. Nutze lxml.html.
Benutzeravatar
microkernel
User
Beiträge: 271
Registriert: Mittwoch 10. Juni 2009, 17:27
Wohnort: Frankfurt
Kontaktdaten:

Danke für den Tipp! ;) Könntest du mir ein Hinweis geben, wie man so etwas mit lxml parst? Wie gesagt kenn ich mich damit so gut wie gar nicht aus... Ich verlang jetzt kein fertigen code - nur ein Weg wie ich das machen könnte.
lunar

@microkernel Lies die Dokumentation. Sie enthält eine Referenz, Beispiele und alles, was das Herz so begehrt :)
Antworten