encoding von webseite herausfinden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
feuervogel
User
Beiträge: 28
Registriert: Dienstag 18. September 2007, 14:59

hallo,

ich weiß, dass ich nicht der erste bin, der dieses problem hat.

ich lade mit urllib2 eine webseite runter und möchte nun feststellen, in welchem encoding sie vorliegt, damit ich sie korrekt nach utf-8 dekodieren kann.

meine bisherige vorgehensweise ist folgende:

Code: Alles auswählen

import urllib2

request = urllib2.Request(url="http://www.google.co.jp/")
request.add_header('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3')
request.add_header('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,1;q=0.7')
remotefile = urllib2.urlopen(request)

def getEncodings():
	return ['ISO-8859-1','utf-8']
	#return [item.replace("_","-") for item in locale.encodings.aliases.aliases.values()]

html = remotefile.read()
flag = False

for e in getEncodings():
	try:
		s = html.decode(e)
		if s.encode(e) == html:
			print "hat geklappt: " + e
			flag = True
			break;
	except Exception, f:
		print "hat nicht geklappt: " + e

if flag == False:
	print ">>> INFO kein encoding gefunden"

die funktioniert nicht, wie man sieht. wie macht man sowas ordentlich/möglichst treffsicher, ohne im html-code nach dem encoding zu suchen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

feuervogel hat geschrieben:ich lade mit urllib2 eine webseite runter und möchte nun feststellen, in welchem encoding sie vorliegt, damit ich sie korrekt nach utf-8 dekodieren kann
Hallo feuervogel!

Da ich nicht weiß, wie das mit urllib2 funktionieren könnte, würde ich persönlich wget verwenden.

Wget ist ein kleines Kommandozeilenprogramm mit dem man Webpages/-sites herunterladen kann. Mit dem Parameter "-S" gibt man an, dass man die Header ebenfalls sehen möchte. Das kann man gut von Python aus ausnutzen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import subprocess

for index, url in enumerate(("http://halvar.at/", "http://www.python-forum.de")):
    charsetline = None
    filename = "%i_index.html" % index
    zieldatei = file(filename, "wb")
    
    proc = subprocess.Popen(
        ["wget", "-S", "-O", "-", url], 
        stdout = zieldatei, stderr = subprocess.PIPE
    )
    for line in proc.stderr:
        #print line,
        if line.find("charset") > -1:
            charsetline = line
    
    zieldatei.close()
    
    if not charsetline:
        zieldatei = file(filename, "rU")
        for line in zieldatei:
            if line.find("charset") > 0:
                charsetline = line
                break
        zieldatei.close()
    
    print "URL:", url
    print "Charset line:", charsetline
Wget findest du hier:
- http://www.gnu.org/software/wget/
- Windows: http://gnuwin32.sourceforge.net/packages/wget.htm

Unter Windows genügt es wahrscheinlich (ich kann es nicht testen, da ich Cygwin installiert habe), die "wget.exe" in den Ordner zu legen, in dem auch dein Python-Programm liegt.

Wahrscheinlich gibt es auch schönere Lösungen für solche Probleme. Aber ich habe es aufgegeben, nach "schönen" Lösungen zu suchen, wenn ich in ein paar Minuten eine nicht so "schöne" Lösung mit Hilfe von Kommandozeilenprogrammen programmieren kann. Diese funktionieren nämlich immer genau so gut wie die "schönen" Lösungen, kosten aber weniger Nerven. ;-)

EDIT:

Wenn du die heruntergeladenen Dateien gleich nach UTF-8 umwandeln möchtest, dann kannst du statt einem file-Objekt StringIO verwenden.

mfg
Gerold
:-)
Zuletzt geändert von gerold am Donnerstag 20. September 2007, 10:15, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

feuervogel hat geschrieben:die funktioniert nicht, wie man sieht. wie macht man sowas ordentlich/möglichst treffsicher, ohne im html-code nach dem encoding zu suchen?
Also theoretisch gibt es IMHO drei Stellen an dem das Encoding festgelegt ist.

Als erstes kann in den Response Headers, sowas ähnliches stehen:

Code: Alles auswählen

Content-Type	text/html; charset=utf-8
Das könntest du nutzten, ohne den html-Code anzusehen. Ich bin mir allerdings nicht sicher ob diese Angabe im Header zwingend ist. Ich denke er nicht.

Dann gibt es noch die modere Variante in der html-Seite. Das kann so aussehen:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
Und als drittes haben wir noch die old-shool Geschichte, als Meta Angabe im html-Header:

Code: Alles auswählen

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Normalerweise müßen alle drei das selbe angeben. Was anderes macht ja keinen Sinn. Aber IMHO ist es nicht immer so ;)

