求解汉字编码问题

2016-09-07 17:07:10 +08:00
 zl2003cn

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

19990 次点击
所在节点    Python
7 条回复
imn1
2016-09-07 17:16:23 +08:00
你这样说什么都不清楚
什么系统
在哪测试,命令行、 web 、 GUI 还是 IDE
测试环境的编码是什么
mgna17
2016-09-07 17:25:42 +08:00
试了一下, utf-8 解不出来, gbk 、 gb2312 、 gb18030 全都能解出来
Magic347
2016-09-07 17:34:40 +08:00
取到的网页文字内容在编码上存在一定的 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
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
2016-09-29 13:34:41 +08:00
@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
2016-09-29 16:49:38 +08:00
@Arthur2e5 确实没有找到问题的根源,主要还是对 requests 的 encoding 用法不太了解。感谢指正。
frmongo
2018-08-21 17:36:37 +08:00
学到了,受益匪浅

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/304608

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX