[常用] 在Python中检测网页编码
在使用Python抓取网页并进行分析时出现这个错误:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xd6
原因是部分中文网站编码不是utf8
, 因此需要进行编码判断
问题描述:
在引入编码自动识别前, 我们有两种途径获取网页的编码信息:
其一、通过服务器返回的 header 里的 charset 变量获取
其二、通过页面里的 meta 信息获取
正常情况下, 如果服务器或者页面有提供这两个参数, 而且参数是正确的, 那我们抓取网页时就不存在编码的问题了.
但是现实总是会难为我们这些程序员, 抓取网页时, 经常会出现以下几种情况:
- 这两个参数缺失了
- 这两个参数虽然都提供了,但是不一致
- 这两个参数提供了,但是与网页实际的编码不一致
为了尽可能的自动的获取所有网页的编码,所以引入了编码自动识别
Chardet
搜索chardet马上就找到了Python的chardet第三方库
可以通过pip安装
pip install chardet
主页描述:
chardet guesses the encoding of text files.
Detects...
- ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants)
- Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese)
- EUC-JP, SHIFT_JIS, ISO-2022-JP (Japanese)
- EUC-KR, ISO-2022-KR (Korean)
- KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic)
- ISO-8859-2, windows-1250 (Hungarian)
- ISO-8859-5, windows-1251 (Bulgarian)
- windows-1252 (English)
- ISO-8859-7, windows-1253 (Greek)
- ISO-8859-8, windows-1255 (Visual and Logical Hebrew)
- TIS-620 (Thai)
Requires Python 2.1 or later.
我使用的是Python 2.7.X
网页编码判断:
import chardet
import urllib2
#可根据需要,选择不同的数据
html = urllib2.urlopen('http://www.zol.com.cn/').read()
print(chardet.detect(html))
运行结果:
{'confidence': 0.99, 'encoding': 'GB2312'}
chardet.detect()
返回字典, 其中confidence
是检测精确度, encoding
是编码形式
另一种方式:
import urllib2
from chardet.universaldetector import UniversalDetector
html = urllib2.urlopen('http://www.zol.com.cn/')
#创建一个检测对象
detector = UniversalDetector()
for line in html.readlines():
#分块进行测试,直到达到阈值
detector.feed(line)
if detector.done: break
#关闭检测对象
detector.close()
html.close()
#输出检测结果
print detector.result
运行结果:
{'confidence': 0.99, 'encoding': 'GB2312'}
如果要对一个大文件进行编码识别, 使用后一种方法, 可以只读一部分去判别编码方式, 从而提高检测速度.
BeautifulSoup4 UnicodeDammit
可以使用BeautifulSoup4的UnicodeDammit模块
from bs4 import UnicodeDammit
dammit = UnicodeDammit("Sacr\xc3\xa9 bleu!")
print(dammit.unicode_markup)
# Sacré bleu!
dammit.original_encoding
# 'utf-8'
总结
不管使用上面哪种模块, 都不能保证百分百正确...部分网站太吭爹了...
下面是我最后的写法, 结合两种方式, 之前测试出现乱码的网站都能检测出来了, 就是代码看着不是很干净: