Python获取网页编码


徐佳莹-失落沙洲

chrome_view_web_source.png

不同网页选择不同编码形式

我们将字符的集合成为字符集(char set),为了让计算机能存储、传输并显示字符,需要给字符集以编码。
由于历史的原因字符集的编码形式有多种。
如:ASCII码只能表示一些英文字符(7bit);GBK/GB2312用两个字节表示中文;UTF-16用2个或者4个字节表示一个字符;UTF-8用变长形式表示unicode,兼容ASCII,英文只需要1个字节表示,而中文则需用三个字节表示。
不同的网站可能会使用不同的编码字符集,如UTF-8虽然完美兼容ASCII,而且俨然是网页编码的主流选择,但是每个中文字符字节数是GBK/GB2312的1.5倍,网站传输过程中的流量也就是1.5倍,所以一些中文网站还是选用GBK/GB2312字符集。

怎么获取网页字符集

web server response header

对网页而言,第一种方法是web server在HTML page之前返回一个类似于Content-Type: text/html; charset=utf-8的http header,这个header作为response header返回而不是网页的一部分。
但是如果网站由很多网页组成,而且不同的网页由使用不同的语言的用户贡献上传,那么web server不知道每个文件的真实编码集。

Content-Type of HTML

第二种方法是在HTML文件中直接写入Content-Type来指定charset。
这里有个鸡生蛋蛋生鸡的问题,你怎么能在不知道HTML文件编码方式的情况下来读取文件呢?
幸运的是,基本上几乎所有的字符集从32到127的字符都是一样的,也就是兼容ASCII码。所以我们可以用ASCII来写HTML页面的tag来指定charset:

html header
1
2
3
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />

我们必须将这段tag放在HTML最开始的地方,因为只要浏览器读取到这个tag,会停止分析网页并用指定的编码集重新解析整个网页。

web brower guessing

如果浏览器没有找到Content-Type怎么办?
浏览器会基于不同语言常用字符集的高词频编码来猜测字符集。
当然猜测就会有猜错的几率,如果猜错了,我们可以手动改变浏览器的编码(如Chrome,在更多工具->编码里可以修改编码)。

Python获取网页编码

用三种不同的方法获取网页编码。

get_website_charsetview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#encoding=utf-8
import sys
import urllib2
import re
import zlib
#http://pypi.python.org/pypi/chardet
import chardet
from chardet.universaldetector import UniversalDetector
__author__ = 'wu_chenxu@126.com'
__version__ = '0.1'
urls = ['http://www.163.com.cn',
'http://www.sohu.com',
'http://www.qq.com',
'http://www.sina.com.cn',
'http://www.baidu.com',
'http://www.renren.com',
'http://www.iqiyi.com',
'http://www.taobao.com',
'http://www.jd.com',
'http://www.tweeter.com',
'http://www.gov.hk/',
'http://www.naver.com',
'http://www.rakuten.co.jp/']
def chardetect(html):
#create a new detector object
detector = UniversalDetector()
for line in html.split('\n'):
#test in blocks until reach to limit
detector.feed(line)
if detector.done: break
#close detector object
detector.close()
#output detect result
return detector.result
def get_charset(url):
request = urllib2.Request(url)
#request for gzip file
request.add_header('Accept-encoding', 'gzip')
try:
opener = urllib2.build_opener()
response = opener.open(request, timeout=10)
html = response.read()
print 'URL:', url
raw_headers = response.headers
#print raw_headers
#norm_header=dict(zip(map(lambda x:x.lower(), raw_headers.keys()),raw_headers.values()))
#print norm_header
#1.get the charset from response header
contentType = raw_headers.get('Content-Type')
matchObj = re.search('charset=(.*)', contentType)
print 'response header: ',
if matchObj:
print matchObj.group(1)
else:
print 'None'
#1.1 unzip the html if needed
gzipped = raw_headers.get('Content-Encoding')
if gzipped:
html = zlib.decompress(html, 16+zlib.MAX_WBITS)
#2. get charset from html page header
matchObj = re.search('charset=([^ \t\n\r\f\v/>]*)', html[0:1024])
print 'html header: ',
if matchObj:
print matchObj.group(1).replace('"','')
else:
print 'None'
#3. get charset by guessing the page content
print 'chardet analysis: '+chardetect(html[0:10*1024]).get('encoding')
#4. print the first 10 bytes of html
print ":".join("{:02x}".format(ord(c)) for c in html[0:20])
print " ".join( c for c in html[0:20])
#print convert2sysEncode(html[0:1024])
except Exception,e:
print url,':',Exception,":",e
def convert2sysEncode(data):
'''
convert the data content to system charset
'''
sysEncode = sys.getfilesystemencoding()
dataEncode = chardet.detect(data).get('encoding', 'utf-8')
dataConvert2sysEncode = data.decode(dataEncode, 'ignore').encode(sysEncode)
return dataConvert2sysEncode
if __name__ == '__main__':
print "system charset:", sys.getfilesystemencoding()
for url in urls:
get_charset(url)
print

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
system charset: mbcs
URL: http://www.163.com.cn
response header: UTF-8
html header: UTF-8
chardet analysis: UTF-8-SIG
ef:bb:bf:0a:3c:21:44:4f:43:54:59:50:45:20:68:74:6d:6c:3e:0a
???
< ! D O C T Y P E h t m l >
URL: http://www.sohu.com
response header: None
html header: GBK
chardet analysis: GB2312
3c:21:44:4f:43:54:59:50:45:20:68:74:6d:6c:20:50:55:42:4c:49
< ! D O C T Y P E h t m l P U B L I

可以看到结果有些站点的response是空值。
大部分网站采用的UTF-8编码,一些网站如qq、sohu、163、jd用的是GBK/GB2312,而日本乐天网站用的是EUC-JP编码。

网页字符集统计

通过Historical yearly trends in the usage of character encodings for websites这个网页可以看到UTF-8是网页编码无可争议的绝对主导地位。
在2016年1月23号,我们看到排在前列的编码字符集是:UTF-8(86.2%) ISO-8859-1(6.8%) Windows-1251(1.9%) Shift JIS(1.1%) Windows-1252(1.0%) GB2312(0.9%)…
website encoding trend

Reference:

  1. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
  2. 使用 python urllib2 抓取网页时出现乱码的解决方案
  3. 用Python抓网页的注意事项
  4. python 模块 chardet下载方法及介绍
  5. 国外网站大全