Man könnte nun eine Liste machen, mit allen Encodings und die dann probieren. Das machen IMHO die Browser ähnlich. Klappt nicht immer, aber so ist's halt...
Schlußendlich kann man dann vielelicht ein decode mit dem Error Typ "replace" nutzten.

In einem alten Codestück hab nur den Meta html Teil analysiert und das so gemacht:

Code: Alles auswählen

            # Inhalt nach UTF-8 wandeln
            charset = re.findall(
                '<meta.*?content-type.*?charset=(.*?)"', sidecontent.lower()
            )[0]
            sidecontent = sidecontent.decode(charset).encode("utf_8")

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Das möchte ich noch ein wenig Kommentieren.
jens hat geschrieben:Als erstes kann in den Response Headers, sowas ähnliches stehen:

Code: Alles auswählen

Content-Type	text/html; charset=utf-8
Das könntest du nutzten, ohne den html-Code anzusehen. Ich bin mir allerdings nicht sicher ob diese Angabe im Header zwingend ist. Ich denke er nicht.
Der Apache 2 ist meist so eingestellt, dass dieser diesen Header mitschickt. Der Apache 1 meist nicht.
jens hat geschrieben:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
Wobei hier das Encoding (wie bei XML) bei UTF-8-Seiten weg gelassen werden kann.
jens hat geschrieben:Normalerweise müßen alle drei das selbe angeben. Was anderes macht ja keinen Sinn. Aber IMHO ist es nicht immer so ;)
Leider :roll:

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
feuervogel
User
Beiträge: 28
Registriert: Dienstag 18. September 2007, 14:59

hallo,

vielen dank erst mal für die antworten. die sache mit wget kann ich auch wie folgt umgehen:

Code: Alles auswählen

import urllib2

request = urllib2.Request(url="http://www.julianmoritz.de")
request.add_header('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3')
request.add_header('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,1;q=0.7')
remotefile = urllib2.urlopen(request)

print remotefile.info()['Content-type']
klappt für google.co.jp, für spiegel.de nicht.

also werde ich wohl doch auf die angaben im html zurückgreifen müssen.

es ergibt sich nämlich bei fehlerhafter kodierung ein problem: ich verschicke die daten per xmlrpc, und wenn da ein zeichen nicht korrekt kodiert wurde, beschwert sich die methode, die die daten sendet und wirft eine exception. da ich nicht die daten für eine webseite einzeln sende, sondern mehrere webseiten bündle, werden also alle daten aus dem bündel verworfen und mir geht eine menge verloren.

daher hätte ich gerne eine methode, wie ich da absolut sicher sein kann, aber das gibts wohl nicht. oder ich schicke alles einzeln...aber das wiederum frisst mir meine resssourcen auf.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Hi,

was Du suchst ist chardet. Ist klein und simpel und genau für solche dinge gedacht => guess encoding :)

mfg
feuervogel
User
Beiträge: 28
Registriert: Dienstag 18. September 2007, 14:59

poker hat geschrieben:Hi,

was Du suchst ist chardet. Ist klein und simpel und genau für solche dinge gedacht => guess encoding :)

mfg
geil, danke!

obwohl ich ja auf das guess dabei gerne verzichten würde ;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

feuervogel hat geschrieben:obwohl ich ja auf das guess dabei gerne verzichten würde ;-)
Man kann ein Encoding nicht mit 100%iger Sicherheit bestimmen, das liegt in der Natur der Daten - sonst wären ja die ganzen Encoding-Deklarationen unnütz. Daher ist ``chardet`` auch mit Vorsicht zu genießen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
feuervogel
User
Beiträge: 28
Registriert: Dienstag 18. September 2007, 14:59

Leonidas hat geschrieben:
feuervogel hat geschrieben:obwohl ich ja auf das guess dabei gerne verzichten würde ;-)
Man kann ein Encoding nicht mit 100%iger Sicherheit bestimmen, das liegt in der Natur der Daten - sonst wären ja die ganzen Encoding-Deklarationen unnütz. Daher ist ``chardet`` auch mit Vorsicht zu genießen.
ja, ist halt doof. ich verwerfe nun daten mit einer confidence von unter 0.6 und es sind trotzdem noch fehlentscheidungen dabei...aber besser als meine "methode" allemal.
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

Ja das ist halt leider so. Aber Browser tun letztendlich auch nichts anderes. Und ich denke chardet ist ein guter Kompromiss :)

mfg
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Wenn's immer noch nicht hinhaut, hat der Site-Betreiber wohl Mist gebaut. Werden dessen Inhalte dann nicht oder nicht korrekt aufgenommen - Pech gehabt, selbst schuld.
Antworten