BeautifulSoup: Nach einem Tag noch einen Text finden?

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
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Hallo Leute, ich habe mal wieder ein Problem:

Ich benutze BS um eine HTML-Seite zu scrapen (oder parsen? Wie auch immer das genannt wird?).

Jetzt habe ich ein div-Container in welchem ein <p>-Tag ist und in welchem wiederrum 2x hintereinander ein <a href..>-Tag ist.
Danach findet sich noch ein 'loser' Text den ich extrahieren möchte.

Also ein Beispiel:

Code: Alles auswählen

<div class="irgendwas1">
    <p class="irgendwas2">
        <a href="url1">DieURL</a>
        <a href="url1">DieURL</a>
        EinText
    </p>
</div>
Ich möchte nun den Text 'EinText' extrahieren.

Ich benutze davor bereits die "FindAll('a')[0]" und "FindAll('a')[1]"-Methode und bekomme auch das korrekte Ergebnis (eben jeweils die Werte der <a>-Tags).
Jetzt will ich noch den folgenden Text haben und habe gegooglet und finde eine Methode 'nextSilbing'. Wenn es die richtige Methode ist, dann setze ich sie falsch ein.
Nämlich so:

Code: Alles auswählen

wert = soup.find("div", {"class": "irgendwas1"}).find("p", {"class": "irgendwas2"}).findAll("a")[1].nextSibling
Ich bekomme zurückgeliefert:
Traceback (most recent call last):
File "./start.py", line 38, in <module>
print(wert[0])
File "/usr/lib/python2.7/dist-packages/bs4/element.py", line 892, in __getitem__
return self.attrs[key]
KeyError: 0
Natürlich mache ich was falsch. Aber 1.) ist es die richtige Methode die ich da gefunden habe?
und 2.) Wenn es so wäre, was mache ich falsch oder wenn es nicht so wäre, wie dann?

Schon mal ein große Dankeschön für eure Mühe!
BlackJack

@kaineanung: Was denkst Du denn was an `wert` gebunden ist und was ``wert[0]`` bewirken soll? Vergleiche das was Du glaubst mit dem was wirklich *ist*. (Wert von `wert` ausgeben, Typ von `wert` ausgeben.)

Und bei `bs4` besser die neuen Namen verwenden statt der alten, also `find_all()` anstelle von `findAll()` und `next_sibling` anstelle von `nextSibling`. Die alten werden irgendwann verschwinden und spätestens dann müsste man alles noch mal auf die neuen Namen umschreiben. Ausserdem kann man einiges mit CSS-Selektoren und `select()` kürzer ausdrücken.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Bin zwar nun bei der Arbeit und kann es nicht testen und mal ein print type machen, aber der Wert, welcher das Zurückgegebene Content von BS beinhaltet ist eine Liste.
Das erste Element der Liste beinhaltet das Ergebnis auf welches ich mit dem Index 0 zugreife.

In diesem Fall enthält

Code: Alles auswählen

wert = soup.find("div", {"class": "irgendwas1"}).find("p", {"class": "irgendwas2"}).findAll("a")[1]
Also das zweite <a>-Tag.
(was auch so wunderbar funktioniert hat)

Das mit den neuen Namen höre ich gerade zum ersten mal. Gut das du das sagst und ich werde es heute Abend abändern. Ist ja nichts weiter dabei geschwind die Namen zu ändern weil sich die erwarteten Parameter ja nicht geändert haben (hoffe ich jetzt mal).

Und das mit dem 'select' verstehe ich jetzt noch nicht und hoffe das ich es bald verstehen werden kann.

So, was nun meinen Fehler betrifft bin ich jetzt ja trotzdem noch ratlos.
Ist 'nextSibling' die falsche Methode? Wie extrahiere ich den Text im <p>-Element nach den zwei <a>-Tags?

