手把手教你写网络爬虫(8):彻底解决乱码问题

python admin 3900 0 评论

1368640-20180426083108287-1201983270

字符编解码是爬虫里必学的一项知识,在我们的爬虫生涯中早晚会爬到乱码的网页,与其遇到时惊慌失措,不如早学早好,彻底避免乱码问题。

字符编码简介

什么是字符集

在介绍字符编码之前,我们先了解下什么是字符集。

字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集:ASCII字符集、GBK字符集、Unicode字符集等。

什么是字符编码

字符编码和字符集不同。字符集只是字符的集合,无法进行网络传送、处理,必须经编码后才能使用。如Unicode字符集可依不同需求以UTF-8、UTF-16、UTF-32等方式编码。

字符编码就是以二进制的数字来对应字符集的字符。各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。

常用字符集

简单介绍几个常见的。

ASCII:

ASCII是学计算机同学的启蒙字符集,一般是从这本书里学到的:

1368640-20180427084222299-735009224

请允许我怀旧一下,以下引用谭浩强老师的讲解:

1368640-20180427084222299-735009224

中文字符集:

GB2312:包含6763个汉字。

GBK:包含21003个汉字。GBK兼容GB2312,也就是说用GB2312编码的汉字可以用GBK来解码。

GB18030:收录了70000个汉字,这么多是因为包含了少数民族文字。同样兼容GBK和GB2312。

Unicode:Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。具有多种编码方式,如UTF-7、 UTF-8、UTF-16、UTF-32等。

为什么会产生乱码

简单的说乱码的出现是因为:编码和解码时用了不同的字符集。对应到真实生活中,就好比是一个英国人为了表示祝福在纸上写了bless(编码)。而一个法国人拿到了这张纸,由于在法语中bless表示受伤的意思,所以认为他想表达的是受伤(解码)。同理,在计算机中,一个用UTF-8编码后的字符,用GBK去解码。由于两个字符集的字库表不一样,同一个汉字在两个字符表的位置也不同,最终就会出现乱码。

那么,爬虫中的乱码是怎么产生的,又该如何解决呢?

爬虫中的乱码

假设我们的爬虫是java开发的,网络请求库使用OkHttp,网页存储到MongoDB中。乱码产生的过程如下:

  1. OkHttp请求指定url,返回了一个GBK编码的网页字节流;
  2. OkHttp以默认UTF-8进行解码(此时已乱),并以UTF-16方式编码为Java的String类型,返回给处理程序。(为什么以UTF-16方式编码?因为Java的数据在内存中的编码是UTF-16);
  3. 爬虫拿到这个编码错误的String类型的网页,调用MongoDB的API,将数据编码为UTF-8存储到数据库中。所以最后在数据库看到的数据是乱的。

1368640-20180427084222299-735009224

显然,导致乱码的根本原因就是OkHttp在最初使用了错误的解码方式进行解码。所以要解决这个问题,就要让OkHttp知道网页的编码类型,进行正确的解码。

1368640-20180427084222299-735009224

网页有两种约定的方式告诉爬虫自己使用的是什么编码方式:

1. Http协议的响应头中的约定:

Content-Type: text/html;charset=utf-8

2. Html中meta标签中的约定:

<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8“/>

从约定中获取网页的编码后,Okhttp就可以正确的解码了。然而实际情况却并不乐观,很多网页并不遵守约定,缺少这两个信息。有人通过Alexa统计各国遵守这个约定的网页数:

使用:

注意:返回结果中有confidence,即置信度,这说明探测结果不是100%准确的。

使用其他语言的小伙伴不用担心,chardet在很多语言中都有移植版。不过C++好像没有太好的选择,可以考虑使用IBM的ICU(http://site.icu-project.org/)。

扩展阅读

《A composite approach to language/encoding detection》

(https://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html)

这篇论文解释了Chardet背后使用的探测算法,分别是“编码模式方法”、“字符分布方法”和“双字符序列分布方法”。最后说明了三种方法组合使用的必要性,并举例说明如何组合使用。

《Charset Encoding Detection of HTML Documents A Practical Experience》

(https://github.com/shabanali-faghani/IUST-HTMLCharDet/blob/master/wiki/Charset-Encoding-Detection-of-HTML-Documents.pdf)

利用现有的探测技术,通过一些技巧来提高探测的准确性。主要原理是组合使用Mozilla CharDet和IBM ICU,并在探测前巧妙的去掉了HTML标签。虽然这是伊朗大学发的Paper,但据说这种方法已经在生产环境取得了很好的效果,目前正应用在一个10亿级别数据量的大型爬虫上。

下一步

最近聊的话题越来越沉重,想必大家也累了。下期打算带大家一起放松一下,聊点轻松的话题。从系列的开篇到现在也有半年了,技术领域有了不小的更新,出现了一些好用的工具,我们需要替换哪些工具呢?请听下回分解!

1 收藏 评论

转载请注明: 飞嗨_分享互联网 » 手把手教你写网络爬虫(8):彻底解决乱码问题

赞 (0) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽

高效,专业,符合SEO

联系我们