Mechanize und BeautifulSoup

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.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich versuche mich gerade damit, mich ein wenig mit Mechanize und BeautifulSoup vertraut zu machen. Was habe ich vor? Nun, ich habe auf meinem WebServer ein PHP-Script für die Registrierung. Mit diesen beiden eben genannten Bibliotheken versuche ich mich dort einzutragen. Jedoch versuche ich mit Absicht Fehler herbeizuführen, so dass ich von Seiten PHP Fehlermeldungen bekomme, zum Beispiel dass der Benutzername existiert, Passwort zu kurz sei etc. Kurz: Ich möchte mit BeautifulSoup den spezifischen Text, in welcher die Fehlermeldungen enthalten sind zu extrahieren.

Um Fehler herbeizuführen, fülle ich die raw_inputs wie folgt aus:
Name: dd
EMail: dd
Password: dd
Password Confirm: dd
ICQ: dd
Die oben genannten Eingaben sind also mit Absicht fehlerhaft, damit ich von PHP aus Fehlermeldungen bekomme, die dann wie folgt aussehen würden:
Ihr Benutzername muss mindestens 3 Zeichen enthalten!
Ihr Passwort muss mindestens 3 Zeichen enthalten!
Sie haben eine ungueltige E-Mail angegeben!
Sie muessen das Feld PrivateKey erst ausfuellen!
Sie muessen das Feld ProgramName erst ausfuellen!
Und genau diese Informationen möchte ich auslesen, und habe versucht dies wie folgt umzusetzen:

Code: Alles auswählen

from mechanize import Browser
from BeautifulSoup import BeautifulSoup

url ="http://xarphus.de/example.php"

br = Browser()
br.set_handle_robots(False)
br.open(url)

name = raw_input("Name: ")
email = 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"] = email
br.form["pass"] = password
br.form["pass2"] = password_confirm
br.form["icq"] = icq_id
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
Da Fehlermeldungen erst nach dem Abschicken des PHP-Formulars entstehen, so habe ich die submit()-Methode an die Variable response gebunden, damit ich mit Hilfe von BeautifulSoup den Inhalt auslesen kann. In der Print-Anweisung seht ihr eine Menge HTML-Tags in Anführungszeichen. Diese habe ich mir von Firebug ausgeben lassen, indem ich die entsprechende Stelle markiert und anschließend auf CSS-Path kopieren gegangen bin. Ich habe hier einfach Kopieren und Einfügen betrieben.

