HTML Analyse mit Python [FRAGE]

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
eLudium
User
Beiträge: 3
Registriert: Freitag 26. April 2013, 14:54

Hallo liebe Python Community,

ich möchte mich in Python einarbeiten um bestimmte Informationen aus HTML Dokumenten zu erhalten. Da ich noch ganz am Anfang stehe erhoffe ich mir allgemeine Vorgehensweisen in Bezug auf mein Problem.

Beispiel: Ich möchte schauen, ob ein Link ein Kommentar ist oder nicht. Nehmen wir dafür einfach den Blog http://seniorgamer.wordpress.com/2013/0 ... tysierung/ und dazu den ersten Kommentar von Anigunner, sein Name verlinkt auf http://pyromanischwarcynn.wordpress.com/. Ich möchte jetzt folgendermaßen vorgehen:

1. Öffne http://seniorgamer.wordpress.com/2013/0 ... tysierung/ (Quellcode)
2. Suche nach: a href='http://pyromanischwarcynn.wordpress.com'
3. Schaue in die übergeordnete Div-Class nach dem Wort "comment"
-> falls es nicht möglich ist die übergeordnete div-class zu ermitteln so würde ich auch mit "gehe x-zeilen nach oben und suche nach "wort" leben können
4. Wenn "comment" gefunden print "Kommentar", wenn nicht dann "Kein Kommentar"

So die vereinfachte herangehensweise. Da gibt es natürlich noch einige Ausnahmen die es zu beachten gilt aber es soll mir hier nur darum gehen einen Einstieg in die Thematik zu finden und wie ich ein solches Problem lösen kann.

Ich freue mich über jeden Hinweis!!
BlackJack

@eLudium: HTML verarbeitet man üblicheweise nicht als Text sondern parst die HTML-Elemente in einen Objektbaum. So etwas wie „gehe x Zeilen zurück”, gibt es also nicht, weil es keine Zeilen mehr gibt.

Zum parsen von HTML und navigieren im Objektbaum kann man beispielsweise `lxml.html` verwenden. Das bietet zum einen die API die auch das `xml.etree` aus der Standardbibliothek für XML bietet, aber auch XPath oder CSS-Selektoren.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Code: Alles auswählen

>>> import requests
>>> import lxml.html
>>> wordpress_html = requests.get('http://seniorgamer.wordpress.com/2013/04/25/simcitysierung/').content
>>> dom = lxml.html.document_fromstring(wordpress_html)
>>> comments = dom.cssselect('ol.commentlist p')
>>> comments[0].text_content()
'Ich denke EA wird uns noch eine Weile erhalten bleiben. Natürlich gibt es kein “too big to fail” in der Branche, aber ich denke der Abstieg wird langsam- und schmerzhaft sein. Was mich an der Sache am meisten erstaunt ist wie stur an der Firmenpolitik festgehalten wird obwohl mittlerweile jeder Depp sehen müsste das es so nicht auf Dauer weitergehen kann. Aber nein, lieber fährt man den Pott sehenden Auges auf den nächsten Eisberg anstatt das man einen Fehler eingestehen müsste und den Kurs ändert. Lieber schmeißt man den Kapitän von der Brücke und ersetzt ihn durch einen anderen der genau den gleichen Kurs beibehält.\nUnd zur Abwechslung schmeißt man noch ein paar Matrosen, Heizer und Stewards vom Pott um die Direktoren der Reederei davon zu überzeugen das man alles im Griff hat. Es sind immer die anderen….'
Hier ein einfaches Beispiel anhand der externen Module requests und lxml.
BlackJack

@webspider: Wobei `requests` hier nicht nötig ist. `lxml` kann das selbst und kann dann zusätzlich noch die HTTP-Header verarbeiten, zum Beispiel um die Zeichenkodierung daraus zu verarbeiten.

@eLudium: Noch ein Beispiel:

Code: Alles auswählen

from lxml import html


def main():
    doc = html.parse(
        'http://seniorgamer.wordpress.com/2013/04/25/simcitysierung/'
    )
    url = 'http://pyromanischwarcynn.wordpress.com'
    for anchor_node in doc.xpath('//a[@href=$url]', url=url):
        if anchor_node.xpath('./ancestor::div[contains(@class, "comment")]'):
            print 'Kommentar.'
        else:
            print 'Kein Kommentar'


if __name__ == '__main__':
    main()
eLudium
User
Beiträge: 3
Registriert: Freitag 26. April 2013, 14:54

@BlackJack

