常见反爬措施--动态字体反爬

发布时间 2023-03-26 19:19:52作者: 乐之之

  通过对数据的获取,我们已经掌握了一些简单的反爬措施,但是获取到的数据都是加密的,无法直接读取其中的信息。那么我们该怎么解决呢?

  只有通过对加密字体的解密,再将解密字体和加密字体进行replace即可。现在,我们还是以实习僧为例,来看一下其中的解密流程是怎样的。

一、解密前的了解。

  首先,观察到数据改成了json类型展示到网页上,我们通过后台管理工具观察XHR发现有一些数据的字体是加密的。如下图所示。

  那么想要解密字体,就必须找到字体字样文件,这里直接点击字体选项,捕获字体文件,一般是file?开头的文件就是解密字体文件。

  在标头中找到链接,打开链接下载即可。

在这里,我们需要一个可视化查看字体的软件:High-Logic FontCreator。打开该文件如下图所示。

  可以确定的是,这就是我们所需要的字体解密文件。

  接下来我们需要通过这个解密文件做两个事情:

  1、通过python读取该文件,获取加密前的数字,进行模拟加密过程,获取同样的加密密文。

  2、手动整体该文件可视化中呈现的字体对应关系。如:“年”:uni5E74。

  大致流程图如下:

二、开始解密

  首先,导入需要读取解密字体样式的模块。

from fontTools.ttLib import TTFont

  将字体解密文件直接放进python中

  读取字体文件的加密前的样式

font = TTFont('file')
cont = font.getBestCmap()
print(cont)

  打印打码如下:

  通过我们对上述加密字体的观察发现,加密字体中的&#xe175,是经uni70所对应的57717通过16进制加密(hex())后,再将其中开头0转为&#表示得来的。如图:

  那么接下来我们只需要通过字体文件中的glyf锁定密文前所指向的值,如:‘uni70’。

glyf = font['glyf']
glyfmap = {
    0: glyf['uni30'],
    1: glyf['uni31'],
    2: glyf['uni32'],
    3: glyf['uni33'],
    4: glyf['uni34'],
    5: glyf['uni35'],
    6: glyf['uni36'],
    7: glyf['uni37'],
    8: glyf['uni38'],
    9: glyf['uni39'],
    'p': glyf['uni70'],
    'P': glyf['uni50'],
    'Y': glyf['uni59'],
    'T': glyf['uni54'],
    'H': glyf['uni48'],
    'O': glyf['uni4f'],
    'y': glyf['uni79'],
    't': glyf['uni74'],
    'h': glyf['uni68'],
    'o': glyf['uni6f'],
    'n': glyf['uni6e'],
    '工': glyf['uni5DE5'],
    '程': glyf['uni7A0B'],
    '师': glyf['uni5E08'],
    '生': glyf['uni751F'],
    '端': glyf['uni7AEF'],
    '前': glyf['uni524D'],
    '网': glyf['uni7F51'],
    '月': glyf['uni6708'],
    '软': glyf['uni8F6F'],
    '联': glyf['uni8054'],
    '银': glyf['uni94F6'],
    '行': glyf['uni884C'],
    '件': glyf['uni4EF6'],
    '政': glyf['uni653F'],
    '人': glyf['uni4EBA'],
    '互': glyf['uni4E92'],
    '三': glyf['uni4E09'],
    '作': glyf['uni4F5C'],
    '告': glyf['uni544A'],
    '个': glyf['uni4E2A'],
    '市': glyf['uni5E02'],
    '广': glyf['uni5E7F'],
    '天': glyf['uni5929'],
    '五': glyf['uni4E94'],
    '设': glyf['uni8BBE'],
    '年': glyf['uni5E74'],
    '周': glyf['uni5468'],
    '招': glyf['uni62DB'],
    '聘': glyf['uni8058'],
    '场': glyf['uni573A'],
    '财': glyf['uni8D22'],
    '四': glyf['uni56DB'],
    '计': glyf['uni8BA1'],
    '会': glyf['uni4F1A'],
    '一': glyf['uni4E00'],
    'X': glyf['uni58'],
    'L': glyf['uni4c'],
    'd': glyf['uni64'],
    '|': glyf['uni6c'],
    'x': glyf['uni78'],
    'C': glyf['uni43'],
    'G': glyf['uni47'],
    'K': glyf['uni4b'],
    'S': glyf['uni53'],
    'W': glyf['uni57'],
    'c': glyf['uni63'],
    'g': glyf['uni67'],
    'k': glyf['uni6b'],
    's': glyf['uni73'],
    'w': glyf['uni77'],
    'B': glyf['uni42'],
    'F': glyf['uni46'],
    'N': glyf['uni4e'],
    'R': glyf['uni52'],
    'V': glyf['uni56'],
    'Z': glyf['uni5a'],
    'b': glyf['uni62'],
    'f': glyf['uni66'],
    'j': glyf['uni6a'],
    'r': glyf['uni72'],
    'v': glyf['uni76'],
    'z': glyf['uni7a'],
    'A': glyf['uni41'],
    'J': glyf['uni4a'],
    'E': glyf['uni45'],
    'I': glyf['uni49'],
    'M': glyf['uni4d'],
    'Q': glyf['uni51'],
    'U': glyf['uni55'],
    'a': glyf['uni61'],
    'e': glyf['uni65'],
    'i': glyf['uni69'],
    'm': glyf['uni6d'],
    'q': glyf['uni71'],
    'u': glyf['uni75'],
}

  只要可视化工具中所对应的glyf['uni70']与字体文件中读取到的glyf['uni70']相等,那么就将密文替换为可视化工具中所打出的对应关系glyfmap字典中的键,即&#e175换成‘P’。

具体代码如下:

cont = font.getBestCmap()
for data,name in cont.items():
    hex_data=hex(data)
    name = glyf[name]
    for data_new,name_new in glyfmap.items():
        if name_new == name:
            name55 = name55.replace(hex_data,str(data_new))

  在这里,由于涉及到翻页,将代码放进了类中进行调用。完整代码如下:

import requests
from fontTools.ttLib import TTFont

font = TTFont('file')
glyf = font['glyf']
glyfmap = {
    0: glyf['uni30'],
    1: glyf['uni31'],
    2: glyf['uni32'],
    3: glyf['uni33'],
    4: glyf['uni34'],
    5: glyf['uni35'],
    6: glyf['uni36'],
    7: glyf['uni37'],
    8: glyf['uni38'],
    9: glyf['uni39'],
    'p': glyf['uni70'],
    'P': glyf['uni50'],
    'Y': glyf['uni59'],
    'T': glyf['uni54'],
    'H': glyf['uni48'],
    'O': glyf['uni4f'],
    'y': glyf['uni79'],
    't': glyf['uni74'],
    'h': glyf['uni68'],
    'o': glyf['uni6f'],
    'n': glyf['uni6e'],
    '工': glyf['uni5DE5'],
    '程': glyf['uni7A0B'],
    '师': glyf['uni5E08'],
    '生': glyf['uni751F'],
    '端': glyf['uni7AEF'],
    '前': glyf['uni524D'],
    '网': glyf['uni7F51'],
    '月': glyf['uni6708'],
    '软': glyf['uni8F6F'],
    '联': glyf['uni8054'],
    '银': glyf['uni94F6'],
    '行': glyf['uni884C'],
    '件': glyf['uni4EF6'],
    '政': glyf['uni653F'],
    '人': glyf['uni4EBA'],
    '互': glyf['uni4E92'],
    '三': glyf['uni4E09'],
    '作': glyf['uni4F5C'],
    '告': glyf['uni544A'],
    '个': glyf['uni4E2A'],
    '市': glyf['uni5E02'],
    '广': glyf['uni5E7F'],
    '天': glyf['uni5929'],
    '五': glyf['uni4E94'],
    '设': glyf['uni8BBE'],
    '年': glyf['uni5E74'],
    '周': glyf['uni5468'],
    '招': glyf['uni62DB'],
    '聘': glyf['uni8058'],
    '场': glyf['uni573A'],
    '财': glyf['uni8D22'],
    '四': glyf['uni56DB'],
    '计': glyf['uni8BA1'],
    '会': glyf['uni4F1A'],
    '一': glyf['uni4E00'],
    'X': glyf['uni58'],
    'L': glyf['uni4c'],
    'd': glyf['uni64'],
    '|': glyf['uni6c'],
    'x': glyf['uni78'],
    'C': glyf['uni43'],
    'G': glyf['uni47'],
    'K': glyf['uni4b'],
    'S': glyf['uni53'],
    'W': glyf['uni57'],
    'c': glyf['uni63'],
    'g': glyf['uni67'],
    'k': glyf['uni6b'],
    's': glyf['uni73'],
    'w': glyf['uni77'],
    'B': glyf['uni42'],
    'F': glyf['uni46'],
    'N': glyf['uni4e'],
    'R': glyf['uni52'],
    'V': glyf['uni56'],
    'Z': glyf['uni5a'],
    'b': glyf['uni62'],
    'f': glyf['uni66'],
    'j': glyf['uni6a'],
    'r': glyf['uni72'],
    'v': glyf['uni76'],
    'z': glyf['uni7a'],
    'A': glyf['uni41'],
    'J': glyf['uni4a'],
    'E': glyf['uni45'],
    'I': glyf['uni49'],
    'M': glyf['uni4d'],
    'Q': glyf['uni51'],
    'U': glyf['uni55'],
    'a': glyf['uni61'],
    'e': glyf['uni65'],
    'i': glyf['uni69'],
    'm': glyf['uni6d'],
    'q': glyf['uni71'],
    'u': glyf['uni75'],
}
# 寻找对应关系
class Shi_Xiseng(object):
    def __init__(self):
        self.url="https://www.shixiseng.com/app/interns/search/v2?"
        self.headers={
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
        }


    def data_parse(self):
        for i in range(1,10):
            params={
            "build_time": "1679799002643",
            "page": i,
            "type": "intern",
            "keyword": "python爬虫",
            "area": "",
            "months": "",
            "days": "",
            "degree": "",
            "official": "",
            "enterprise": "",
            "salary": "-0",
            "publishTime": "",
            "sortType": "",
            "city": "全国",
            "internExtend": "",
            }
            response = requests.get(url=self.url,headers=self.headers,params=params).json()
            # print(response)
            response_list=response['msg']['data']
            for data in response_list:
                city = data['city']
                cname = data['cname']
                name55 = str(data['name']).replace("&#","0")
                name = self.jie_mi(name55)
                maxsal = self.jie_mi(str(data['maxsal']).replace("&#","0"))
                minsal = self.jie_mi(str(data['minsal']).replace("&#","0"))
                month_num = self.jie_mi(str(data['month_num']).replace("&#","0"))
                print(city,cname,name,maxsal,minsal,month_num)
    def jie_mi(self,name55):
        cont = font.getBestCmap()
        for data,name in cont.items():
            hex_data=hex(data)
            name = glyf[name]
            for data_new,name_new in glyfmap.items():
                if name_new == name:
                    name55 = name55.replace(hex_data,str(data_new))
        return name55

if __name__ == '__main__':
    d=Shi_Xiseng()
    d.data_parse()

 效果:

  ps:字体文件具有时效性,有的是需要定期更换(即重新下载一遍放进python中读取就可以了)。

  那么到这里,关于动态字体的解密示例就结束了...希望可以帮助到你。