Führe ich den oben genannten Quelltext aus, erhalte ich folgende Fehlermeldung:
Traceback (most recent call last):
File "D:\Dan\Python\Übung\login_mechanize.py", line 25, in <module>
print soup.select('html body table tbody tr td div table tbody tr td table tbody tr td div p').text
TypeError: 'NoneType' object is not callable
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: welche Version von BeautifulSoup benutzt Du denn? Die aktuelle wird über bs4 importiert, die Version 3 kennt noch kein select. Den ganzen HTML-Baum anzugeben ist übrigens ziemlich umständlich und kann leicht falsche Ergebnisse liefern. Normalerweise werden Fehlermeldungen ja entsprechend formatiert und lassen sich so leicht über eine CSS-Klasse selektieren, z.B. als "p.error".
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Wie finde ich raus, welche Version ich benutze? Aber ich habe eben versucht über BS4 zu importieren, und das klappt auch. Also habe ich beide Versionen auf meinem Laptop. Und das mit dem HTML-Baum: Ich habe mich an diesem Archiv orientiert. Gleich am Anfang erklärt er, wie er bestimmte HTML-Elemente mittels Firebug extrahiert, und diese dann über die Print-Anweisung ausgibt.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: überleg doch mal selbst, ob es sinnvoll ist ein Element über 17 Ebenen zu referenzieren, das muß man ja immer aktuelle halten und prüfen, ob sich die Seite nicht inzwischen irgendwo geändert hat. Wie schon geschrieben, zeigt ja jede normale Webseite Fehler z.B. in rot an. Dazu werden Klassen benutz, anhand derer man diese Fehlermeldungen viel sicherer und einfacher finden kann. Bei google z.B. über "span.error-msg". Damit ist es egal, wo genau dies Fehlermeldung auf der Seite befindet.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Es ist ja schön, dass du deine Gedanken zum Ausdruck bringst, jedoch funktioniert ja mein Skript nicht einmal. Da wäre es - meiner Meinung nach - zunächst unwichtig über ungelegte Eier zu diskutieren, wenn man Skript nicht mal funktioniert. Da ist die Tatsache mit dem HTML-Baum erst einmal uninteressant. Dies könnte man im Anschluss diskutieren. Wieso immer gleich den dritten Schritt vor dem zweiten Schritt machen?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: das Problem mit der falschen BS-Version ist ja schon gelöst 8)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich glaube, es handelt sich hierbei um ein kleines Missverständnis bezüglich der Fehlermeldungen von Seiten der Webseite. Was ich meine, ist eher die Textausgabe. Angenommen zu registrierst dich irgendwo oder meldest dich irgendwo an. Nehmen wir man dieses Forum als Beispiel. Sobald deine Daten korrekt eingegeben wurden, bekommst du eine Textausgabe alâ "Sie haben sich erfolgreich angemeldet" oder "Sie haben sich erfolgreich registriert blah blah". Andersrum wäre auch möglich wie "Ihre Daten sind falsch" oder "Ihre Registrierung konnte nicht abgeschlossen werden, weil..." Und genau diese Textausgabe wünsche ich in meinem Script, wenn ich mich entweder anmelde oder registriere, damit ich nun weiß, ob ich mich nun richtig angemeldet bzw. mich erfolgreich registriert habe.

Und daher dieser Quelltext:

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.select('html body table tbody tr td div table tbody tr td table tbody tr td div p').text
Hier möchte ich also einen spezifischen Text ausgeben lassen, der nach der Registrierung erfolgt, entweder der Fehlerhinweis alâ "Kann nicht angemeldet werden, weil..." oder "Ihre Anmeldung war erfolgreich". Führe ich das Programm aus, bekomme ich folgende Fehlermeldung:

Fehlermeldung:
Traceback (most recent call last):
File "D:\Dan\Python\Übung\login_mechanize.py", line 26, in <module>
print soup.select('html body table tbody tr td div table tbody tr td table tbody tr td div p').text
AttributeError: 'list' object has no attribute 'text'
Wie ist es möglich die speziellen Textausgaben von Seiten der Webseite nun angezeigt zu bekommen? List kennt offenbar die Methode text nicht.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: was Listen sind, solltest Du solangsam gelernt haben, und dass Attribute von Elementen nicht auf dem List-Objekt verfügbar sind, hast Du ja jetzt gemerkt.
Und es handelt sich um kein Missverständnis. Hier beim Python-Forum kann man Fehlermeldungen über einen "span.error"-Selector finden, ohne den ganzen Baum kennen zu müssen. Aus Web-Designer-Sicht ist es einfach am sinnvollsten, eine bestimmte Klasse für Fehlermeldungen zu definieren, und als automatisierten Leser kann man das dann wunderbar nutzen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich habe mal in PastBin den Seitenquelltext hochgeladen.

Schau mal in Zeile 65. Dort sind also die Textausgaben, die ich also benötige, um diese in meine Print-Ausgabe anzeigen zu können. Dieses PHP-Skript stammt keineswegs von mir, sondern von jemand anders. Ich kann also nicht wie hier im Forum über span.error die Meldungen abfangen. Desmzufolge bleibt mir nichts anderes übrig, als den ganzen HTML-Baum zu benutzen, mhmh?

Zu der Frage bezüglich der Liste:

Im Beispiel-Quelltext, woran ich mich orientierte sieht es wie folgt aus:

Code: Alles auswählen

import requests
import bs4
 
