2013-06-30 3 views
5

Ich bekomme den Quellcode einer Webseite und die Codierung ist cp1252. Chrome zeigt die Seite korrekt an.Wie cp1252 dekodieren, die in Dezimal ist & # 147 statt x93?

Hier ist mein Code:

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

Aus der Vorschau Abschnitt können wir das
& nbsp \ sehen; = \ xc2 \ xa0
& # 167; = \ xc2 \ xa7
& # 120; = X

Für die cp1252 Codierungsstandard, ich beziehe zu http://en.wikipedia.org/wiki/Windows-1252#Code_page_layout und /Lib/encodings/cp1252.py

Wenn ich BeautifulSoup (Seite from_encoding = "cp1252") einige Zeichen sind codierte verwenden richtig, aber einige andere nicht.

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

Ich benutze diesen Code zu bekommen Äquivalenz:

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

Ausgang

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

Einige Zeichen, die Copy-Paste in den Editor wie seltsam X und seltsam‘schlug fehl, also habe ich hinzugefügt etwas Code, um damit umzugehen.

Was kann ich mit dem Befehl get \ xe2 \ x80 \ x9d anstelle von \ xc2 \ x94 für "(& # 148) tun?

Mein Setup:
Windows 7
Terminal: chcp 1252 + Lucida Console Schriftart
Python 3.3
BeautifulSoup 4

Wir freuen uns auf Ihre Antworten

Antwort

1

Dies ist, was ich

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

Hinweise am Ende mit: Ich BS4 mit python3.3 in windows7

entdeckte ich bin mit, dass die from_encoding zum BeautifulSoup wirklich keine Rolle, können Sie setzen utf-8 oder windows-1252 und es gibt eine vollständige utf-8-Codierung, die die Windows-1252-Codierung in utf-8 ersetzt.
Grundsätzlich werden alle Codepunkte als utf-8 und single byte \ x interpretiert? werden als Windows-1252 interpretiert.

Soweit ich weiß, unterscheiden sich nur Zeichen von 128 bis 159 in den Windows-1252 von den UTF-8 Zeichen.

Zum Beispiel wird eine gemischte Codierung (Windows-1252: \ x93 und \ x94 mit utf-8: & # 376;) eine Transformation nur in utf-8 ausgeben.

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

Schöne Suppe interpretiert die Code-Punkte in der Entität ist das die Zahl in beispielsweise als Unicode-Codepunkt und nicht als CP-1252 Codepunkte. Aus der Dokumentation und Quelle für BeautifulSoup 4 ist nicht ersichtlich, ob es eine Möglichkeit gibt, diese Interpretation von HTML-Entitäten zu ändern. (Die Klasse EntitySubstitution sah vielversprechend aus, aber es gibt keine Möglichkeiten, sie anzupassen.)

Die folgende Lösung ist Hackey und funktioniert nur unter der Annahme, dass alle Nicht-ASCII-Zeichen (dh über Codepunkte 127) auf die gleiche Weise falsch interpretiert wurden (dies ist nicht der Fall, wenn rohe CP-1252 Zeichen vorhanden waren) im Original, das BeautifulSoup richtig interpretieren wird, wird diese Lösung diese Zeichen verfehlen).

Unter der Annahme, dass Sie den Text aus Schöne Suppe der Umwandlung haben (mit den HTML-Code als Unicode-Codepunkten interpretiert):

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

Im Folgenden wird neu interpretieren die Codes als 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)) 

Diese Lösung ist nicht für die Leistung optimiert, aber ich denke, es könnte gut genug für diesen speziellen Fall sein.

Ich extrahierte alle Codepunkte über 127 aus dem "festen" Text für die URL, die Sie in Ihrem Beispiel angegeben haben. Dies ist, was ich habe (scheint die Zeichen zu decken, die Sie interessiert sind):

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

Ein numerisches Bezugszeichen in HTML bezieht sich auf einen Unicode-Codepoint dh es hängt nicht von Zeichencodierung des Dokuments z. B. &#148; ist U+0094 CANCEL CHARACTER*.

b"\xe2\x80\x9d" Bytes als utf-8 interpretiert sind 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;' 

den Code zu beheben, unnötige Bits entfernen:

from urllib.request import urlopen 
from bs4 import BeautifulSoup 

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

Wenn es fehlschlägt; Versuchen Sie sys.stdout.buffer.write(soup.encode('cp1252')) oder setzen Sie PYTHONIOENCODING Umgebungsvariable auf cp1252:xmlcharrefreplace.