欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python爬虫-字体反爬-猫眼国内票房榜

程序员文章站 2022-07-09 21:36:27
偶然间知道到了字体反爬这个东西, 所以决定了解一下. 目标: https://maoyan.com/board/1 问题: 类似下图中的票房数字无法获取, 直接复制粘贴的话会显示 □ 等无法识别的字符, 且网页源码中该类数字均被 .&# ......

偶然间知道到了字体反爬这个东西, 所以决定了解一下.

目标:   

问题:  类似下图中的票房数字无法获取, 直接复制粘贴的话会显示 □ 等无法识别的字符, 且网页源码中该类数字均被 . 之类的字符串代替.

Python爬虫-字体反爬-猫眼国内票房榜

解决:

出现这种情况的原因是因为网页字体是在 css3 @font-face 规则中定义的, 我觉得这种字体就类似描点连线那种方式绘制出来的.

因为定义规则是动态随机获取的, 不能保证每次都是一个字体文件.

如下:

Python爬虫-字体反爬-猫眼国内票房榜

Python爬虫-字体反爬-猫眼国内票房榜

可以看到, 同一数字所对应的字符是不一样的, 所以我们也就动态下载实时字体文件, 具体分析.

虽然每次对应的字符可能不一样, 但是可以发现同一数字的字形是一样的, 也就是"描点的坐标"应该相同.

事实证明在这个例子中是确实如此.

如下:

Python爬虫-字体反爬-猫眼国内票房榜

同一数字对象里的这些值是一样的.

既然找到问题所在和规律了, 就可以直接开始写代码了.

 1 import re
 2 from urllib.request import urlretrieve, urlopen
 3 from fonttools.ttlib import ttfont
 4 
 5 
 6 def process_font(url):
 7     # loc.woff是事先下载好的字体文件
 8     # 可以通过font1.savexml()来了解文件的结构, font1就像一个的字典, xml文件里的tag可以对font1用字典的方法获取
 9     font1 = ttfont('loc.woff')
10     # 使用百度的fonteditor手动确认本地字体文件name和数字之间的对应关系, 保存到字典中
11     loc_dict = {'unie8b2': '5', 'unif818': '3', 'unieccc': '8', 'unie622': '1', 'uniec92': '2', 'unif31a': '4',
12                 'unie86d': '9', 'unie33c': '6', 'unie1fa': '7', 'unie13e': '0'}
13     # 获取字符的name列表, 打印出来后发现第一个和最后一个name所对应的不是数字, 所以切片
14     uni_list1 = font1.getglyphnames()[1: -1]
15 
16     # 网页源码
17     rsp = urlopen(url).read().decode()
18     # 获取动态的字体文件并下载
19     font_url = 'http://' + re.findall(r'url\(\'//(.*?\.woff)', rsp)[0]
20     # web字体文件落地名
21     filename = font_url.split('/')[-1]
22     # 下载web字体文件
23     urlretrieve(font_url, filename)
24 
25     # 打开web字体文件
26     font2 = ttfont(filename)
27     # 获取字符的name列表
28     uni_list2 = font2.getglyphnames()[1: -1]
29 
30     # web字体文件中name和num映射
31     new_map = {}
32 
33     for uni2 in uni_list2:
34         # 获取name 'uni2' 在font2中对应的对象
35         obj2 = font2['glyf'][uni2]
36         for uni1 in uni_list1:
37             # 获取name 'uni1' 在font1中对应的对象
38             obj1 = font1['glyf'][uni1]
39             # 如果两个对象相等, 说明对应的数字一样
40             if obj1 == obj2:
41                 # 将name键num值对加入new_map
42                 new_map[uni2] = loc_dict[uni1]
43 
44     # 将数字替换至源码
45     for i in uni_list2:
46         pattern = '&#x' + i[3:].lower() + ';'
47         rsp = re.sub(pattern, new_map[i], rsp)
48 
49     # 返回处理处理后的源码
50     return rsp
51 
52 
53 if __name__ == '__main__':
54     # 猫眼国内实时票房top10
55     url = 'https://maoyan.com/board/1'
56     # 替换数字后的网页源码
57     res = process_font(url)

 

代码里loc.woff文件是先下载好的, 通过它找到数字和"描点坐标"之间的对应关系. 这个文件大家可以自己提前下载, 并且手动找到对应关系.

这里也提供了我下载的loc.woff文件, , 里面的font.xml文件就是通过savexml()方法得到的, 可以看到字体文件的具体结构.

这是第一次写分享博客, 而且github也没怎么用过, 希望以后能坚持吧.