2009-09-30 9 views
32

Problem

Podczas przesiewania ekranu strony przy użyciu Pythona należy znać kodowanie znaków strony. Jeśli kodowanie znaków będzie nieprawidłowe, wyniki będą pomieszane.Jak pobrać dowolną (!) Stronę z poprawnym zestawem znaków w pythonie?

Ludzie zazwyczaj używają podstawowej techniki do wykrywania kodowania. Używają zestawu znaków z nagłówka lub zestawu znaków zdefiniowanego w metatagu lub używają nazwy encoding detector (która nie dba o metatagi lub nagłówki). Używając tylko jednej z tych technik, czasami nie uzyskasz takiego samego wyniku, jak w przeglądarce.

Przeglądarki zrobić to w ten sposób:

  • Metatagi zawsze ma pierwszeństwo (lub definicji XML)
  • Kodowanie zdefiniowanego w nagłówku jest używany, gdy nie ma zdefiniowane w znaczniku meta charset
  • Jeśli kodowanie w ogóle nie jest zdefiniowane, niż czas na wykrycie kodowania.

(No ... przynajmniej, że jest sposób, wierzę większość przeglądarek to zrobić. Dokumentacja jest naprawdę rzadkością.)

Co szukam to biblioteka, która może zadecydować zestaw znaków strony w taki sam sposób jak przeglądarka. Jestem pewien, że nie jestem pierwszym, który potrzebuje odpowiedniego rozwiązania tego problemu.

Rozwiązanie (nie próbowałem go jeszcze ...)

Według Beautiful Soup's documentation.

Piękne Soup próbuje następujące kodowanie, w kolejności, aby obrócić dokument w formacie Unicode:

  • Kodowanie przekazać w jak fromEncoding argument do zupy konstruktora.
  • Kodowanie odkryte w samym dokumencie: na przykład w deklaracji XML lub (dla dokumentów HTML) znacznik META równoważny http. Jeśli Beautiful Soup znajdzie ten rodzaj kodowania w dokumencie, od nowa analizuje dokument i daje nowe próby kodowania. Jedynym wyjątkiem jest to, że wyraźnie określiłeś kodowanie i kodowanie faktycznie zadziałało: wtedy zignoruje kodowanie znalezione w dokumencie.
  • Kodowanie powąchano, patrząc na pierwsze kilka bajtów pliku. Jeśli na tym etapie zostanie wykryte kodowanie , będzie to jedno z kodowań UTF- *, EBCDIC lub ASCII.
  • Kodowanie wąchane przez bibliotekę chardet , jeśli jest zainstalowane.
  • UTF-8
  • systemu Windows-1252
+4

Nie można pobrać z prawidłowym zestawem znaków. Przeglądarki odgadują błędnie przez cały czas, gdy poprawny zestaw znaków nie jest określony. Używam menu widoku-> kodowania w FF, aby codziennie naprawiać nieprawidłowe domysły. Chcesz robić tak dobrze, jak możesz, ale porzuć zgadywanie każdej strony poprawnie. –

+7

Odgadywanie zestawów znaków jest złe i wpędza nas w ten bałagan. Jeśli przeglądarki nigdy nie próbowały zgadywać, programiści byliby zmuszeni dowiedzieć się o nagłówkach HTTP i zawsze dokładnie określać kodowanie. Zgadywanie oznacza, że ​​kiedyś się pomylisz –

+0

Gnibbler, zgadywanie to ostatnia deska ratunku –

Odpowiedz

3

użyłbym html5lib do tego.

+2

To wygląda naprawdę ładnie. Dokumentacja dotycząca sposobu odkrycia kodowania: http: //html5lib.readthedocs.org/pl/latest/movingparts.html # encoding-discovery –

14

Użyj Universal Encoding Detector:

>>> import chardet 
>>> chardet.detect(urlread("http://google.cn/")) 
{'encoding': 'GB2312', 'confidence': 0.99} 

Innym rozwiązaniem byłoby po prostu użyć wget:

import os 
    h = os.popen('wget -q -O foo1.txt http://foo.html') 
    h.close() 
    s = open('foo1.txt').read() 
+0

To nie jest dobre, ponieważ czasami się nie udaje. Zobacz także: http://chardet.feedparser.org/docs/faq.html#faq.yippie (Yippie!) –

+0

Głównym problemem w tym podejściu jest ignorowanie jawnie określonego kodowania znaków strony. –

+2

OK, to nie ma tu srebrnej kuli, obawiam się - więc napisz to sam. :) – rajax

36

po pobraniu plik z urllib lub urllib2, możesz dowiedzieć się, czy został przesłany nagłówek zestawu znaków:

fp = urllib2.urlopen(request) 
charset = fp.headers.getparam('charset') 

Można użyć BeautifulSoup zlokalizować meta element HTML:

soup = BeatifulSoup.BeautifulSoup(data) 
meta = soup.findAll('meta', {'http-equiv':lambda v:v.lower()=='content-type'}) 

