一. chardet是什么

chardet是python中比较常用的一个编码方式检测库,需要注意的是它只检测并返回检测结果,并不负责对原数据做什么处理。

可以使用PIP命令安装:

pip install chardet

二. 如何使用

1.API简介

一般都是调用chardet.detect传入一个字节数组,返回一个字典,此字典中存放分析的结果,一个可能的分析结果字典:

{
  'encoding': 'ISO-8859-1',
  'confidence': 0.44923076923076927,
  'language': ''
}
  • encoding: 可能的编码方式
  • condidence: 识别的正确率是多少,这是一个区间[0, 1]上的值,值越大表示结果越可信
  • language: 字节码中存放的可能会是什么语言
2.读取配置文件时自动识别编码方式

只要是一个灵活的软件都会提供配置文件来让用户根据自己需要进行定制,但是我们没法保证用户究竟是使用什么鬼东西来编辑这个配置文件的,vim?notepad?editplus?奇奇怪怪的文本编辑工具一大堆,如果强制配置文件只能使用UTF-8格式的甚至还可能会碰上notepad的bom,要了命了,所以一个比较好的方式是在读取配置文件的时候能够自动检测它的编码方式,这样就无需关心用户究竟是使用什么鬼东西编辑的。一个可能的例子如下:

import chardet
import json
 
 
class EncodingUtil:
    """
    编码工具类
    """
    @staticmethod
    def decode(content):
        """
        读取字节数组为字符串
        :param content: a byte array
        :return:
        """
        encoding = chardet.detect(content)['encoding']
        return content.decode(encoding)
 
 
class ConfigurationLoader:
    """
    配置文件加载类
    """
    @staticmethod
    def load_config(config_path):
        with open(config_path, 'rb') as config_file:
            config_content = EncodingUtil.decode(config_file.read())
            return json.loads(config_content)
 
 
if __name__ == '__main__':
    print(ConfigurationLoader.load_config('D:/config.json'))
3.爬虫中用来判别网页的编码方式

呃,貌似现在python已经成为爬虫的代名词,正常的套路网站内容都是按照UTF-8编码返回的,但是总有一些非主流站,会以奇奇怪怪的编码返回(根据笔者的经验,国内的网站一般也就是UTF-8和GB2312的,GB2312估计是因为很多编写网页的开发工具默认是GB2312的,写网页的家伙没改然后就这样子了呗),如果是有针对性的爬虫(时髦的说法叫做垂直领域爬虫),大不了我们失败几次之后专门做下编码格式转换就可以了,毕竟写爬虫基本就是不断处理异常情况,但是如果写的是通用爬虫,我们甚至都不知道爬虫会去哪里抓取内容更别提内容的编码方式了,所以在这种情况下在处理内容之前要进行一个编码转换,将其转换为宇宙通用的UTF-8格式,一个简单的例子如下:

import chardet
import urllib3
 
 
class Spider:
    """
    蜘蛛侠,爬爬爬
    """
    pool_manager = urllib3.PoolManager()
 
    @staticmethod
    def get(url):
        return Spider.pool_manager.urlopen('GET', url)
 
 
class EncodingUtil:
    """
    编码工具类
    """
    @staticmethod
    def decode(content):
        """
        读取字节数组为字符串
        :param content: a byte array
        :return:
        """
        encoding = chardet.detect(content)['encoding']
        return content.decode(encoding)
 
 
if __name__ == '__main__':
    # UTF8格式编码的
    response = Spider.get('http://www.baidu.com/')
    html = EncodingUtil.decode(response.data)
    print(html)
 
    # GB2312格式编码的,找了好久才找到一个GB2312编码的...
    response = Spider.get('http://www.hzsjwjcj.gov.cn/')
    html = EncodingUtil.decode(response.data)
    print(html)

当正确读取到内容之后就可以传给bs处理啦,chardet在爬虫中的应用大致就是这样子。

4.检测大文件

检测编码方式是需要一定的数据量作为参考的,当要检测一个特别大的文件的时候,比如一个几个G大的文件,没必要全部输入太浪费了,但是究竟给它多少数据做检测比较合适呢,1M?10M?100M?这个还真不知道…

所以这个时候我们就可能需要它提供一种方式,我们一点一点的把数据喂给它,当能够确定编码方式的时候就立即返回。哈,它确实提供了这么一种方式:

import chardet
from chardet.universaldetector import UniversalDetector
 
 
class EncodeUtil:
    """
    编码工具类
    """
    @staticmethod
    def detect_big_file(file_path, block_size=1000):
        """
        用于检测大文件的编码方式
        :param file_path:  str,要检测文件的路径
        :param block_size: 每次读取的块大小
        :return:
        """
        detector = UniversalDetector()
        with open(file_path, 'rb') as big_file:
            block = big_file.read(block_size)
            while block and not detector.done:
                detector.feed(block)
                block = big_file.read(block_size)
        detector.close()
        return detector.result
 
#Python小白学习交流群:711312441 
if __name__ == '__main__':
    print(EncodeUtil.detect_big_file('D:/foo.txt', 1024))

网上流传的版本都是每次读取一行,但是这种方式对于整个大文件只有一行或者是每一行都巨长的情况下直接就歇菜了,所以一个比较好的方式是将每次能够读取的长度把握一下。

通过上面的实验我们也可以总结出来,检测的数据量越少结果就越可能产生偏差,输入的数据量越大结果就可能越正确,所以也明确了chardet的应用场景,当只有几个字节的时候就别指望chardet能给出多正确的结果了。

3. 需要注意的一些坑

如果看到检测的返回值是这个样子的:

{
  'encoding': 'ascii',
  'confidence': 1.0,
  'language': ''
}

不要急着感叹于chardet的牛叉竟然可以100%的确定是ASCII编码,这个八成就是没有检测出来,因为chardet.detect方法的默认返回值就是这个,贴源代码有图有真相:

# Default to ASCII if it is all we've seen so far
elif self._input_state == InputState.PURE_ASCII:
    self.result = {'encoding': 'ascii',
                   'confidence': 1.0,
                   'language': ''}