Vielen Dank das funktioniert super! Ich verstehe es auch so langsam und kann es zumindest umbauen, um z.B. auch nach Sidebar etc.. zu suchen. Wenn ich das alles kombinieren will werde ich wohl ELIF nehmen, korrekt? Ist es möglich direkt mehrere Wörter quasi in einem Array anzulegen? Also das man nicht nur nach "comment" sucht sondern sagen kann er soll schauen, ob folgende Wörter ("comment","usercm","kommentar",...) vorkommen, wenn ja dann wird "Kommentar" ausgegeben und wenn nicht wird eine neue Abfrage mit den Wörtern ("sidebar","sb","widget",...) gestartet, wenn vorhanden dann wird "Sidebar" ausgegeben ansonsten "Nicht identifiziert"

So oder so, vielen Dank für die Hilfe. Ich erahne das System und kann mich von nun an auch selber einarbeiten. Dauert nur manchmal etwas länger ;)

Bisher habe ich:

Code: Alles auswählen

from lxml import html


def main():
    doc = html.parse(
        'http://seniorgamer.wordpress.com/2013/04/25/simcitysierung/'
    )
    url = 'http://www.polyneux.de/'
    for anchor_node in doc.xpath('//a[@href=$url]', url=url):
        if anchor_node.xpath('./ancestor::div[contains(@class, "sidebar")]'):
            print 'Sidebar'
        elif anchor_node.xpath('./ancestor::div[contains(@class, "widget")]'):
            print 'Sidebar'
        elif anchor_node.xpath('./ancestor::div[contains(@class, "comment")]'):
            print 'Kommentar'        
        else:
            print 'Link nicht identifiziert'
            

if __name__ == '__main__':
    main()
Das funktioniert auch gut, möchte aber nicht für jedes Wort eine neue Abfrage machen, daher die Arraylösung.
Zuletzt geändert von Anonymous am Montag 29. April 2013, 13:06, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@eLudium: Falls ich richtig verstehe was Du vorhast, dann wird das etwas komplizierter. Denn Du willst ja anscheinend raten ob ein Link als Kommentar oder in einer Sidebar auftaucht, wobei Du eigentlich keinen festen Anhaltspunkt hast, welche Klassenwerte jeweils verwendet werden. Zumal es noch Fälle geben kann wie Blogs, die die letzten x Kommentare in der Sidebar anzuzeigen, oder zumindest einen Anriss und/oder einen Link zum Kommentar.

Neben ``div`` könnte ``id`` hier auch noch interessant sein.

Das was Du Array nennst, heist in Python übrigens Liste. Arrays gibt es auch, darunter versteht man in der Regel aber den `numpy.array`-Datentyp oder den eingebauten `bytearray`-Typ in aktuelleren Python-Versionen, oder in seltenen Fällen auch den Datentyp aus dem `array`-Modul in der Standardbibliothek.

Ich würde wahrscheinlich von den Ankerknoten aus nicht mehr mit XPath arbeiten, sondern in Python-Code die <div>s Richtung Wurzelnoten (`iterancestors()`-Methode auf dem Ankerknoten) einzeln testen und dann beim ersten Aufhören, auf den Sidebar oder Kommentar zutrifft. Das ganze ist komplex genug um es in Funktionen aufzuteilen, damit es übersichtlich bleibt.

Die Bedingungen könnte man als Wörterbuch abbilden, welches die Linkart, auf die Suchwörter für die Attribute der <div>-Tags abbildet.
eLudium
User
Beiträge: 3
Registriert: Freitag 26. April 2013, 14:54

Ich würde wahrscheinlich von den Ankerknoten aus nicht mehr mit XPath arbeiten, sondern in Python-Code die <div>s Richtung Wurzelnoten (`iterancestors()`-Methode auf dem Ankerknoten) einzeln testen und dann beim ersten Aufhören, auf den Sidebar oder Kommentar zutrifft. Das ganze ist komplex genug um es in Funktionen aufzuteilen, damit es übersichtlich bleibt.

Die Bedingungen könnte man als Wörterbuch abbilden, welches die Linkart, auf die Suchwörter für die Attribute der <div>-Tags abbildet.
Wenn ich das recht verstehe dann sehe ich das genauso, ich habe mir einige Blogs etc.. angeschaut, es bleibt nichts anderes übrig als div class und id und einige weiterer elemente (span class="") zu nutzen. Ich werde mich weiter einlesen und ggf. zu einem späteren Zeitpunkt den Stand des Scripts erneut posten.

Vielen Dank für die Denkanstöße und Hilfestellungen :)
Antworten