py 爬中文网页,总是遭遇 UnicodeEncodeError - V2EX
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
KyL
V2EX    Python

py 爬中文网页,总是遭遇 UnicodeEncodeError

  •  
  •   KyL 2015-01-17 17:12:01 +08:00 6703 次点击
    这是一个创建于 3921 天前的主题,其中的信息可能已经有所发展或是发生改变。

    觉得《码农周刊》挺有意思的,想写个脚本把内容抓下来。

    import urllib import httplib def get_html_content(url): respOnse= urllib.urlopen(url) html = response.read() print type(html) return html if __name__ == '__main__': url = 'http://weekly.manong.io/issues/58' html = get_html_content(url) print html.decode('utf-8') 

    结果报错误

    <type 'str'> Traceback (most recent call last): File "E:\src\infra.py", line 32, in <module> print html.decode('utf-8') UnicodeEncodeError: 'ascii' codec can't encode character u'\u201c' in position 44: ordinal not in range(128) [Finished in 1.6s] 

    我真的查了许多,像是http://nedbatchelder.com/text/unipain.html 通读一遍,结果怎么都搞不定,求解。

    第 1 条附言    2015-01-18 14:46:20 +08:00
    应该附上我的工作环境的。
    Windows 7 64bit
    Python2.7.9
    Sublime Text Ctrl + B 调试
    24 条回复    2015-02-01 22:05:14 +08:00
    ihciah
        1
    ihciah  
       2015-01-17 17:23:12 +08:00 via Android
    我这里正常…不过是用的urllib2.urlopen
    KyL
        2
    KyL  
    OP
       2015-01-17 17:29:03 +08:00
    @ihciah 你是PY2还是PY3?
    liqiu
        3
    liqiu &nsp;
       2015-01-17 17:34:29 +08:00
    aaaa007cn
        4
    aaaa007cn  
       2015-01-17 19:16:11 +08:00
    如果只考虑这个场合
    print html.decode('utf-8').encode('gb18030')
    Delbert
        5
    Delbert  
       2015-01-17 19:25:26 +08:00
    .decode("utf-8", "ignore")
    这样试试呢?
    9hills
        6
    9hills  
       2015-01-17 19:39:44 +08:00   2
    程序没问题,出问题的是你的环境。直接print Unicode的话,如果在Linux or OSX的默认UTF-8环境,是没有问题的。但是在Windows上就呵呵(4楼是windows上的解决办法)

    两点:
    1. 不要学3楼的reload sys哈,不是解决问题的办法。你reload前你懂它到底做了什么么,不懂的话就别写自己不懂的代码,如果你懂的话,那也不需要这个reload的办法
    2. 直接输出Unicode是不好的行为,输出的时候应该encode下的,环境是utf-8就encode成utf-8,环境是gbk就encode成gbk
    9hills
        7
    9hills  
       2015-01-17 19:42:12 +08:00   1
    处理编码的主要一个原则,就是让程序的默认隐式转换变成显式的。

    比如所有中文都写成 u"中文" 这样的Unicode,而不是让它的编码随文件编码变化而变化
    比如输出的时候显式转换编码,而不是依赖系统的编码转换。。
    pyKun
        8
    pyKun  
       2015-01-17 21:02:33 +08:00
    @9hills

    处理编码的主要一个原则,就是让程序的默认隐式转换变成显式的。

    +1
    lerry
        9
    lerry  
       2015-01-17 21:39:23 +08:00
    @KyL
    我理解的,输出之前encode,处理之前decode,

    另外,这样写,print的时候不会出错
    print u"你妹".encode(sys.stdout.encoding)
    lyhapple
        10
    lyhapple  
       2015-01-17 21:40:09 +08:00
    用python的requests库试试.
    pip install requests
    9hills
        11
    9hills  
       2015-01-17 21:43:20 +08:00
    @lerry 恩,这样适合写通用的CLI系统,可以兼容多种编码环境
    icedx
        12
    icedx  
       2015-01-17 21:47:12 +08:00
    print html.decode('utf8').encode('gbk','backslashreplace')
    因为楼主用的是Windows
    endoffight
        13
    endoffight  
       2015-01-17 22:12:02 +08:00 via Android
    建议安装chardect模块确定编码后再决定转不转码
    EPr2hh6LADQWqRVH
        14
    EPr2hh6LADQWqRVH  
       2015-01-17 22:15:34 +08:00
    不要assume数据的编码,拿到手保持Bytes的形态,然后自己尝试解码
    Sylv
        15
    Sylv  
       2015-01-18 13:44:25 +08:00
    你这个问题其实是和 Sublime Text 有关的,我刚好在总结一篇相关的 Unicode 编码问题的分析,写好会分享出来。

    而针对你这个问题的短答案是:
    你在最后将 str 类型的 html decode 为了 unicode 类型的字符串,然后想要打印 print 出这个 unicode 字符串时,Python 又需要将其转换为 str 后才能输出。在正常情况下,Python 能判断出正确的输出编码来进行转换而并不会出错,这就是前面有人说在他们的环境能正常运行的原因。而你会出现 UnicodeEncodeError 的原因是,因为某种原因,在 Sublime Text 里运行的 Python 不能判断出这个正确的输出编码,于是它就使用了它的默认编码 ascii 来进行转换,于是就出错了。
    在 Sublime Text 里想要 print 出一个 unicode 类型的字符串,正确的方法是显式地进行 encode('utf-8') 后再 print。而为什么我说要用的是 utf-8 编码,而不是前面说的 gbk 等编码呢?因为默认配置下的 Sublime Text 所接收的默认输出编码是 utf-8,你用其它编码会出现 [Decode error - output not utf-8] 错误。而如果你用 cmd 运行的话,可能就要用 gbk 等编码了。
    而针对这种情况下的你这段代码,其实最后没必要 .decode('utf-8') 一遍,因为 html 原本就是 utf-8 编码的 str 了,可以直接 print html 了,否则你需要多此一举 print html.decode('utf-8').encode('uff-8')。

    关于这个问题的详细解释和其它解决方法,请等我的长答案。
    KyL
        16
    KyL  
    OP
       2015-01-18 14:44:29 +08:00
    @Sylv 我确实是在用Sublime Text调试Py2的时候遇到这个问题的。str只可以hold1Byte字符的串,怎么会有所谓utf-8编码的str呢。期待着你的长答案。
    Sylv
        17
    Sylv  
       2015-01-18 15:16:23 +08:00
    @KyL str 存储的是 bytes 字节序列,不是只有 1 byte。它当然就有不同的编码,见下例,同样表示的是 “中文” 的 str 字符串,在不同编码下,其内部存储的字节序列是不同的:
    >>> text = u'中文'
    >>> text.encode('utf-8')
    '\xe4\xb8\xad\xe6\x96\x87'
    >>> text.encode('gbk')
    '\xd6\xd0\xce\xc4'
    KyL
        18
    KyL  
    OP
       2015-01-18 16:33:41 +08:00
    @Sylv py2中有两种字符串类型,str和unicode。 text = u'中文' 是Unicode类型,不是str类型。str只能储存ascii类型的字符吧。
    Sylv
        19
    Sylv  
       2015-01-19 04:10:37 +08:00
    @KyL 你概念理解有错

    首先,Python 2 unicode 和 str 转换方法要弄清楚:
    str.decode('utf-8') -> unicode
    unicode.encode('utf-8') -> str

    我上例中 text.encode('utf-8') 后已经是 str 了。

    然后你理解错了,str 并不是只能存储 ascii 类型的字符。str 存储的是一个一个的字节数据,也可以说就是一个一个字节的数字。然后这些数字代表的是什么字符,要看你用什么编码去解读它。

    例如现在一个 str 用一个字节存储了数字 97,那么你用 ascii 编码去解读它,那么这个 str 就是 'a'。

    然后现在有一个 str 用三个字节存储了三个数字:235、184 和 173,连起来用 16 进制表示也就是 '\xe4\xb8\xad'。然后你也可以用 ascii 编码去解读它,查 ascii 表后可以发现这三个数字都不在基本的 128 位 ascii 里,而是在扩展表里,都是一些很奇怪的字符,可见这个 str 用 ascii 编码去解读它对我们来说没有意义。
    但是我们换一个编码 'utf-8' 去解读它,这个 str 就变得有意义了,在 utf-8 编码里是用三个字节来存储一个汉字字符的,而不是像 ascii 编码一个字节就对应了一个字符。那么 235、184 和 173 这三个字节的数字在 utf-8 编码里对应的就是一个汉字字符的 “中”。你可以用以下方法验证:
    >>> char = u'中'
    >>> print type(char)
    <type 'unicode'>
    >>> char = char.encode('utf-8')
    >>> print type(char)
    <type 'str'>
    >>> print repr(char) # repr 方法可以将对象在 Python 内部的存储形式表现出来
    '\xe4\xb8\xad'
    >>> print char

    >>> print '\xe4\xb8\xad'


    在 print 这个 str 类型的 char 时,Python 只是把那三个数字直接发送给了用来显示的控制台(console)。console 就是用来输出的地方,例如 Sublime Text 的运行结果窗口,还有 Windows 下的 cmd。
    然后决定用什么编码去解读它,是由 console 来决定的。在 Sublime Text 下这个编码默认是 utf-8,它用 utf-8 去解读 235、184 和 173 这三个数字,发现是 “中” 字,那么它就会去字库里找出 “中” 字这个字形给我们显示出来,因此我们就能看到 “中” 了。
    而在 cmd 下,它用来解读的编码就不是 utf-8 了,而是 gbk 之类的。那它用 gbk 编码去解读这三个数字,可能它能找到另一些对应的字符,也有可能它完全找不到对应的字符,这时就产生了显示出乱码的情况。如果你想让它显示出 “中”,那么你就要让 Python 发送给它 gbk 编码下 “中” 所对应的数字,也就是 214 和 208,写成 16进制就是 '\xd6\xd0'。

    因此你在 print 的时候想要显示正常,要看你现在的输出 console 用的是什么编码,然后就要发送给它对应编码的 str。
    我最开始所说 html 是 utf-8 编码的 str,意思就是 html 里存储的字节序列,就是你想要的网页源码文字在 utf-8 编码下对应的一个个的数字,所以我们可以说它是 utf-8 编码的 str,因为它在 ascii 和 gbk 等编码下是没有意义的。Python 将它送给 Sublime Text, Sublime Text 也用 utf-8 编码去解读它,最后就能显示出你能看得懂的网页源代码文字。
    Sylv
        20
    Sylv  
       2015-01-20 12:44:26 +08:00
    @KyL 之前说的长答案 /t/163786
    KyL
        21
    KyL  
    OP
       2015-01-20 17:40:08 +08:00
    @Sylv 多谢
    KyL
        22
    KyL  
    OP
       2015-02-01 18:14:14 +08:00
    @aaaa007cn 为什么在cmd中使用gb18030是正确的呢。sys.stdout.encoding得到的结果是cp936,可是html.decode('utf-8').encode('cp936')报错。
    aaaa007cn
        23
    aaaa007cn  
       2015-02-01 18:47:21 +08:00
    https://en.wikipedia.org/wiki/Code_page_936
    https://en.wikipedia.org/wiki/GB_2312
    https://en.wikipedia.org/wiki/GBK
    https://en.wikipedia.org/wiki/GB_18030
    https://docs.python.org/2/library/codecs.html

    收录 6763 个汉字
    gbk 收录 20900 多汉字
    gb18030 收录 70244 个汉字
    python2 中 cp936 是 gbk 的别名
    所以有时候 encode("cp936") 会出错
    一般在中文 windows 的 cmd 中用 python2 输出中文都是 encode("gb18030") 的
    KyL
        24
    KyL  
    OP
       2015-02-01 22:05:14 +08:00
    @aaaa007cn 谢谢
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5532 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 08:58 PVG 16:58 LAX 01:58 JFK 04:58
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86