Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

关于爬虫字符集不匹配问题的解决办法

背景

由于世界上语言各异,进行信息爬取的过程中,难免遇到各种各样奇怪的字符,导致我们进行持久化时遇到困难。
今天在写爬虫时就遇到了一个’gbk’编码集无法解析的字符:’\u2022’
该字符使用正确编码时,在显示器上显示的应为然而我们先要知道该字符是来自于哪一字符集较为困难,那有没有什么直接使用原网页字符集的方法呢?

查看原网页字符集

查看原网站编码集的方法有很多,比如通过console输入:

1
document.charset  //'GBK'

或者直接查看网页的如下标签:

1
<meta http-equiv="Content-Type" content="text/html;charset=gbk">

然而这通常只能知道该网页是使用某种gbk编码编制的。让然不够准确,而且需要我么切到浏览器进行操作,这有可能打断我们编码的思路。

chardet模块

另一种更好的方法是使用python的chardet库。

chardet为我们提供了一种网络资源编码方式自动检测的方案。

该模块的作者实际上是将Mozilla的自动编码检测技术封装成了接口 the auto-detection code in Mozilla

对原算法感兴趣的话可以阅读如下论文A composite approach to language/encoding detection (mozilla.org)

detect()

chardet最常用的使用方式就是:

1
2
3
4
5
>>> import urllib.request
>>> rawdata = urllib.request.urlopen('http://yahoo.co.jp/').read()
>>> import chardet
>>> chardet.detect(rawdata)
{'encoding': 'EUC-JP', 'confidence': 0.99}

这样就能直接获取某一整篇文本的编码方式。

该方法接收一个non-Unicode字符串作为参数,并且返回该字符串最有可能使用的字符集,以及推测使用该字符集的概率confidence,该值是一个0-1之间的数。

UniversalDetector

而当我们面对一个非常长的文本时,直接传入正片文本会导致算法推断的事件过长。

试该接口为我们提供了一个UniversalDetector类,该类包含一个feed方法,接受一个字符串,用于推断,一个done属性,用于检测推断的字符集的置信度是否已经到达了推断的最低门槛,到达最低门槛时,该对象将会吧done值置为true

在分析完全部分当时,请调用close方法,因为该方法会进行最后的计算,以应对没有任何一个可能的字符集的置信度达到最低门槛的情况。

最后result属性将是一个包含了可能的字符集与置信度的dictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
import urllib.request
from chardet.universaldetector import UniversalDetector

usock = urllib.request.urlopen('http://yahoo.co.jp/')
detector = UniversalDetector()
for line in usock.readlines():
detector.feed(line)
if detector.done: break
detector.close()
usock.close()
print(detector.result)

# {'encoding': 'EUC-JP', 'confidence': 0.99}

多文档分析

如果需要对多个文本进行字符集预测,则可以使用同一个UniversalDetector类,并在对每个文档进行预测前使用reset方法。

1
2
3
4
5
6
7
8
9
10
11
12
import glob
from chardet.universaldetector import UniversalDetector

detector = UniversalDetector()
for filename in glob.glob('*.xml'):
print(filename.ljust(60), end='')
detector.reset()
for line in open(filename, 'rb'):
detector.feed(line)
if detector.done: break
detector.close()
print(detector.result)

评论