在项目中解决python2.*中文编码问题

从用户行为日志中检测到用户的搜索行为,从而获得搜索关键词。

搜索关键词的获取

日志读取

面对海量的用户行为日志,使用Hadoop Streaming工具做初始的数据提取,根据MapReduce模型。
Map阶段输出:
用户id,网站类型,搜索关键词,跳转前出现次数,跳转后出现次数
Reduce阶段输出:
用户id,网站类型,

搜索引擎的正则表达:

根据某段时间各搜索引擎的pv排行前六位:
baidu,haosou,sougou,bing,google,youdao
记录用户在这6个搜索引擎上的搜寻关键词应该能覆盖95%的需求。写一段正则:

re.compile(r'(google.+?q=|baidu.+?wd=|baidu.+?kw=|baidu.+?word=|haosou.+?q=|youdao.+?q=|sogou.+?query=|bing.+?q=)([^&]*)')

购物网站搜索词字段

通过观察电子商务网站搜索请求的URL,下列这段正则:

re.compile(r'(list.tmall.com/search_product.htm?q=|s.taobao.com/search?q=|search.jd.com/Search?keyword=|search.suning.com/|search.gome.com.cn/search?question=|search.gome.com.cn/search?question=)([^&]*)')

URL中的中文编码问题

比如说:“苹果手机6p”的淘宝搜索url:https://s.taobao.com/search?q=%E8%8B%B9%E6%9E%9C%E6%89%8B%E6%9C%BA6p&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.7724922.8452-taobao-item.2&initiative_id=tbindexz_20150814
它的Unicode编码UTF-8实现是:\xE8\x8B\xB9\xE6\x9E\x9C\xE6\x89\x8B\xE6\x9C\xBA\x36\x70
URL的UTF-8表示:
%E8%8B%B9%E6%9E%9C%E6%89%8B%E6%9C%BA6p
因此,要获得关键词,先得把URL的UTF-8表示的编码转换成正常的UTF-8编码,用到python里的urllib包:

kewyowrd_utf-8 = urllib.unquote(keyword_url_utf-8)

此时utf-8编码的关键词在终端上能正确显示(环境编码配置为utf-8),然而情况要复杂些。

回顾下中文编码知识,中文可以由GBK2312编码,也可以Unicode编码,再由UTF-8,UTF-16表示,那我们从url中获得的关键词编码怎么才能知道是用啥编码表示的呢,这样才能正确解析啊。
绝大部分网站的正常搜索行为的url中得关键词编码均是UTF-8的,但存在莫名其妙的跳转去taobao搜索等情况,关键词会变成gbk2312编码,而天猫的关键词是用gbk2312编码,所以,目前不能总结出网站,情况与关键词编码格式的对应表,只能对一串编码进行检测,获得它最有可能的编码表达方式。
Python的chardet包,morzilla开发的,用于检测一串字符串最有可能的编码方式,使用方法如下:

chardet.detect(keyword_in_url)

返回一段字符串的最有可能的编码方式和置信度,以此为判断,对UTF-8,GBK,ASCii字符串进行解析。
代码表示:

keyword_str = urllib.unquote(keyword_str)
keyword_in_ref = urllib.unquote(keyword_in_ref)
if chardet.detect(keyword_in_ref)['encoding'] == 'utf-8':
     keyword_in_ref = keyword_in_ref.replace('+',' ')
     print guid + ' ' + web_type_ref + ' ' + keyword_in_ref + '\t' + '1' + ' ' + '0'
     pass
elif chardet.detect(keyword_in_ref)['encoding'] == 'ascii':
     keyword_in_ref = keyword_in_ref.replace('+',' ')
     print guid + ' ' + web_type_ref + ' ' + keyword_in_ref + '\t' + '1' + ' ' + '0'
     pass
else:
     try:
        keyword_in_ref = keyword_in_ref.replace('+',' ')
        keyword_in_ref = keyword_in_ref.decode('gbk')
        print guid + ' ' + web_type_ref + ' ' + keyword_in_ref.encode('utf-8') + '\t' + '1' + ' ' + '0'
    except UnicodeDecodeError:
         pass

用户的搜索行为习惯使用空格作为分词手法,例如:连衣裙 波点的%E8%BF%9E%E8%A1%A3%E8%A3%99+%E6%B3%A2%E7%82%B9,以加号表示空格,所以在转成中文的时候,得先把加号replace成空格。

搜索关键词持久化

导入Mongodb过程中,出现错误。
Image
幸好错误提示很清晰,是Mongodb的输入出现了非utf-8编码的字符。定位到错误数据:
某用户id%密封箱 食品%密封箱%气袋%酸奶杯%气垫床医用%气垫床%酸奶机˫᪸˾�
在最后的一个关键词中出现了乱码。
出现乱码最简单的原因是一种编码的字符串被另一张编码规则解码,比如一个UTF-8编码表示的字符串安装GBK2312来解,那出来很有可能就是生僻字或者乱码。找到原始日记中这个关键词的记录:
ref字段中的关键词编码:这段编码长度是8,显然应当是GBK编码,这个是“双岐杆菌”的GBK编码,但是根据chardet包的判断:
Image
它是UTF-8编码表示的置信度高达87.6%,于是,我的程序就把它按UTF-8解码了,出现了乱码。
解决方法:
暂未改进chardet包中的判断编码方式的算法,那只能靠捕捉导入mongodb时候的异常了咯:

try:
    导入
except bson.errors.InvalidStringData:
    pass
Comments
Write a Comment