解析某电影和某招聘网站的web-font自定义字体

猫眼电影

如果试图有人采集过猫眼电影的票房数据,就会发现它的关键字段是加密的。
打开浏览器的控制面板,就会发现虽然在前端是人眼可识别的,但是在网页源代码里面却不是正常的数字,可见它们是被加密了。

解析某电影和某招聘网站的web-font自定义字体_第1张图片
element.png
source.png

可以通过返回的网页源代码看到,3105.65在网页源代码里是.,其中0对应着,这样对于爬虫而言是无法处理的,所以需要解析出真正的数字

解决方法

我们可以看到class名字是stonefont,这是一个自定义字体,我们可以在返回的页面里面找到这个字体资源的地址

解析某电影和某招聘网站的web-font自定义字体_第2张图片
font.png

如果可以将字体和数值一一对应那么就可以解决问题了,那么,如何通过 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('&#x[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 = {'&#x{};'.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)

实习僧

上面的网站只有数字被替换了,比较容易处理,但是这个网站就不一样了,里面还有一些汉字也被替换了:


front.png
上海
-/天 | 天/周 | 个月

如果我们像之前那样那样操作,发现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('&#x[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{:x}'.format(k)] = unichr(int(v[3:], 16))
        else:
            mappings['&#x{: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)

你可能感兴趣的:(解析某电影和某招聘网站的web-font自定义字体)