Mechanize und BeautifulSoup
@Sirius3:Listen benutzt man um Daten zu strukturieren. Ich erspare mir jetzt eine weite Ausführung. In meinem Beispiel sehe ich allerdings nur eine Liste, und keine Mehrzahl (Listen). Und warum hier die Liste leer ist?
Nun, ich sehe hier die Variable response, in der der ganze HTML-Code nach dem Absenden des Formulars gespeichert ist - darunter auch die Textausgaben. Durch BeautfifulSoup wird der Inhalt von response gelesen, und hier in die Variable soup gespeichert. Anschließend folgt eine Print-Anweisung. Hier wird die select()-Methode verwendet, damit Elemente aus der BeautifulSoup-Instanz ausgewählt werden können.
Code: Alles auswählen
response = br.submit()
soup = BeautifulSoup(response.read())
print soup.select('html body table tbody tr td div table tbody tr td table tbody tr td div p').text
@Sirius3: Damit ich mich an den Quelltext entlang angeln kann, wenn ich etwas schreibe, damit man mich nachvollziehen kann, wo ich einen gedanklichen Knoten habe? Warum select die Liste als Ergebnis zurückliefert? Weil bein der text()-Methode nicht mit angegeben wurde, welcher Index zurück gegeben werden soll. In der fremden Version steht ja der Index 0. Python beginnt mit der Zählung auch mit 0.
@Sophus: sowohl der Index als auch die text-Methode arbeiten auf dem Ergebnis, bzw. einem Element des Ergebnisses der select-Methode. Also hat das mit meiner Frage gar nichts zu tun, weil es im Programmablauf erst später kommt. Daher nochmal: Warum liefert select eine Liste und warum ist die Liste bei Dir leer?
@Sophus: sehr gut
Es gibt verschiedene Arten, wie man den HTML-Baum aufbaut. Das wurde erst mit HTML5 in feste Regeln gegossen, die BS wahrscheinlich noch nicht so umsetzt. Daher ist Dein 17 Elemente langer Pfad aus FireFox nicht das, was BS sieht.
Es gibt verschiedene Arten, wie man den HTML-Baum aufbaut. Das wurde erst mit HTML5 in feste Regeln gegossen, die BS wahrscheinlich noch nicht so umsetzt. Daher ist Dein 17 Elemente langer Pfad aus FireFox nicht das, was BS sieht.
Zuletzt geändert von Sirius3 am Samstag 2. Mai 2015, 23:02, insgesamt 1-mal geändert.
@Sirius3: Ich habe jedoch diesen Abschnitt, also diesen HTML-Baum von Firebug entnommen. Gehe ich beim Firebug über diese Stelle wird auf die Text-Ausgab mit markiert. Es heißt also, es muss da irgendwas sein? Oder lügt Firebug?
@Sirius3:
Ich habe das Ganze etwas anders gelöst. Womöglich alles falsch, jedoch hat es den Anschein dass es klappt.
In Zeile 1 habe ich bei der Variable response die read()-Methode entfernt. In Zeile 3 habe ich mit zunächst eine einfache For-Schleife gebastelt, mit der find_all()-Methode. Und innerhalb dieser Methode habe ich eine anonyme Funktion lambda gepackt, die sozusagen eine implizite Schleife ausführt. Hier wird also gesagt, wenn das Argument Ihr Benutzername muss mindestens 3 Zeichen enthalten! in der find_all()-Methode vorhanden ist, dann soll die Print-Anweisung angesprochen werden.
Der Nachteil wäre: Es gibt ja mehrere Meldungen, wie "E-Mail-Adresse ungültig", "Passwort zu kurz", "Benutzername existiert bereits" und eine ganze Menge mehr. Das heißt dann, ich müsste dann mehrere solcher lambdas innerhalb dieser find_all()-Methode schreiben? Würde sich das nicht aufblähen?
Ich habe das Ganze etwas anders gelöst. Womöglich alles falsch, jedoch hat es den Anschein dass es klappt.
Code: Alles auswählen
soup = BeautifulSoup(response)
for item in soup.find_all(text=lambda x: 'Ihr Benutzername muss mindestens 3 Zeichen enthalten!' in x):
print "Lambda ", str(item)
Der Nachteil wäre: Es gibt ja mehrere Meldungen, wie "E-Mail-Adresse ungültig", "Passwort zu kurz", "Benutzername existiert bereits" und eine ganze Menge mehr. Das heißt dann, ich müsste dann mehrere solcher lambdas innerhalb dieser find_all()-Methode schreiben? Würde sich das nicht aufblähen?
@Sophus: jetzt hast Du Dir selbst bewiesen, dass es nicht sinnvoll sein kann, so einen komplizierten Selector zu benutzen und verwendest statt dessen ein noch viel spezielleres, das exakt den Text der Fehlermeldung kennen muß? Wo da eine implizite Schleife im lambda ist, wirst auch nur Du sehen können.
Weil Dein HTML-Salat absolute keine Struktur hat, ist es auch nicht einfach möglich, irgendetwas daraus zu lesen. Wenn Du also nach einem Alleinstellungsmerkmal suchst, dann merkst Du, dass die Fehlermeldungen vor dem <form>-Tag mit Namen "register" stehen.
Damit kannst Du sicher die Fehlermeldungen finden:
Weil Dein HTML-Salat absolute keine Struktur hat, ist es auch nicht einfach möglich, irgendetwas daraus zu lesen. Wenn Du also nach einem Alleinstellungsmerkmal suchst, dann merkst Du, dass die Fehlermeldungen vor dem <form>-Tag mit Namen "register" stehen.
Damit kannst Du sicher die Fehlermeldungen finden:
Code: Alles auswählen
print soup.find("form", {"name":"register"}).fetchParents('p')[0].text
@Sirius3: Ich habe eben deine Variante Probiert:
Du hast Recht, meine Variante kennt den ganzen Text der Fehlermeldung. Wenn man erweitern will, kann man sagen: "wenn dieser oder jener Text auf dieser Webseite nach dem submit()" zu finden ist, dann mache dies und jenes". Es wäre ja nur eine Alternative?Traceback (most recent call last):
File "D:\Dan\Python\Übung\login_mechanize.py", line 31, in <module>
print soup.find("form", {"name":"register"}).fetchParents('p')[0].text
IndexError: list index out of range
@Sophus: dann hast Du wieder eine andere Seite als ich zum Testen. Denn wenn es die Form gibt, gibt es auch ein übergeordnetes p-Tag. Das war aber auch nur ein Beispiel, wie Du bestimmte Elemente in einem HTML-Dokument finden kannst, Du mußt es halt auf Deinen Fall entsprechend anpassen. Ich sag Dir ja nur die ganz Zeit, welche Probleme Du mit Deinen Ansätzen haben wirst, und prompt tauchen die auch auf. Es gibt nicht die perfekte Lösung, sondern nur mehr oder weniger robuste. Irgend ein p-Tag enthält halt in 99% der Fälle keine Fehlermeldung, "p.error" dagegen wird wohl in 99.9% der Fälle eine Fehlermeldung enthalten.
Die Seite hat mehrere Forms und damit mehrere submit. Die richtige Form erkennt man am Namen. Solche Gedanken solltest eigentlich DU von selbst machen. Alles was zu kompliziert ist, wird bei kleinen Änderungen nicht mehr funktionieren, alles was zu einfach ist, wird Dir möglicherweise falsche Ergebnisse liefern, wenn etwas zu einfach und gleichzeitig zu kompliziert ist, wie Deine Ideen bisher, ...
Die Seite hat mehrere Forms und damit mehrere submit. Die richtige Form erkennt man am Namen. Solche Gedanken solltest eigentlich DU von selbst machen. Alles was zu kompliziert ist, wird bei kleinen Änderungen nicht mehr funktionieren, alles was zu einfach ist, wird Dir möglicherweise falsche Ergebnisse liefern, wenn etwas zu einfach und gleichzeitig zu kompliziert ist, wie Deine Ideen bisher, ...
@Sophus: einen HTML-Baum über 17 Ebenen anzugeben halte ich für kompliziert. Trotzdem ist es zu einfach gedacht, weil auf diese Weise auch viele <p>-Elemente gefunden werden können, die keine Fehlermeldungen enthalten.
Hier das Ergebnis, wenn ich meine Suche durchführe:
Hier das Ergebnis, wenn ich meine Suche durchführe:
Code: Alles auswählen
>>> soup.find("form", {"name":"register"}).fetchParents('p')[0].text
u'Sie muessen einen Benutzernamen angeben!Sie muessen ein Passwort angeben!Sie muessen eine E-Mail Adresse angeben!Sie muessen das Feld PrivateKey erst ausfuellen!Sie muessen das Feld ProgramName erst ausfuellen! \n\nName :\n\n\n\nEmail :\xa0\n \n\n\nPasswort :\xa0\n \n\n\nPasswort (Wdh.) :\xa0\n\n\n\nICQ :\xa0\n\n\n\n\n\n\n\n\n\xa0\xa0\xa0 \n\xa0\xa0\xa0\n\n\n\n\n'
@Sirius3: Hier nochmal mein Quelltext mit deiner Print-Anweisung.
Fehlermeldung:
Code: Alles auswählen
from mechanize import Browser
from bs4 import BeautifulSoup
url ="http://xarphus.de/example.php"
br = Browser()
br.set_handle_robots(False)
br.open(url)
name = raw_input("Name: ")
e_mail = raw_input("EMail: ")
password = raw_input("Password: ")
password_confirm = raw_input("Password Confirm: ")
icq_id = raw_input("ICQ: ")
br.select_form(nr=1)
br.form["uname"] = name
br.form["email"] = e_mail
br.form["pass"] = password
br.form["pass2"] = password_confirm
br.form["icq"] = icq_id
response = br.submit()
soup = BeautifulSoup(response.read())
print soup.find("form", {"name":"register"}).fetchParents('p')[0].text
Du siehst, ich benutze ein und die selbe Seite.Traceback (most recent call last):
File "D:\Dan\Python\Übung\login_mechanize.py", line 26, in <module>
print soup.find("form", {"name":"register"}).fetchParents('p')[0].text
IndexError: list index out of range
@Sirius3: Nur so als Anmerkung nebenbei: BeautifulSoup 4 hält sich mittlerweile an PEP8-Namensgebung, hat aber die alten Methoden noch aus Kompatibilitätsgründen. Bei neuem Code würde ich deshalb statt `fetchParents()` eher `find_parents()` vewenden. Es wurden also auch Methoden nicht nur in der Schreibweise angepasst, sondern auch umbenannt damit die API einheitlicher ist. Und wenn man sowieso nur das erste Element braucht, ginge auch `find_parent()`.
@Sophus: Bei Dir wird aus irgendwelchen Gründen das <p>-Tag vor <form> geschlossen, also ist es ja logisch, dass Du statt dem Parent-Element das Vorgänger-Element möchtest:
Code: Alles auswählen
soup.find("form", {"name":"register"}).find_previous('p').text