2013-06-30 17 views
5

Otrzymuję kod źródłowy strony internetowej, a kodowanie to cp1252. Chrome wyświetla poprawnie stronę.Jak odszyfrować cp1252, który jest w systemie dziesiętnym? Zamiast x93?

Oto mój kod:

import sys 
from urllib.request import urlopen 
from bs4 import BeautifulSoup, UnicodeDammit 
import re 
import codecs 

url = "http://www.sec.gov/Archives/edgar/data/1400810/000119312513211026/d515005d10q.htm" 
page = urlopen(url).read() 
print(page) 
# A little preview : 
# b'...Regulation S-T (&#167;232.405 of this chapter) during the preceding 12 months (or for such shorter period that the\nregistrant was required to submit and post such files).&nbsp;&nbsp;&nbsp;&nbsp;Yes&nbsp;&nbsp;<FONT STYLE="FONT-FAMILY:WINGDINGS">&#120;</FONT>...' 

soup = BeautifulSoup(page, from_encoding="cp1252") 
print(str(soup).encode('utf-8')) 
# Same preview section as above 
# b'...Regulation S-T (\xc2\xa7232.405 of this chapter) during the preceding 12 months (or for such shorter period that the\nregistrant was required to submit and post such files).\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0Yes\xc2\xa0\xc2\xa0<font style="FONT-FAMILY:WINGDINGS">x</font>' 

W sekcji podglądu, możemy zobaczyć, że
& nbsp \; = \ xc2 \ xa0
& # 167; = \ xc2 \ xa7
& # 120; = X

dla standardu kodowania CP1252, jestem refering http://en.wikipedia.org/wiki/Windows-1252#Code_page_layout i /Lib/encodings/cp1252.py

Gdy używam BeautifulSoup (strona, from_encoding = "CP1252") niektóre znaki są kodowane poprawnie, ale inne nie.

znak | kodowanie dziesiętne | cp1252-> utf-8 kodowanie
"| & # 147; | \ xc2 \ x93 (źle)
"| & # 148; | \ xc2 \ x94 (źle)
X | & # 120; | \ xc2 \ x92 (źle)
§ | & # 167; | \ xc2 \ xa7 (ok)
þ | & # 254;
¨ | & # 168;
"| & # 146; | \ xc2 \ x92 (źle)
- | & # 150;

używam tego kodu, aby uzyskać równoważność:

characters = "’ “ ” X § þ ¨ ' –" 
list = characters.split() 

for ch in list: 
    print(ch) 
    cp1252 = ch.encode('cp1252') 
    print(cp1252) 

    decimal = cp1252[0] 

    special = "&#" + str(decimal) 
    print(special) 
    print(ch.encode('utf-8')) 
    print() 

offenders = [120, 146] 

for n in offenders: 
    toHex = hex(n) 
    print(toHex) 
print() 

#120 
off = b'\x78' 
print(off) 
buff = off.decode('cp1252') 
print(buff) 
uni = buff.encode('utf-8') 
print(uni) 
print() 

#146 
off = b'\x92' 
print(off) 
buff = off.decode('cp1252') 
print(buff) 
uni = buff.encode('utf-8') 
print(uni) 
print() 

wyjście

’ 
b'\x92' 
&#146 
b'\xe2\x80\x99' 

“ 
b'\x93' 
&#147 
b'\xe2\x80\x9c' 

” 
b'\x94' 
&#148 
b'\xe2\x80\x9d' 

X 
b'X' 
&#88 
b'X' 

§ 
b'\xa7' 
&#167 
b'\xc2\xa7' 

þ 
b'\xfe' 
&#254 
b'\xc3\xbe' 

¨ 
b'\xa8' 
&#168 
b'\xc2\xa8' 

' 
b"'" 
&#39 
b"'" 

– 
b'\x96' 
&#150 
b'\xe2\x80\x93' 

0x78 
0x92 

b'x' 
x 
b'x' 

b'\x92' 
’ 
b'\xe2\x80\x99' 

Niektóre znaki nie powiodło się kopiowania i wklejania do redakcji niczym dziwnym X i dziwne”, więc dodałem jakiś kod, żeby sobie z tym poradzić.

Co mogę zrobić z get \ xe2 \ x80 \ x9d zamiast \ xc2 \ x94 dla "(& # 148;)?

Moja konfiguracja:
Windows 7
terminalowe: CHCP 1252 + Lucida Console czcionki
Python 3.3
BeautifulSoup 4

Czekamy na odpowiedzi

Odpowiedz

1

To, co skończyło się używając

def reformatCp1252(match): 
    codePoint = int(match.group(1)) 

    if 128 <= codePoint <= 159: 
     return bytes([codePoint]) 
    else: 
     return match.group() 

localPage = urlopen(r_url).read() 
formatedPage = re.sub(b'&#(\d+);', reformatCp1252, localPage, flags=re.I) 
localSoup = BeautifulSoup(formatedPage, "lxml", from_encoding="windows-1252") 

