V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zl2003cn
V2EX  ›  Python

求解汉字编码问题

  •  
  •   zl2003cn · 2016-09-07 17:07:10 +08:00 · 19989 次点击
    这是一个创建于 2997 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这个页面 jobs.51job.com/shenzhen-baq/81498324.html?s=0 ,页面上是写着 gb2312 编码的,我用 Python 3.4 解码,gb2312,gbk,gb18030,utf8 都试过了,根本解不出正确的字符,但是直接把 Binary 写进 txt,notepad 可以正确识别,求解这到底是什么编码?

    7 条回复    2018-08-21 17:36:37 +08:00
    imn1
        1
    imn1  
       2016-09-07 17:16:23 +08:00
    你这样说什么都不清楚
    什么系统
    在哪测试,命令行、 web 、 GUI 还是 IDE
    测试环境的编码是什么
    mgna17
        2
    mgna17  
       2016-09-07 17:25:42 +08:00
    试了一下, utf-8 解不出来, gbk 、 gb2312 、 gb18030 全都能解出来
    Magic347
        3
    Magic347  
       2016-09-07 17:34:40 +08:00   ❤️ 3
    取到的网页文字内容在编码上存在一定的 trick ,简单来说就是 unicode 形式的 gbk 编码内容,形如: u'\xd6\xb0\xce\xbb\xc3\xe8\xca\xf6'

    而事实上,这个字符串实际所要表达的 gbk 编码内容为
    '\xd6\xb0\xce\xbb\xc3\xe8\xca\xf6',对应的汉字字符为“职位描述”

    解这个问题可参见
    http://stackoverflow.com/questions/14539807/convert-unicode-with-utf-8-string-as-content-to-str

    可以看到,关键之处在于利用了以下这一特性:
    Unicode codepoints U+0000 to U+00FF all map one-on-one with the latin-1 encoding

    先将 unicode 字符串编码为 latin1 字符串,编码后保留了等价的字节流数据。
    而此时在这个问题中,这一字节流数据又恰恰对应了 gbk 编码,因此对其进行解码即可还原最初的 unicode 字符。
    不过值得注意的是,需要确定的是形如\xd6\xb0 究竟是 utf8 编码还是类似 gbk 类型的其他编码,
    这一点对于最终正确还原 unicode 字符同样重要。

    综上所述,对拿到的 content 执行以下操作即可:
    content.encode("latin1").decode("gbk")

    以题主提到的 url 为例:
    >>> import requests
    >>> from lxml import etree
    >>> url = "http://jobs.51job.com/shenzhen-baq/81498324.html?s=0"
    >>> resp = requests.get(url)
    >>> tree = etree.HTML(resp.text)
    >>> x = tree.xpath("//div[@class='bmsg job_msg inbox']")
    >>> content = "".join(x[0].xpath("string()").split())
    >>> content
    u'\xd6\xb0\xce\xbb\xc3\xe8\xca\xf6\xa3\xba\xb8\xda\xce\xbb\xd6\xb0\xd4\xf0\xa3\xba1\xa1\xa2\xb8\xba\xd4\xf0\xb9\xab\xcb\xbe\xd2\xbb\xb0\xe3\xc4\xc9\xcb\xb0\xc8\xcb\xb5\xc4\xc8\xab\xc5\xcc\xd5\xcb\xce\xf1\xb4\xa6\xc0\xed\xa3\xbb2\xa1\xa2\xb6\xc0\xc1\xa2\xcd\xea\xb3\xc9\xb9\xfa\xcb\xb0\xa1\xa2\xb5\xd8\xcb\xb0\xb5\xc4\xc9\xea\xb1\xa8\xa3\xac\xc9\xf3\xba\xcb\xb3\xf6\xbf\xda\xcd\xcb\xcb\xb0\xb1\xa8\xb9\xd8\xd7\xca\xc1\xcf\xb2\xa2\xc9\xea\xb1\xa8\xcd\xcb\xcb\xb0\xa3\xac\xbd\xf8\xd0\xd0\xc4\xea\xb6\xc8\xcb\xb0\xce\xf1\xb3\xef\xbb\xae\xb5\xc8\xa3\xbb3\xa1\xa2\xb0\xb4\xca\xb1\xb1\xe0\xd6\xc6\xb2\xc6\xce\xf1\xb1\xa8\xb1\xed\xa3\xac\xbd\xf8\xd0\xd0\xba\xcf\xc0\xed\xb5\xc4\xbe\xad\xd3\xaa\xb7\xd6\xce\xf6\xa3\xbb4\xa1\xa2\xc8\xab\xc3\xe6\xb9\xdc\xbf\xd8\xb2\xc6\xce\xf1\xb9\xa4\xd7\xf7\xa3\xac\xcd\xea\xc9\xc6\xb2\xc6\xce\xf1\xd6\xc6\xb6\xc8\xa3\xac\xd0\xad\xb5\xf7\xb4\xa6\xc0\xed\xd3\xeb\xb9\xa4\xc9\xcc\xa1\xa2\xcb\xb0\xce\xf1\xb2\xbf\xc3\xc5\xb5\xc4\xd2\xbb\xc7\xd0\xca\xc2\xcf\xee\xa3\xac\xb4\xb4\xd4\xec\xc1\xbc\xba\xc3\xb5\xc4\xb2\xc6\xce\xf1\xbb\xb7\xbe\xb3\xa3\xbb5\xa1\xa2\xc4\xda\xb2\xbf\xc8\xd5\xb3\xa3\xc1\xf7\xcb\xae\xd5\xcb\xa1\xa2\xd3\xa6\xb8\xb6\xd5\xcb\xbf\xee\xb5\xc4\xc9\xf3\xba\xcb\xd3\xeb\xbc\xe0\xb9\xdc\xa3\xac\xb5\xa5\xbe\xdd\xb5\xc4\xb1\xa3\xb9\xdc\xa3\xbb6\xa1\xa2\xc1\xed\xcd\xe2\xc1\xbd\xbc\xd2\xd0\xa1\xb9\xe6\xc4\xa3\xb9\xab\xcb\xbe\xb5\xc4\xd5\xcb\xce\xf1\xb4\xa6\xc0\xed\xb9\xa4\xd7\xf7\xa1\xa3\xc8\xce\xd6\xb0\xd2\xaa\xc7\xf3\xa3\xba1\xa1\xa2\xbb\xe1\xbc\xc6\xcf\xe0\xb9\xd8\xd7\xa8\xd2\xb5\xa3\xac\xb4\xf3\xd7\xa8\xd2\xd4\xc9\xcf\xd1\xa7\xc0\xfa\xa3\xac\xd3\xd0\xbb\xe1\xbc\xc6\xd6\xa4\xa3\xac\xd6\xd0\xbc\xb6\xbb\xe1\xbc\xc6\xca\xa6\xd3\xc5\xcf\xc8\xbf\xbc\xc2\xc7\xa3\xbb2\xa1\xa2\xc8\xfd\xc4\xea\xd2\xd4\xc9\xcf\xc9\xfa\xb2\xfa\xd0\xcd\xc6\xf3\xd2\xb5\xc8\xab\xc5\xcc\xbb\xe1\xbc\xc6\xb9\xa4\xd7\xf7\xbe\xad\xd1\xe9\xa3\xac\xc4\xdc\xb6\xc0\xc1\xa2\xb4\xa6\xc0\xed\xd2\xbb\xb0\xe3\xc4\xc9\xcb\xb0\xc8\xcb\xc8\xab\xc5\xcc\xd5\xcb\xce\xf1\xa3\xbb3\xa1\xa2\xd3\xd0\xc3\xe2\xb5\xd6\xcd\xcb\xcb\xb0\xb9\xa4\xd7\xf7\xbe\xad\xd1\xe9\xa3\xac\xca\xec\xcf\xa4\xbd\xf8\xb3\xf6\xbf\xda\xd2\xbb\xb0\xe3\xb2\xd9\xd7\xf7\xc1\xf7\xb3\xcc\xa3\xbb4\xa1\xa2\xca\xec\xc1\xb7\xd3\xa6\xd3\xc3\xbd\xf0\xb5\xfb\xbb\xf2\xd3\xc3\xd3\xd1\xb5\xc8\xb2\xc6\xce\xf1\xc8\xed\xbc\xfe\xbc\xb0Office\xb0\xec\xb9\xab\xc8\xed\xbc\xfe\xa3\xbb5\xa1\xa2\xca\xec\xcf\xa4\xcf\xe0\xb9\xd8\xcb\xb0\xb7\xa8\xa1\xa2\xb7\xa8\xc2\xc9\xbc\xb0\xcf\xd6\xd0\xd0\xb2\xc6\xce\xf1\xd6\xc6\xb6\xc8\xa3\xac\xd3\xd0\xc1\xbc\xba\xc3\xb5\xc4\xd6\xb0\xd2\xb5\xb2\xd9\xca\xd8\xa3\xbb6\xa1\xa2\xb9\xa4\xd7\xf7\xbb\xfd\xbc\xab\xd6\xf7\xb6\xaf\xa3\xac\xd1\xcf\xbd\xf7\xcf\xb8\xd6\xc2\xa3\xac\xd6\xb4\xd0\xd0\xc4\xdc\xc1\xa6\xc7\xbf\xa3\xac\xb1\xa3\xc3\xdc\xd2\xe2\xca\xb6\xc7\xbf\xa3\xac\xd3\xd0\xc1\xbc\xba\xc3\xb5\xc4\xb9\xb5\xcd\xa8\xc4\xdc\xc1\xa6\xba\xcd\xbd\xe2\xbe\xf6\xce\xca\xcc\xe2\xc4\xdc\xc1\xa6\xa1\xa3\xb9\xab\xcb\xbe\xd6\xc6\xb6\xc8\xa3\xba1\xa1\xa2\xc9\xcf\xb0\xe0\xca\xb1\xbc\xe4\xa3\xba08:30-12:00\xa3\xac13:30-17:30\xa3\xac\xc3\xbf\xd6\xdc\xce\xe5\xcc\xec\xb0\xeb\xb9\xa4\xd7\xf7\xd6\xc6\xa3\xbb2\xa1\xa2\xc8\xeb\xd6\xb0\xbc\xb4\xb9\xba\xc2\xf2\xce\xe5\xcf\xd5\xd2\xbb\xbd\xf0\xbc\xb0\xc9\xcc\xd2\xb5\xd2\xe2\xcd\xe2\xcf\xd5\xa3\xbb3\xa1\xa2\xcf\xed\xd3\xd0\xb7\xa8\xb6\xa8\xbd\xda\xbc\xd9\xc8\xd5\xa1\xa2\xb4\xf8\xd0\xbd\xc4\xea\xbc\xd9\xa1\xa2\xbd\xda\xc8\xd5\xb8\xa3\xc0\xfb\xbc\xb0\xc4\xea\xd6\xd5\xbd\xb1\xbd\xf0\xb5\xc8\xa1\xa3\xd6\xb0\xc4\xdc\xc0\xe0\xb1\xf0\xa3\xba\xbb\xe1\xbc\xc6\xbe\xd9\xb1\xa8\xb7\xd6\xcf\xed'
    >>> content = content.encode("latin1").decode("gbk")
    >>> print content
    职位描述:岗位职责: 1 、负责公司一般纳税人的全盘账务处理; 2 、独立完成国税、地税的申报,审核出口退税报关资料并申报退税,进行年度税务筹划等; 3 、按时编制财务报表,进行合理的经营分析; 4 、全面管控财务工作,完善财务制度,协调处理与工商、税务部门的一切事项,创造良好的财务环境; 5 、内部日常流水账、应付账款的审核与监管,单据的保管; 6 、另外两家小规模公司的账务处理工作。任职要求: 1 、会计相关专业,大专以上学历,有会计证,中级会计师优先考虑; 2 、三年以上生产型企业全盘会计工作经验,能独立处理一般纳税人全盘账务; 3 、有免抵退税工作经验,熟悉进出口一般操作流程; 4 、熟练应用金蝶或用友等财务软件及 Office 办公软件; 5 、熟悉相关税法、法律及现行财务制度,有良好的职业操守; 6 、工作积极主动,严谨细致,执行能力强,保密意识强,有良好的沟通能力和解决问题能力。公司制度: 1 、上班时间: 08:30-12:00 , 13:30-17:30 ,每周五天半工作制; 2 、入职即购买五险一金及商业意外险; 3 、享有法定节假日、带薪年假、节日福利及年终奖金等。职能类别:会计举报分享
    mingyun
        4
    mingyun  
       2016-09-17 23:08:14 +08:00
    @Magic347 你好,我想运行你的代码,在 windows 下 pip install lxml ,但是失败了
    copying src\lxml\isoschematron\resources\xsl\iso-schematron-xslt1\readme.txt
    -> build\lib.win32-3.5\lxml\isoschematron\resources\xsl\iso-schematron-xslt1
    running build_ext
    building 'lxml.etree' extension
    error: Unable to find vcvarsall.bat

    ----------------------------------------
    Command "d:\python3\python.exe -u -c "import setuptools, tokenize;__file__='C:\\
    Users\\ADMINI~1\\AppData\\Local\\Temp\\pip-build-54urj6qv\\lxml\\setup.py';exec(
    compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'),
    __file__, 'exec'))" install --record C:\Users\ADMINI~1\AppData\Local\Temp\pip-55
    b_8t91-record\install-record.txt --single-version-externally-managed --compile"
    failed with error code 1 in C:\Users\ADMINI~1\AppData\Local\Temp\pip-build-54urj
    6qv\lxml\
    Arthur2e5
        5
    Arthur2e5  
       2016-09-29 13:34:41 +08:00   ❤️ 1
    @Magic347 其实是因为 requests 没有调 encoding 直接取 text ,自动当作 ISO-8859-1 回落解码了:

    >>> import requests
    >>> resp = requests.get("http://jobs.51job.com/shenzhen-baq/81498324.html?s=0" )
    >>> resp.encoding
    'ISO-8859-1'

    这时候取内容会逐 byte 处理,按照你提到的 U+00 ~ U+FF 和 latin1 一一对应的方式处理得到了一个 *错误的* resp.text 字符串:

    <class 'str'>

    接下来你直接把这锅浆糊送进 lxml 是在碰运气作死。之所以说碰运气,是因为 GBK 第二范围有 [40, 7F) ∪ [80, FF), 直接按照字节处理的话前半在 ASCII 内有对应的部分搞不好会和别的东西撞。比如说 CDATA 用 ]]> 结尾,而“昡”这个字又是编码成 b'\x95\x50',原文出现“昡]>”的时候就会错误认定 CDATA 结束。这是一个安全问题。

    要正确解决的话很简单,就是先把 resp.encoding 设置成正确的编码名,这样取 resp.text 才会按照正确的解码方式得到正确的字符串。

    @mingyun lxml 只是拿来解析得到的数据而已,和你解码文字的过程并无太大关系。
    Magic347
        6
    Magic347  
       2016-09-29 16:49:38 +08:00
    @Arthur2e5 确实没有找到问题的根源,主要还是对 requests 的 encoding 用法不太了解。感谢指正。
    frmongo
        7
    frmongo  
       2018-08-21 17:36:37 +08:00
    学到了,受益匪浅
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1077 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 22:38 · PVG 06:38 · LAX 14:38 · JFK 17:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.