Jeśli nie jest dostępny, przeglądarki zazwyczaj spadają z powrotem do konfiguracji użytkownika, w połączeniu z funkcją automatycznego wykrywania. Jak proponuje rajax, możesz użyć modułu chardet. Jeśli masz dostępną konfigurację użytkownika informującą, że strona powinna być chińska (powiedzmy), być może uda Ci się to zrobić lepiej.

+0

Myślę, że to 'getparam' – u0b34a0f6ae

+5

@ kaizer.se: right; to 'get_param' w 3.x (ale potem jest to również urllib.request) –

+0

Niestety (przynajmniej w Pythonie 2.7) urllib2 nie parsuje charset z nagłówka Content-Type, więc musisz zrobić coś takiego odpowiedź w http://stackoverflow.com/a/1020931/69707 –

1

zamiast próbować dostać stronę potem zastanawianie się charset przeglądarka będzie używać, dlaczego nie wystarczy użyć przeglądarki, aby pobrać stronę i sprawdź co charset korzysta ..

from win32com.client import DispatchWithEvents 
import threading 


stopEvent=threading.Event() 

class EventHandler(object): 
    def OnDownloadBegin(self): 
     pass 

def waitUntilReady(ie): 
    """ 
    copypasted from 
    http://mail.python.org/pipermail/python-win32/2004-June/002040.html 
    """ 
    if ie.ReadyState!=4: 
     while 1: 
      print "waiting" 
      pythoncom.PumpWaitingMessages() 
      stopEvent.wait(.2) 
      if stopEvent.isSet() or ie.ReadyState==4: 
       stopEvent.clear() 
       break; 

ie = DispatchWithEvents("InternetExplorer.Application", EventHandler) 
ie.Visible = 0 
ie.Navigate('http://kskky.info') 
waitUntilReady(ie) 
d = ie.Document 
print d.CharSet 
+0

po prostu przetestowałem to na origo.hu i działa, aczkolwiek niesamowicie wolno - może spróbuję z komponentem firefox activex zamiast tego – Ravi

3

Wygląda jak ty potrzebuję hybrydę odpowiedzi przedstawione:

  1. pobrać strony za pomocą urllib
  2. Znajdź <meta> tagów za pomocą pięknej zupę lub inną metodę
  3. Jeśli nie istnieją znaczniki meta, sprawdź nagłówki zwrócone przez urllib
  4. Jeśli to nadal nie daje odpowiedzi, użyj uniwersalnego detektora kodowania.

Szczerze, nie wierzę, że znajdziesz coś lepszego.

W rzeczywistości, jeśli czytasz dalej w najczęściej zadawanych pytaniach, które zawierałeś w komentarzach do drugiej odpowiedzi, autor zalecił bibliotekę detektorów.

Jeśli uważasz, że często zadawane pytania są tym, co robią przeglądarki (zgodnie z pierwotnym pytaniem), ponieważ detektor jest portem kodu wykrywającego firefox.

+0

To, co uważam za dziwne, to że nie ma tam żadnej biblioteki/fragmentu. –

+0

Stobor wskazał na istnienie feedparser.py (który jest niestety tylko dla XML), ale zawiera większość rzeczy, których potrzebuję. –

+0

Algorytm nie jest poprawny, ponieważ nagłówki HTTP powinny mieć pierwszeństwo przed metatagami. Brakuje również znaków BOM i kroku normalizacji kodowania (kodowanie nazw w HTML/HTTP nie jest tym samym, co nazwy dostarczone przez Python). –

2

Aplikacja Scrapy pobiera stronę i wykrywa poprawne kodowanie, w przeciwieństwie do pliku requests.get (url) .text lub urlopen. W tym celu stara się postępować zgodnie z regułami podobnymi do przeglądarki - jest to najlepsze, ponieważ właściciele witryn mają motywację, aby ich witryny działały w przeglądarce. Scrapy musi przyjmować nagłówki HTTP, <meta>, oznaczenia BOM i różnice w nazwach kodowania na koncie.

Samodzielne odgadywanie zawartości (chardet, UnicodeDammit) nie jest rozwiązaniem właściwym, ponieważ może się nie udać; powinien być używany tylko w ostateczności, gdy nagłówki lub znaki <meta> lub BOM są niedostępne lub nie zawierają żadnych informacji.

Nie musisz używać funkcji Scrapy, aby uzyskać funkcje wykrywania kodowania; są one wydawane (między innymi z innymi) w osobnej bibliotece o nazwie w3lib: https://github.com/scrapy/w3lib.

Aby uzyskać kodowanie Unicode strona ciała i użyć w3lib.encoding.html_to_unicode funkcję, o zawartości opartej zgadywania awaryjna: „any” stronie

import chardet 
from w3lib.encoding import html_to_unicode 

def _guess_encoding(data): 
    return chardet.detect(data).get('encoding') 

detected_encoding, html_content_unicode = html_to_unicode(
    content_type_header, 
    html_content_bytes, 
    default_encoding='utf8', 
    auto_detect_fun=_guess_encoding, 
) 
Powiązane problemy