Uwagi: Używam BS4 z python3.3 w Windows7

odkryłem, że from_encoding do BeautifulSoup naprawdę nie ma znaczenia, można umieścić utf-8 lub windows-1252 i daje pełne kodowanie utf-8 zastępujące kodowanie Windows-1252 utf-8.
Zasadniczo wszystkie kodowalne są interpretowane jako utf-8 i pojedynczy bajt \ x? są interpretowane jako Windows-1252.
O ile mi wiadomo, tylko znaki od 128 do 159 w oknach-1252 różnią się od znaków utf-8.

Na przykład mieszane kodowanie (Windows-1252: \ x93 i \ x94 z utf-8: & # 376;) wyświetli transformację tylko w utf-8.

byteStream = b'\x93Hello\x94 (\xa7232.405 of this chapter) &#376; \x87' 
# with code above 
print(localSoup.encode('utf-8')) 
# and you can see that \x93 was transformed to its utf-8 equivalent. 
0

Piękny zupa jest interpretowania kodu punkty w obiekcie jest to liczba w, powiedzmy, &#147; jako kodowy kod Unicode, a nie punkty kodowe CP-1252. Z dokumentacji i źródła dla BeautifulSoup 4 nie jest jasne, czy istnieje sposób na zmianę tej interpretacji encji HTML. (Klasa EntitySubstitution wyglądała obiecująco, ale nie ujawniono żadnych haczyków do jej dostosowania.)

Następujące rozwiązanie jest hackey i działa tylko przy założeniu, że wszystkie znaki spoza ASCII (tj. Powyżej 127 punktów kodu) zostały błędnie zinterpretowane w ten sam sposób (nie stanie się tak w przypadku surowych znaków CP-1252 w oryginale, który BeautifulSoup będzie interpretował poprawnie, to rozwiąże te znaki).

Zakładając, że masz tekst z konwersją Piękne Soup za (z kody HTML interpretowane jako Unicode kodowej punktów):

soup = BeautifulSoup(page, from_encoding="cp1252") 
txt = str(soup) 

Poniższa ponownie zinterpretować kody jak CP-1252:

def reinterpret_codepoints(chars, encoding='cp1252'): 
    '''Converts code-points above 127 in the text to the given 
    encoding (assuming that all code-points above 127 represent 
    code-points in the given encoding) 
    ''' 
    for char, code in zip(chars, map(ord, txt)): 
     if code < 127: 
      yield char 
     else: 
      yield bytes((code,)).decode(encoding) 

fixed_text = ''.join(reinterpret_codepoints(txt)) 

To rozwiązanie nie jest zoptymalizowane pod kątem wydajności, ale myślę, że może to być wystarczająco dobre dla tego konkretnego przypadku:.

Wyciągnąłem wszystkie punkty kodowe powyżej 127 z "ustalonego" tekstu dla adresu URL podanego w przykładzie. To, co mam (wydaje się obejmować znaki, które Cię interesują):

char | Unicode code-point | CP-1252 code-point | CP-1252 | UTF-8 
  | 160 | 160 | b'\xa0' | b'\xc2\xa0' 
§ | 167 | 167 | b'\xa7' | b'\xc2\xa7' 
¨ | 168 | 168 | b'\xa8' | b'\xc2\xa8' 
– | 8211 | 150 | b'\x96' | b'\xe2\x80\x93' 
— | 8212 | 151 | b'\x97' | b'\xe2\x80\x94' 
’ | 8217 | 146 | b'\x92' | b'\xe2\x80\x99' 
“ | 8220 | 147 | b'\x93' | b'\xe2\x80\x9c' 
” | 8221 | 148 | b'\x94' | b'\xe2\x80\x9d' 
• | 8226 | 149 | b'\x95' | b'\xe2\x80\xa2' 
0

numeryczne odniesienia w postaci HTML odnosi się do Unicode punkt kodowy tj to nie zależy od kodowania znaków dokumentu np. &#148; to U+0094 CANCEL CHARACTER*.

b"\xe2\x80\x9d" bajty interpretowane jako UTF-8 są U+201D RIGHT DOUBLE QUOTATION MARK:

u'\u201d'.encode('utf-8') == b'\xe2\x80\x9d' 
u'\u201d'.encode('cp1252') == b'\x94' 
u'\u201d'.encode('ascii', 'xmlcharrefreplace') == b'&#8221;' 

Aby rozwiązać ten kod, usunąć niepotrzebne bitów:

from urllib.request import urlopen 
from bs4 import BeautifulSoup 

url = "http://www.sec.gov/path/to.htm" 
soup = BeautifulSoup(urlopen(url)) 
print(soup) 

Jeśli to się nie powiedzie; spróbuj sys.stdout.buffer.write(soup.encode('cp1252')) lub ustaw zmienną środowiskową PYTHONIOENCODING na cp1252:xmlcharrefreplace.

Powiązane problemy