现在网站应对爬虫的手法越来越多,很多能够看到的数据也没法通过代码直接获取。
我们在之前的文章 中提到了,大众点评是用了静态字体加密。这次我们抱着学习的态度,以猫眼电影为例来探讨下如何应对动态字体加密。
没有了解过字体加密的小伙伴可以先看看上一篇,本文与上一篇重复的部分就不细讲了。
我们打开猫眼电影票房榜单的首页
https://maoyan.com/board/1
很明显,猫眼电影的榜单进行了字体加密。
让我们回忆一下破解大众点评的步骤:
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:790921645
1、下载网站font字体包
2、将font字体包中导入FontEditor 观察得到乱码与数字的关系
3、前缀替换,并将字体名字和它们所对应的乱码构成一个字典
4、根据字典将加密的数字替换
然而,右键刷新页面,字体文件一直在变:
为了探究一下,我们随便下载3个字体文件,对比看看能不能发现其中的规律。
分别重命名为A.woff,B.woff,C.woff,将他们依次导入FontEditor中打开
其中A字体的1对应的是【uniECC8】
B字体的1对应的是【uniE5FD】
C字体的1对应的是【uniEE6C】
并无规律。
我们再将.woff文件转换成.xml文件,看看字体结构有没有相似之处:
#.woff文件转换成.xml文件
from fontTools.ttLib import TTFont
font = TTFont('./.woff')
font.saveXML('A.xml')
每一个编码都对应一个TTGlyph对象,而许多行的XY坐标点最终绘制成数字。
很多网上的教程到这里就结束了,因为按理说这三个字体的统一数字对应的XY坐标应是一样的。
这说明猫眼最近又新挖了一个坑,继续填坑。
看看上面的三个图,其实他们的XY坐标差异并不大。
所以我们允许在一定范围内的差异就算一样就好啦。
由于有负数,通过abs函数取绝对值
#对比两个坐标的差异
def compare(AA, BB):
for i in range(5):
if abs(AA[i][0] - BB[i][0]) < 80 and abs(AA[i][1] - BB[i][1]) < 80:
pass
else:
return False
return True
#True则可视为是同一个字
这样我们就以某字体基准,无论现在实时的字体是哪一个,只要下载下来,再与该字体进行坐标差异对比,相似的就是同一数字。
在网上找了一张思路图,方便大家理解:
我们下面尝试一下:
1、将新下载的字体文件与base_font对比,找到对应关系
2、前缀替换,并将字体名字和它们所对应的乱码构成一个字典
3、根据字典将加密的数字替换
# 字体解密
def modify_html(newFont, html):
basefont = TTFont('./base_font.woff')
unilist = newFont['cmap'].tables[0].ttFont.getGlyphOrder()
numlist = []
base_num = ['6', '3', '7', '1', '5', '9', '0', '4', '2', '8']
base_unicode = ['uniF0DA', 'uniE907', 'uniED01', 'uniEAE1', 'uniF206',
'uniE455', 'uniF401', 'uniE19C', 'uniEB76', 'uniF855']
for i in range(1, len(unilist)):
newGlyph = newFont['glyf'][unilist[i]].coordinates
for j in range(len(base_unicode)):
baseGlyph = basefont['glyf'][base_unicode[j]].coordinates
if compare(newGlyph,baseGlyph):
numlist.append(base_num[j])
break
rowList = []
for i in unilist[2:]:
i = i.replace('uni', '').lower() + ";"
rowList.append(i)
dictory = dict(zip(rowList, numlist))
for key in dictory:
if key in html:
html = html.replace(key, str(dictory[key]))
return html
# 返回解密后的html
4、利用正则表达式获取数据
# 正则
def parse_page(html): pattern = re.compile('
.*?board-index-.*?>(.*?).*?src="(.*?)".*?' + 'title="(.*?)".*?class="star">(.*?).*?releasetime">(.*?).*?' + 'realtime".*?stonefont">(.*?).*?' + 'total-boxoffice".*?stonefont">(.*?).*? ', re.S) items = re.findall(pattern, html) data = pd.DataFrame(items,columns=['index','image','title','star','releasetime','realtime','total-boxoffice']) data['star']=data['star'].str[3:] data['releasetime']=data['releasetime'].str[5:] print(data) return data
运行一下。
票房数据get✔️