Meine Theorie: ich suche nach dem <p>-Tag, liefere mir den Content zurück und extrahiere es mit Stringfunktionen? Das kann BS aber sicherlich besser und es ist eine Vorgehensweise vorhergesehen, richtig?
BlackJack

@kaineanung: Im letzten Beitrag hast Du `wert` an etwas anderes gebunden als in dem Beitrag wo Du die Ausnahme gezeigt hast. Der Fehler ist ja auch gar nicht in der Zeile, sondern beim Versuch auf ``wert[0]`` zuzugreifen. In keinem der beiden Fälle, also nicht in dem letzten Beitrag und auch nicht in dem wo die Ausnahme steht, ist `wert` an eine Liste gebunden.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Kann es sein daß ich im ersten Fall, also im 'FinAll('a').contens' eine Liste erhalte und darauf mit einem Index zugreifen muss um die einzeltnen ListElmente abzugreifen und beim 'nextSilbing' es eben keine Liste ist sondern eine Variable? Oder was willst du mir da dann genau sagen?
Ich bekomme den Fehler beim 'print(wert[0])'. Das heisst daß ich den Fehler bekomme beim Zugriff auf 'wert'.
Da in der Fehlermeldung was von 'key' steht, vermute ich nun mal das es keine Liste ist und ich mit keinem Index darauf zugreifen kann.
Mich würde es aber jetzt wundern daß ich gestern Abend das nicht mehr ohne Index versucht habe auszugeben...
Meinst du wenn ich dann lediglich

Code: Alles auswählen

print(wert)
verwende, ich den korrekten Inhalt bekommen würde?

Das werde ich später natürlich ausprobieren.

Folglich benutze ich aber die richtige Methode da niemand was bezüglich des 'nextSibling' was gesagt hat, richtig?
BlackJack

@kaineanung: Naja, `next_sibling` statt `nextSibling` hatte ich schon gesagt. Du musst Dir halt klar machen was die Methoden und Properties liefern. `contents` — *mehrzahl* wird mehr als ein Element liefern. Und `next_sibling` liefert *das* nächste Geschwisterelement. Da kann es ja nur eines geben, warum sollte das eine Element in einer Liste stecken wenn es immer nur eins wäre.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Klar, habe im Eifer nun schnell 'nextSibling' statt 'next_Sibling' geschrieben. Die letzte Schreibweise gefällt mir sowieso besser da ich es bisher in meiner 'angestammten' Programmiersprache auch so oder so ähnlich benutzt habe.
Das wir heute Abend jedenfalls umbenannt in meinem Quellcode.

Das Wort 'Geschwisterelement' irritiert micht nun ein wenig.
Ist in meinem o.g. Beispiel der 'MeinText'-Text ein Geschwisterelement zu den <a>-Tags?
Oder sind, wie ich jetzt vermute, alle Entitäten in einem aktuell ausgewähltem Element 'Geschwisterelemente'?

In meinem Fall also:
1. Geschwisterelement: <a href=...></a>
2. Geschwisterelement: <a href=...></a>
3. Geschwisterelement: MeinText
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Also ich habe es jetzt ohne 'next_sibling' gelöst.
Ich weis nicht ob es eine 'professionellere' Lösung dafür gibt, aber ich habe mir
mit dem findAll nicht die contents zurückgegeben sondern das ganze 'tag' und in diesem suche ich mir per string-Funktionen das entsprechende heraus

Code: Alles auswählen

wert[wert.find('startzeichenfolge')+len('startzeichenfolge'):wert.find('endzeichenfolge')]
Ist das so eine vorgehensweise die professionel genug ist oder zumindest akzeptabel?
Wenn nicht, dann wie würde es anders gehen?

Jedenfalls: Vielen Dank für alles bisherige (und zukünftige was sicherlich noch so einiges vorkommen wird).
BlackJack

