2013-06-30 11 views
5

Ricevo il codice sorgente di una pagina Web e la codifica è cp1252. Chrome visualizza la pagina correttamente.Come decodificare cp1252, che è in decimale e # 147 invece di x93?

Ecco il mio codice:

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>' 

Dalla sezione anteprima, possiamo vedere che
& nbsp \; = \ xc2 \ xa0
& # 167; = \ xc2 \ xa7
& # 120; = X

Per lo standard di codifica CP1252, mi riferisco a http://en.wikipedia.org/wiki/Windows-1252#Code_page_layout e /Lib/encodings/cp1252.py

Quando uso BeautifulSoup (pagina from_encoding = "CP1252") alcuni caratteri sono codificati correttamente, ma alcuni altri non lo sono.

carattere | codifica decimale | cp1252-> codifica utf-8
"| & # 147; | \ xc2 \ x93 (errato)
"| & # 148; | \ xc2 \ x94 (errato)
X | & # 120; | \ xc2 \ x92 (errato)
§ | & # 167; | \ xc2 \ xa7 (ok)
þ | & # 254;
¨ | & # 168;
'| & # 146; | \ xc2 \ x92 (errato)
- | & # 150;

Io uso questo codice per ottenere l'equivalenza:

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() 

uscita

’ 
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' 

Alcuni caratteri fallito il copia-incolla per l'editor come strano X e strana', così ho aggiunto un codice per affrontarlo.

Cosa posso fare per ottenere \ xe2 \ x80 \ x9d invece di \ xc2 \ x94 per "(& # 148;)?

La mia configurazione:
Windows 7
Terminal: CHCP 1252 + Lucida Console caratteri
Python 3.3
BeautifulSoup 4

In attesa di vostre risposte

risposta

1

Questo è quello che ho finito per usare

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") 

Note: Sto usando BS4 con python3.3 in windows7

ho scoperto che il from_encoding al BeautifulSoup davvero non importa, si può mettere utf-8 o windows-1252 e fornisce una codifica utf-8 completa che sostituisce la codifica windows-1252 con utf-8.
Fondamentalmente tutti i codepoint sono interpretati come utf-8 e byte singolo \ x? sono interpretati come Windows-1252.

Per quanto ne so, solo i caratteri da 128 a 159 in windows-1252 differiscono dai caratteri utf-8.

Ad esempio, una codifica mista (windows-1252: \ x93 e \ x94 con utf-8: & # 376;) genererà una trasformazione solo in 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

Bella minestra sta interpretando il codice-punti nell'entità, questo è il numero, ad esempio, &#147; come punto di codice Unicode anziché i punti di codice CP-1252. Dalla documentazione e dall'origine di BeautifulSoup 4, non è chiaro se esiste un modo per modificare questa interpretazione delle entità HTML. (La classe EntitySubstitution sembrava promettente ma nessun hook per la personalizzazione è esposto.)

La seguente soluzione è hackey e funzionerà solo presupponendo che tutti i caratteri non ASCII (ovvero sopra i punti codice 127) siano stati interpretati erroneamente nello stesso modo (ciò non si verificherà se vi fossero caratteri raw CP-1252 nell'originale, che BeautifulSoup interpreterà correttamente, questa soluzione manipolerà quei caratteri).

Supponendo di avere il testo dalla conversione della bella zuppa (con i codici HTML interpretati come Unicode in codice punti):

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

Di seguito si ri-interpretare i codici come 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)) 

Questa soluzione non è ottimizzata per le prestazioni, ma penso che potrebbe essere abbastanza buono per questo caso particolare.

Ho estratto tutti i punti di codice sopra 127 dal testo "fisso" per l'URL che hai fornito nell'esempio. Questo è quello che ho avuto (sembra coprire i caratteri che interessano):

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

Un riferimento carattere numerico in HTML si riferisce ad un Unicode codepoint cioè, esso non dipende codifica dei caratteri del documento ad esempio, &#148; è U+0094 CANCEL CHARACTER*.

b"\xe2\x80\x9d" byte interpretata come UTF-8 sono 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;' 

Per fissare il codice, rimuovere i bit necessari:

from urllib.request import urlopen 
from bs4 import BeautifulSoup 

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

Se fallisce; prova sys.stdout.buffer.write(soup.encode('cp1252')) o imposta la variabile di ambiente PYTHONIOENCODING su cp1252:xmlcharrefreplace.

Problemi correlati