zoukankan      html  css  js  c++  java
  • 小白学爬虫:彻底解决乱码问题(八)

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

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

    字符编码简介

    什么是字符集

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

    字符(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后缀URL数HTTP头中包含

    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亿级别数据量的大型爬虫上。

    下一步

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

  • 相关阅读:
    ios 分组列表头部空白处理
    滑动cell 显示的按钮 类型分别是 删除 置顶 其他
    获取View所在的控制器
    MySQL联表查询
    Yii的常用URL和渲染方法
    Ubuntu下为Firefox安装Adobe Flash Player
    ubuntu下的nginx+php+mysql配置
    ubuntu下配置nginx+php
    如何使用PHP操作cookie
    ubuntu broadcom无线驱动安装
  • 原文地址:https://www.cnblogs.com/paisenpython/p/10308402.html
Copyright © 2011-2022 走看看