@kaineanung: Das ist alles andere als professionell, mit HTML-Parsern will man ja gerade die nicht robusten Zeichenkettenoperationen umgehen. Ich verstehe auch nicht so ganz warum Du das dann so umständlich machen willst‽
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Weil ich python, aber allen voran BS garde so durch den 'Nebel erkennen' kann.
Sprich: mir ist es noch nicht so geläufig und ich weiß noch nicht wann welche Funktion/Methode mich weiterbringt!

Dann behelfe ich mich eben mit dem was ich schon kenne bzw. mit was ich schon besser umgehen kann. Also Substrings statt den Dschungel an Methoden in BS zu kennen....

Ich weiß, man kann sich die Doku von BS durchlesen. Aber wenn ich nichtmal genau weiß wonach ich suchen muss... z.B. ich wäre nie auf die Idee gekommen nach 'next_sibling' zu suchen wenn ich es nicht zufällig und mit vielen umschreibungen in der Google-Suche gefunden hätte.
Darum frage ich ja auch hier nach wie ich in meinem o.g. Beispiel an den Text 'Mein Text' herankomme?

Ich suche nach dem Parent-Container (also der Container der die betreffenden Tags beinhaltet).
Da sind zweimal <a>-Tags hintereinander und dann ein 'loser' Text, also ohne ein Tag nach dem ich in BS hätte weiter (also noch tiefer) suchen können.
Ich weis jetzt nicht wie ich zu diesem 'Mein Text' kommen kann? Substr + find sind mir dann halt als erstes in den Sinn gekommen....

Aber wenn es anders geht: ich bin offen für die 'Profi-Lösungen' und immer her damit und zar sehr gerne..
BlackJack

@kaineanung: Du hattest doch die Lösung schon!
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Meinst du das mit Lösung:
In meinem Fall also:
1. Geschwisterelement: <a href=...></a>
2. Geschwisterelement: <a href=...></a>
3. Geschwisterelement: MeinText
?

Das war eher als Frage gedacht...
habe ich mit next_sibling durch die 'Elemente' navigieren wollen, aber nach dem zweiten 'next_sibling' kam nur noch 'None'.
Und ich wollte aber den Text haben und daher habe ich mich eben anders, leider nicht professionel, beholfen.

Also nochmal:
Wenn ich mit

Code: Alles auswählen

wert = soup.find("div", {"class": "irgendwas1"}).find("p", {"class": "irgendwas2"})
den betreffenden Container gefunden habe, dann kann ich mit 'next_Sibling' zu dem ersten <a>-Tag navigieren. Dann noch ein next_Sibling und dann habe ich den zweiten <a>-Tag. Soweit so gut.
Wenn ich aber jetzt noch ein 'next_Sibling' anwende, dann beinhaltet meine variable (in diesem Fall wird ist es eine Variable und keine Liste -> da hast du mir mit deinem ersten Post geholfen) ein 'None' oder ist leer (ich weis es nicht mehr, ist schon ein paar Stunden her seit ich es 'umgebaut' (also verschmlimmbessert habe)).

Dann habe ich hier in den Thread reingeschaut und keine Lösung gefunden. Dann habe ich mir den Containerinhalt (also da wo BS hinnavigiert ist durch meine 'finds') und habe den ganzen Div-Container gesehen. Dann habe ich gedacht da gibt es keine Möglichkeit an 'Mein Text' ranzukommen (weil er in keinem 'HTML-Tag', welches als Geschwisterelement neben den <a>-Tags geführt ist) und dachte ich kann es nur durch string-funktionen extrahieren. Das dachte ich zumindest.

Also, wie komme ich dann mit next_Sibling an 'Mein Text' im o.g. Beispiel?
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Ok, ich hab es gefunden.
1) next_sibling statt next_Sibling (kann aber sein das beides geht=
2) next_sibling bringt den ganzen Tag. Wenn ich Contents aufrufe dann eine Liste alles gefundenen Contents. Und davon dan den entsprechenden Index der Liste und es klappt...

BlackJack, VIELEN DANK!
Antworten