def main():
    r = requests.get("http://developerexcuses.com/")
    soup = bs4.BeautifulSoup(r.text)
    print soup.select("html body div.wrapper center a")[0].text
 
if __name__ == "__main__":
    main()
Zugeben, hier wird die Bibliothek requests benutzt, und nicht mechanize wie bei mir. Wir sehen also, dass hier die text()-Methode klappt. Wesentlich anders ist es bei mir auch nicht? Oder übersehe ich was?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: ja, Du übersiehst 'was.
Aus welchem Jahrhundert stammt denn diese HTML-Seite? Von Advanced sehe ich da gar nichts. Tut mir echt leid, wenn Du soetwas benutzen mußt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Immer diese maßlose Übertreibung. Da ist etwas 2-3 Jahre (alt) bzw. liegt etwas 2-3 Jahre zurück und schon spricht man von Jahrhunderte :-) Grausame Wahrnehmung :-) Ich bin kein PHP-Programmierer, aber allgemein von Jahrhunderten zu sprechen ist schon ziemlich anmaßend :-) Egal, also liegt das "Problem" nicht bei mir, sondern bei der Webseite?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: nein, das Problem liegt bei Dir, ist ja auch ein Python-Fehlermeldung.
Und solchen HTML-Code schreibt man seit gefühlt 20, aber mindestens seit 12 Jahren nicht mehr. Und das liegt nicht an PHP. Die Seite hat ja auch gar keine Styles, geschweige denn irgendeine Form von Layout.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich habe mal die Print-Anweisungen miteinander verglichen.

Fremde Variante:

Code: Alles auswählen

print soup.select("html body div.wrapper center a")[0].text
Was ich hier noch nicht ganz verstehe, ist, was er mit dem Indext 0 hier meint. Die Zahl in den eckigen Klammern habe ich nicht ganz verstanden. Aus diesem Grund habe ich die Zahl bei mir auch weggelassen. Aber vielleicht kann mir ja jemand erklären, was es mit der Zahl 0 auf sicht hat?

Meine Variante:

Code: Alles auswählen

print soup.select('html body table tbody tr td div table tbody tr td table tbody tr td div p').text
Wie man sieht habe ich dies nahezu übernommen, nur den HTML-Baum habe ich angepasst. Ich kann immer noch meinen Fehler nicht finden. Und zwar in sofern, weshalb in der fremden Variante die text()-Methode greift, und bei mir nicht. Liegt es an der Zahl 0?

Würde ich die Zahl 0 ebenfalls übernehmen, dann bekäme ich die Fehlermeldung:
File "D:\Dan\Python\Übung\login_mechanize.py", line 27, in <module>
print soup.select('html body table tbody tr td div table tbody tr td table tbody tr td div p')[0].text
IndexError: list index out of range
Der Index 0 liegt also außerhalb des Wertes?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: wenn Du nicht weißt, was Listen sind, dann nimm Dir nochmal (erstmal?) ein Grundlagentutorial vor.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Dies würde mir aber nicht erklären, wieso bei mir out of range sein soll. Wie man eine leere Liste erstellt, wie man Elemente in eine Liste hinzufügt, löscht, editiert, diese Liste dann ausgeben kann, in der Liste nach einem bestimmten Element sucht etc habe ich schon durchgekaut. Und dennoch komme ich nicht dahinter. Huch? Überraschung? Oder soll ich jetzt nochmal lernen, wie man Listen erstellt, Elemente hinzufügt, löscht, editiert sucht etc? Meinst, es bringt was, wenn ich ein und die selbe Sache redundant durchkaue?
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: vielleicht lernst Du dann auch, wann man Listen benutzt, warum man hier Listen benutzt, und warum hier die Liste leer ist.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@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?

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
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.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: die Frage ist generisch gemeint: Warum liefert select eine Liste als Ergebnis zurück? Und warum postest Du schon wieder diesen fehlerhaften Code?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@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.
Sirius3
User
Beiträge: 18265
Registriert: Sonntag 21. Oktober 2012, 17:20

@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?
Antworten