Datei im Internet öffnen mit Python 3

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
unbekannt100

Kann mir bitte einer erklären, wie ich in Python 3 eine Datei aus dem Internet öffnen kann. Ich habe es bis jetzt so probiert:

Code: Alles auswählen

import urllib.request
dat = urllib.request.urlopen("http://abc.de")
inhalt = dat.readlines()[0]
dat.close()
Wenn ich jetzt den Datentyp von inhalt ausgebe kommt <class 'bytes'>. Also hab ich versucht, es zu encoden:

Code: Alles auswählen

inhalt = str(inhalt, "utf-8")
(Das hab ich bei Google gefunden)

Hat aber nicht funktioniert. Also, weiß einer wie das geht?
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Was hat denn nicht funktioniert?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
unbekannt100

na ja, nach dem encoden habe ich einen leeren string bekommen
BlackJack

@unbekannt100: Die Richtung von Bytes nach Unicode heisst dekodieren. Und wenn das Ergebnis eine leere Zeichenkette ist, dann waren die Ausgangsdaten schon leer. Schau Dir doch einfach mal an was Du da zurück bekommst.
unbekannt100

Gibt es denn nicht eine einfache Methode, Dateien in Internet zu öffnen so dass man einen String bekommt?
So wie in Python 2.5?
BlackJack

@unbekannt100: In Python 2.x hat man keine Zeichenkette im Sinne von Python 3.x bekommen sondern eine Bytekette. Die bekommt man in Python 3.x genau so einfach: Lass einfach das dekodieren weg.

Und nun erzähl mal was Du *eigentlich* machen willst.
unbekannt100

Also, mein Projekt ist etwas größer.
Ich will im wesentlichen eine Google-Suche starten (dafür muss ich die Datei laden, die ich zurückbekomme wenn ich http://www.google.de/search?q=a+b+c aufrufe) und den Seitenquelltext nach einem Wikipedia-Eintrag durchsuchen. Bei dem Link den ich (eventuell) finde wierderholt sich das ganze (Da will ich dann bestimmte Einträge verarbeiten).
Benutzeravatar
sparrow
User
Beiträge: 4600
Registriert: Freitag 17. April 2009, 10:28

Wie hast du denn den Umstand gelöst, dass Google nicht möchte, dass du das tust?
Versteh das nicht falsch, aber wenn ich von deinen Problemen hier auf deinen Erfahrungsumfang schließe, bekommst als Antwort auf deine Anfrage an Google entweder ein 403 oder einen freundlichen Hinweis auf die ToS zurück.
BlackJack

@unbekannt100: Dann möchtest Du also eigentlich eine Bibliothek wie `lxml.html` verwenden, die das herunterladen und parsen für Dich übernimmt. Ich weiss jetzt nicht aus dem Kopf ob es die für Python 3 schon gibt, aber für praktisch einsetzbare Programme würde ich sowieso noch auf Python 2 setzen.
unbekannt100

@sparrow:
Noch gar nicht, das ist in der Tat auch ein Problem über das ich mir noch gar keine Gedanken gemacht habe. Aber vielleicht gibt es dafür auch eine Möglichkeit. Ich meine, wenn der Browser das "darf", warum dann nicht auch jedes andere Programm? Außerdem benötige ich etwas vergleichbares auch an anderen Stellen im Programm bzw. in anderen Projekten.

@BlackJack:
Ich finde es nicht sinnvoll, jetzt wieder zu Python 2 zurückzukehren. Große Teile des Projekts sind bereits extra für Python 3 umgeschrieben worden und eigentlich sollte alles, was in Python 2 ging auch in Python 3 irgendwie funktionieren. Warum sollten die Entwickler von Python denn daran interessiert sein, ihre Programmiersprache zu verschlechtern?
Benutzeravatar
sparrow
User
Beiträge: 4600
Registriert: Freitag 17. April 2009, 10:28

@unbekannt100: lxml funktioniert von CPython 2.4 - 3.3, das kannst du also einsetzen. Und damit solltest du entsprechend die Seite parsen, nachdem du sie auslesen konntest.
lunar

@unbekannt100: Es geht im Allgemein nicht alles, was in Python 2 ging, auch so ohne Weiteres in Python 3. Das liegt auch nur mittelbar an den Entwicklern von Python selbst, denn ein Großteil der Funktionalität, die man in Python zur Verfügung hat, wird von Drittbibliotheken gestellt. Und wenn eine wichtige Bibliothek wie Django Python 3 noch nicht unterstützt, kannst Du Dich meinetwegen auf den Kopf stellen, verwenden aber kannst Du die Bibliothek trotzdem nicht :)

Aus diesem Grund ist es nicht ratsam, einfach blind auf Python 3 zu setzen, ohne vorher zu evaluieren, ob Du auch wirklich alle nötigen Bibliotheken mit Python 3 verwenden kannst. Andernfalls musst Du mit Problemen rechnen.

Zum Thema Google: Es ist Sache des Anbieters – in diesem Fall Google – zu entscheiden, wer eine Seite wie benutzen darf. Ergo folgt aus der Tatsache, dass Du Google im Browser verwenden kannst, nicht, dass Du Google auch aus beliebigen Programmen heraus verwenden darfst und kannst.

