【问题标题】:How to decode cp1252 which is in decimal &#147 instead of \x93?如何解码十进制&#147而不是\ x93的cp1252?
【发布时间】:2025-12-10 14:15:01
【问题描述】:

我正在获取网页的源代码,编码是 cp1252。 Chrome 可以正确显示页面。

这是我的代码:

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

从预览部分,我们可以看到
&nbsp\; = \xc2\xa0
§ = \xc2\xa7
x = x

对于cp1252编码标准,我指的是 http://en.wikipedia.org/wiki/Windows-1252#Code_page_layout 和 /Lib/encodings/cp1252.py

当我使用 BeautifulSoup(page, from_encoding="cp1252") 时,一些字符被正确编码,但另一些则不是。

字符 |十进制编码 | cp1252->utf-8 编码
“ | “ | \xc2\x93(错误)
” | ” | \xc2\x94(错误)
X | x | \xc2\x92(错误)
§ | § | \xc2\xa7 (好的)
þ | þ
¨ | ¨
' | ’ | \xc2\x92(错误)
– | –

我使用这段代码来获得等价:

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

输出

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

有些字符无法复制粘贴到编辑器,比如奇怪的 X 和奇怪的 ',所以我添加了一些代码来处理。

我可以做什么来获取 \xe2\x80\x9d 而不是 \xc2\x94 for ” (”)?

我的设置:
视窗 7
终端:chcp 1252 + Lucida 控制台字体
Python 3.3
美丽汤 4

期待您的回答

【问题讨论】:

    标签: encoding utf-8 python-3.x beautifulsoup cp1252


    【解决方案1】:

    这就是我最终使用的

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

    注意:我在 windows7 中使用 bs4 和 python3.3

    我发现BeautifulSoup的from_encoding真的无关紧要,你可以输入utf-8或windows-1252,它提供了一个完整的utf-8编码,将windows-1252编码替换为utf-8。
    基本上所有的代码点都被解释为 utf-8 和单字节 \x?被解释为 windows-1252。

    据我所知,windows-1252 中只有 128 到 159 的字符与 utf-8 字符不同。

    例如,混合编码(windows-1252 : \x93 和 \x94 与 utf-8 : Ÿ)将仅输出 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.
    

    【讨论】:

      【解决方案2】:

      Beautiful soup 将实体中的代码点解释为 Unicode 代码点,而不是 CP-1252 代码点,例如,&amp;#147; 中的数字。从 BeautifulSoup 4 的文档和源代码来看,尚不清楚是否有办法改变对 HTML 实体的这种解释。 (EntitySubstitution 类看起来很有希望,但没有暴露用于自定义它的钩子。)

      以下解决方案是 hackey,仅在所有非 ASCII(即代码点 127 以上)字符都被以相同方式误解的假设下有效(如果有原始 CP-1252 字符则不会出现这种情况在原始版本中,BeautifulSoup 将正确解释;此解决方案将破坏这些字符)。

      假设您有来自 Beautiful Soup 转换的文本(将 HTML 代码解释为 Unicode 代码点):

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

      以下将代码重新解释为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))
      

      此解决方案并未针对性能进行优化,但我认为对于这种特殊情况可能足够好

      我从您在示例中提供的 URL 的“固定”文本中提取了 127 以上的所有代码点。这是我得到的(似乎涵盖了你感兴趣的角色):

      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'
      

      【讨论】:

        【解决方案3】:

        HTML 中的数字字符引用指的是 Unicode 代码点,即 它不依赖于文档的字符编码 例如,&amp;#148;U+0094 CANCEL CHARACTER*

        b"\xe2\x80\x9d" 解释为 utf-8 的字节为 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;'
        

        要修复代码,请删除不必要的位:

        from urllib.request import urlopen
        from bs4 import BeautifulSoup
        
        url = "http://www.sec.gov/path/to.htm"
        soup = BeautifulSoup(urlopen(url))
        print(soup)
        

        如果失败;尝试sys.stdout.buffer.write(soup.encode('cp1252')) 或将PYTHONIOENCODING 环境变量设置为cp1252:xmlcharrefreplace

        【讨论】:

        • 为我工作(Windows 10 上的 python 3.7.3)。 Python 遇到 &check; 的问题(02713) 但仅在管道输出时。也许是因为这个注意,PYTHONIOENDODING 通常在 Windows 上被忽略 docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING
        • @stephanf “忽略”部分是指使用 Unicode 控制台 API 的情况(在这种情况下,输出未编码为字节,因此不使用 PYTHONIOENCODING)。管道是另一种情况,默认情况下,输出 is 使用locale.getpreferredencoding(False) 编码(类似于 cp1252)进行编码。 PYTHONIOENCODING envvar 应该在这种情况下生效。 Python, Unicode, and the Windows console