小白学爬虫:彻底解决乱码问题(八)

摘要:从零开始写爬虫,初学者的速成指南!

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

字符编码简介

什么是字符集

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

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

什么是字符编码

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

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

常用字符集

简单介绍几个常见的。

ASCII:

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


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

号:923414804 群里有志同道合的小伙伴,互帮互助, 群里有视频学习教程和PDF!


中文字符集:

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中。乱码产生的过程如下:

OkHttp请求指定url,返回了一个GBK编码的网页字节流;

OkHttp以默认UTF-8进行解码(此时已乱),并以UTF-16方式编码为Java的String类型,返回给处理程序。(为什么以UTF-16方式编码?因为Java的数据在内存中的编码是UTF-16);

爬虫拿到这个编码错误的String类型的网页,调用MongoDB的API,将数据编码为UTF-8存储到数据库中。所以最后在数据库看到的数据是乱的。


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


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

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

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

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

charset=UTF-8“/>

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

语言URL后缀URLHTTP头中包含

charset的URL数

Chinese.cn100863776

English.us/.uk2156513223

Russian.ru3945328257

Japanese.jp203396833

Arabic.iq19041093

German.de3531823225

Persian.ir73964018

Indian.in122364867

Totalall14829785292

结果表明我们不能被动的依赖网页告诉我们,而要根据网页内容来主动探测其编码类型。


探测字符编码

什么是字符编码自动检测?

它是指当面对一串不知道编码信息的字节流的时候,尝试着确定一种编码方式以使我们能够读懂其中的文本内容。它就像我们没有解密钥匙的时候,尝试破解出编码。

那不是不可能的吗?

通常来说,是的,不可能。但是,有一些编码方式为特定的语言做了优化,而语言并非随机存在的。有一些字符序列在某种语言中总是会出现,而其他一些序列对该语言来说则毫无意义。一个熟练掌握英语的人翻开报纸,然后发现“txzqJv 2!dasd0a QqdKjvz”这样一些序列,他会马上意识到这不是英语(即使它完全由英语中的字母组成)。通过研究许多具有“代表性(typical)”的文本,计算机算法可以模拟人的这种对语言的感知,并且对一段文本的语言做出启发性的猜测。换句话说就是,检测编码信息就是检测语言的类型,并辅之一些额外信息,比如每种语言通常会使用哪些编码方式。

这样的算法存在吗?

结果证明,是的,它存在。所有主流的浏览器都有字符编码自动检测的功能,因为互联网上总是充斥着大量缺乏编码信息的页面。Mozilla Firefox包含有一个自动检测字符编码的库,已经移植到Python中,叫做chardet。

chardet使用

安装:

pip install chardet

使用:

>>> import urllib

>>> rawdata = urllib.urlopen('http://www.jd.com/').read()

>>> import chardet

>>> chardet.detect(rawdata)

{'confidence': 0.98999999999999999, 'language': '', 'encoding': 'utf-8'}

注意:返回结果中有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亿级别数据量的大型爬虫上。


下一步

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容