猫眼电影
如果试图有人采集过猫眼电影的票房数据,就会发现它的关键字段是加密的。
打开浏览器的控制面板,就会发现虽然在前端是人眼可识别的,但是在网页源代码里面却不是正常的数字,可见它们是被加密了。
可以通过返回的网页源代码看到,3105.65
在网页源代码里是.
,其中0
对应着
,这样对于爬虫而言是无法处理的,所以需要解析出真正的数字
解决方法
我们可以看到class
名字是stonefont
,这是一个自定义字体,我们可以在返回的页面里面找到这个字体资源的地址
如果可以将字体和数值一一对应那么就可以解决问题了,那么,如何通过
python
实现?答案就是一个有关字体的模块:
fonttools
from fontTools.ttLib import TTFont
font_maoyan = TTFont('maoyan.woff')
font_maoyan.saveXML('maoyan.xml')
通过saveXML()
方法可以把字体资源保存在xml
文件中,打开xml
文件,可以发现下面的内容
如果通过浏览器对比,可以发现,0
对应
,1
对应
,...,9
对应
,正好和上图对应上了,而这个其实可以通过getGlyphOrder()
方法的到。
print(font_maoyan.getGlyphOrder())
# ['glyph00000', 'x', 'uniF07E', 'uniED44', 'uniE41D', 'uniE26D', 'uniF391', 'uniEE67', 'uniF83E', 'uniED3B', 'uniF5A0', 'uniE080']
所以我们在请求页面之后,解析出字体资源的链接,然后再请求字体资源,解析出字符的映射关系,最后再把请求页面替换成源字符即可。我们可以把字体资源的结果缓存起来,如果缓存中有的话,就直接返回,如果没有,就请求资源在解析。
python实现
import re
import requests
from cStringIO import StringIO
from fontTools.ttLib import TTFont
_pat_font_url = re.compile("'(//vfile.meituan.net/colorstone/([0-9a-f]{32}).+?woff)'")
_pat_font = re.compile('[0-9a-f]{4};')
maps = {}
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
'Host': 'maoyan.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
}
def get_font_regx(digest, font_url):
if digest in maps: # 缓存
return maps[digest]
resp = requests.get(font_url)
font = TTFont(StringIO(resp.content))
mappings = {'{};'.format(k[3:].lower()): str(idx) for idx, k in enumerate(font.getGlyphOrder()[2:])} # 生成映射关系
def callback(regx): # 替换策略
return mappings.get(regx.group(0), regx.group(0))
maps[digest] = callback
return callback
if __name__ == '__main__':
resp = requests.get('http://maoyan.com/', headers=headers)
url, digest = _pat_font_url.search(resp.text).groups()
font_url = 'http:' + url
callback = get_font_regx(digest, font_url)
text = _pat_font.sub(callback, resp.text)
print(text)
实习僧
上面的网站只有数字被替换了,比较容易处理,但是这个网站就不一样了,里面还有一些汉字也被替换了:
如果我们像之前那样那样操作,发现GlyphOrder
没有什么有用的内容:
...
其实通过注释可以看到,id
只是显示字符的顺序,并不是它所代表的字符,猫眼里面也是这样。但是我们可以在另一个结构里面找到答案:
...
比较了几个之后可以发现,
对应1
,它的unicode
编码是\u0031
,
对应广
,它的unicode
编码是\u5e7f
,和上面能够正确对应。而这个映射关系可以通过getBestCmap()
得到,因此我们的实现,只要稍微修改之前的代码即可。
font = TTFont('shixiseng.woff')
font.getBestCmap()
{120: 'x',
57408: 'uni45',
57427: 'uni6b',
57555: 'uni53',
57603: 'uni42',
57882: 'uni57',
57887: 'uni4E00',
57905: 'uni38',
57939: 'uni5a',
57946: 'uni5E7F',
57970: 'uni77',
58051: 'uni4c',
58080: 'uni4b',
...
}
python实现
import re
import requests
from cStringIO import StringIO
from fontTools.ttLib import TTFont
_pat_font_content = re.compile('myFont; src: url\("data:application/octet-stream;base64,(.+?)"')
_pat_font = re.compile('[0-9a-f]{4}')
maps = {}
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
'Host': 'www.shixiseng.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
}
def get_font_regx(content):
if content in maps:
return maps[content]
ctx = content.decode('base64')
font = TTFont(StringIO(ctx))
mappings = {}
for k, v in font.getBestCmap().items():
if v.startswith('uni'):
mappings['{:x}'.format(k)] = unichr(int(v[3:], 16))
else:
mappings['{:x}'.format(k)] = v
def callback(regx):
return mappings.get(regx.group(0), regx.group(0))
maps[content] = callback
return callback
if __name__ == '__main__':
resp = requests.get('https://www.shixiseng.com/interns?k=python&p=1', headers=headers)
content = _pat_font_content.search(resp.text).group(1)
callback = get_font_regx(content)
text = _pat_font.sub(callback, resp.text)
print(text)