当前位置 博文首页 > xyl192960的博客:Python爬虫实例——2021字体加密反爬对策

    xyl192960的博客:Python爬虫实例——2021字体加密反爬对策

    作者:[db:作者] 时间:2021-09-16 15:55

    Python爬虫实例——2021字体加密反爬对策

    前言:

    本篇内容仅供学习参考交流,转载请注明出处,如若侵权,请联系删除
    猫眼票房页面的字体加密是动态的,每次或者每天加载页面的字体文件都会有所变化,本篇内容针对这种加密方式进行分析
    字体加密原理:简单来说就是程序员在设计网站的时候使用了自己设计的字体代码对关键字进行编码,在浏览器加载的时会根据这个字体文件对这些字体进行编码,从而显示出正确的字体。

    已知的使用了字体加密的一些网站:
    58同城,起点,猫眼,大众点评,启信宝,天眼查,实习僧,汽车之家
    本篇内容不过多解释字体文件的映射关系,不了解的请自行查找其他资料。
    如若还未入门爬虫,请往这走 Python爬虫入门篇
    High-Logic FontCreator Pro 12.0.0.2546 提取码tt9x

    本篇内容涉及:
    main.py:
    import requests
    import urllib.request as down
    import json
    import re
    from fontTools.ttLib import TTFont # 字体解析库
    from xml.etree.ElementTree import parse # 解析Xml
    MyPyClass.py:
    from difflib import SequenceMatcher # 序列匹配器
    import random
    

    安装:
    pip install difflib
    pip install requests
    pip install fonttools

    分析页面

    发现审查元素或者是查看源文件都无法看到关键票房信息

    01查看字体加密的显示状态

    在每次更新数据的时候会加载一个 xxxx.woff的字体文件,类似于这样的就是字体加密

    02查看woff字体文件

    分析字体文件

    我们可以在请求中找到字体文件的链接,下载并用High-Logic FontCreator 打开,鼠标移动到数字上停留可以看见数字的一些信息
    0304
    在使用fontTools把该字体文件保存为XML格式查看,我们在文本中查找发现比如数字 “1”所对应的“$EA31 ” 在XML格式里面的GlyphOrder组中正好对应的是GlyphID=7,对比其他数字图像与GlyphID是一一对应的

    from fontTools.ttLib import TTFont
    font=TTFont('4fd0b539.woff')
    font.saveXML('maoyan.xml')
    

    05
    我们在搜索每个数字对应的“TTGlyph”,可以找到每个数字每一笔每一画的坐标,并且可以看到‘on’属性
    06
    通过对字体Xml文件的分析和坐标的对比,我想到了两种根据字体文件去匹配数字的方法。

    一:“on属性”列表匹配法

    ——每个数字坐标下都有一个on属性,通过多组字体文件对比发现,on属性是字体文件之间唯一的共同点,比如数字1的on属性列表on_list=[‘1’, ‘1’, ‘1’, ‘0’, ‘0’, ‘1’, ‘1’, ‘0’, ‘1’, ‘0’, ‘0’, ‘1’, ‘1’],通过对比on列表的相似度,可以准确的匹配出数字1

    二:斜率对比法。

    ——因为每个字体文件的字体形状都是一样的,所以每条线段的斜率也应该是一样的,通过坐标相减得到每条线段的斜率,在对斜率进行匹配,应该可以匹配出字形。
    (很早之前我是通过这一种方法尝试去解决字体加密,结果发现猫眼票房每次更新的字体文件的坐标数量都会发生变化,而且不同文件下数字坐标线段所得出的斜率也会发生变化,如果想要准确匹配出数字,可能需要很多的for去迭代,所以不考虑这种方法)



    使用“on属性”列表匹配法解决字体加密

    首先我们需要获得on属性的值就要对字体xml文件进行解析,读取其子节点

    from fontTools.ttLib import TTFont # 字体文件库
    from xml.etree.ElementTree import parse # 解析xml
    font=TTFont('4fd0b539.woff')
    font.saveXml('maoyan.xml') # 将字体保存为xml格式
    xml=parse('maoyan.xml')
    root= xml.getroot() # 获取xml树的根元素
    for i in root:
        if i.tag=='glyf': # 找到glyf元素
            dict_0 = {}
            for j in i: # 遍历在glyf元素下找到每个坐标对应的元素 
                if j.get('name')=='glyph00000' or j.get('name')=='x': # 跳过非数字元素
                    continue
                list_1 = []
                for k in j: # 遍历TTGlyph元素下的多个contour元素
                    for l in k:# 提取每个坐标下pt元素里面的on属性值
                        list_1.append(l.get('on'))
                dict_0[j.get('name')]=list_1 # 将on列表加入到字典
    print(dict_0)
    在通过High-Logic FontCreator软件上找到每个字形的代码可以得出on列表所对应的数字:
        0: ['1', '0', '1', '0', '0', '1', '0', '1', '0', '1', '0', '1', '0', '0', '1', '0', '0', '1', '0', '1', '0', '1', '0', '0', '1', '0', '1', '0', '0', '1', '0', '0', '1', '0'],
        1: ['1', '1', '1', '0', '0', '1', '1', '0', '1', '0', '0', '1', '1'],
        2: ['1', '1', '1', '0', '1', '0', '0', '0', '0', '1', '0', '0', '1', '0', '0', '1', '0', '1', '0', '1', '0', '1', '1', '0', '0', '0', '0', '1', '0', '1', '0', '1', '0', '0', '1', '0', '0', '0', '0', '1'],
        3: ['1', '0', '0', '1', '0', '0', '1', '0', '1', '0', '1', '0', '1', '1', '0', '0', '1', '0', '1', '0', '1', '0','1', '0', '1', '0', '0', '1', '1', '0', '1', '0', '1', '0', '1', '0', '0', '0', '0', '1', '0', '0', '1', '0','1', '0', '1', '0', '0', '1'],
        4: ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'],
        5: ['1', '0', '0', '1', '0', '1', '0', '0', '0', '1', '0', '0', '1', '1', '1', '1', '1', '1', '1', '0', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1'],
        6: ['1', '0', '1', '0', '1', '0', '1', '0', '1', '0', '0', '1', '0', '0', '1', '0', '0', '1', '0', '0', '0', '1','0', '0', '1', '0', '1', '0', '1', '0', '1', '0', '1', '1', '0', '1', '0', '0', '1', '0', '0', '1', '0', '1','0', '1', '0', '1', '0'