Der Grund für eine derartige Einschränkung ist offensichtlich: Google möchte automatisierte Suchabfragen zur Verwendung seiner API zwingen, da diese Art von Suchabfragen die für natürliche Personen sichtbaren Werbeanzeige auf der Ergebnisseite umgeht. Die Verwendung dieser API lässt sich Google dann natürlich bezahlen, damit dem Konzern keine Einnahmen aus seiner Suchmaschine entgehen.
unbekannt100

Ich habe mir jetzt eine eigene Methode zum Download von Dateien im Internet geschrieben:

Code: Alles auswählen

def webseite_laden(url):

    import urllib.request, os

    filename = "webseite.tmp"
    try:
        dat = urllib.request.urlretrieve(url, filename)
        dat = open(filename)
        inhalt = dat.read()
        dat.close()
        os.remove(filename)
        return inhalt
    
    except:
        return False
Zum Thema Google: Die haben ja leider auch ihre Wetter-Api privat gemacht, das ist auch total blöd.
Benutzeravatar
sparrow
User
Beiträge: 4600
Registriert: Freitag 17. April 2009, 10:28

Das Öffnen von Dateien sollte man mit dem with-statement kapseln.
BlackJack

@unbekannt100: Importe sollte man nicht in Funktionen verstecken sondern oben im Modul aufführen. Dann sieht man auf den ersten Blick wovon ein Modul abhängt.

Der feste Dateiname für die temporäre Datei ist schlecht. Man kann die Funktion so zum Beispiel nicht verwenden um in mehreren Threads parallel Daten herunter zu laden. Ausserdem ist die Datei relativ zum aktuellen Arbeitsverzeichnis des Prozesses — da muss man aber gar keine Schreibrechte besitzen. Für temporäre Dateien gibt es deshalb das `tempfile`-Modul.

Letztendlich ist eine temporäre Datei hier aber auch vollkommen überflüssig, weil man statt `urlretrieve()` auch `urlopen()` verwenden kann und die Daten direkt von dort in den Speicher lesen kann.

Ein weiteres Problem mit der temporären Datei ist der Dateimodus — aus dem Netz können beliebige Binärdateien kommen, dementsprechend muss man die Datei auch im Binärmodus öffnen, sonst kann es passieren, dass man am Ende nicht die Daten unverändert im Speicher hat, wie sie im Internet standen.

Die „Fehlerbehandlung“ ist sehr schlecht. Um spezielle Fehlerrückgabewerte und die Probleme die sich daraus ergeben, los zu werden, wurden Ausnahmen erfunden. Und hier werden sämtliche Ausnahmen die auftreten können, mit all den Zusatzinformationen die so eine Ausnahme hat, auf ein simples `False` reduziert. Das zwingt den Aufrufer den Rückgabewert zu überprüfen, was man vergessen oder ignorieren kann ohne dass das für den Leser ersichtlich ist. Und es nimmt dem Aufrufer jede Möglichkeit differenziert auf Probleme zu reagieren.

Bei der Fehlersuche fallen einem solche „nackten” ``except``\s die Ausnahmen „verschlucken” auch schnell auf die Füsse weil man nicht sieht *warum* eine Funktion nicht das tut was sie soll. Wenn man irgendwo einfach alle Ausnahmen unterdrückt, sollte man sie zumindest protokollieren, damit man nachvollziehen kann welche Ausnahmen konkret aufgetaucht sind, wenn man diese Information zur Fehlersuche benötigt.
unbekannt100

@BlackJack:

Das mit der temporären Datei ist auch gar nicht das Thema, die Methode soll nur symbolisieren was ich eigentlich erreichen will.
Ich würde es ja auch gerne mit urlopen() machen, aber das liefert mir eben Bytes zurück (bei manchen Seiten, wie ich mittlerweile festgestellt habe).

Aber im Prinzip habe ich jetzt eine Lösung für das Problem gefunden (bzw. weiß, dass es nicht geht da Google automatisierte Anfragen ohnehin abblockt).
BlackJack

@unbekannt100: Bei welcher Seite liefert `urlopen()` denn keine Bytes zurück? Das sollte *immer* Bytes liefern, denn das ist schliesslich das was da über die Verbindung rein kommt.

Vor dem Hintergrund ist Deine Funktion noch unsinniger, denn das Du einfach nur die Bytes dekodiert haben möchtest, geht selbstverständlich *auch* ohne eine temporäre Datei. So wie Du das dort machst ist das ziemlich kaputt, denn Du hoffst, dass die lokale „Standardkodierung” zufällig die von der geladenen Webseite ist. Ob das klappt oder nicht hängt also sowohl von dem Rechner ab, auf dem man es ausführt, als auch von der konkreten Webseite.

Du musst die Bytes lesen und aus den Header-Informationen der Antwort und den Bytes der Antwort bei (X)HTML ermitteln welche Kodierung überhaupt verwendet werden muss. Selbst dann muss man noch damit rechnen das diese Informationen falsch sind. Dann könnte man die Bytes analysieren und raten. So gehen jedenfalls Browser vor.

Da es laut sparrow `lxml.html` für Python 3 gibt, würde ich das alles der Bibliothek überlassen, statt das Rad neu zu erfinden.
Antworten