【爬虫进阶】猿人学任务七之字体反爬(难度2.0)

前言

往期有讲解过某团字体反爬,感觉效果不太好,所以本章重新找了个例子,希望能帮助大家理解透彻!再遇到直接手撕。

特此说明

如果涉及到版权问题,请立刻联系博主删除!

分析

首先,我们看题目,要求是找出胜点最高的召唤师,意思我们需要先获取胜点

在这里插入图片描述

找到数据接口,发现字体都加密了,怎么破呢?这就是本文所要讲解的核心内容!

在这里插入图片描述在这里插入图片描述

先找到字体文件,他这里是动态加载的,每次请求结果都不一样,请求一次就保存一次,实时获取!

在这里插入图片描述

这里就已经保存本地了,pycharm不能直接读取woff文件,所以需要通过fontTools库操作woff

pip install fontTools

在这里插入图片描述

解密之后,保存为xml文件,为后面找出字体与编码的对应关系

在这里插入图片描述

代码过程

  • 请求数据接口

    def get_html(page):
        headers = {
            "user-agent": "yuanrenxue.project" # 题目要求,必须加上请求头
        }
        url = f"https://match.yuanrenxue.com/api/match/7?page={page}" # 数据有5页,通过遍历实现
        print(url)
        req = requests.get(url=url, headers=headers)
        return req.json() # 返回json格式
    
    
  • 获取woff字体文件

    def with_ttf(woff):
        b64_code = woff
        with open('font.woff', 'wb') as f:
            f.write(base64.decodebytes(b64_code.encode())) # 解码
    

解析字体文件,处理编码与字体的对应关系,这一步是关键点,所以着重说明:接口中字体前三位字符是&#x,而woff文件中前三位是uni,后面四位字符则其对应,所以我们需要将一方转换成另一方,这样才方便我们后续匹配
在这里插入图片描述
在这里插入图片描述
处理对应关系,我们根据编码找出对应的glfy值,无论编码如何变化,值是不会改变的

在这里插入图片描述

  • 保存xml文件
    def get_xml(data):
        font = TTFont('font.woff') # 打开woff文件
        font.saveXML('movie.xml') # 保存xml文件
        data_value = data.get('value').strip().replace('&#x', 'uni') # 替换&#x为uni
        data_value_list = data_value.split(" ") # 去除空格
        map_num_list = []
        for data_v in data_value_list:
            flags_num = list(font['glyf'][data_v].flags)  # 得到编码映射的值
            flags_num_str = "".join([str(flag) for flag in flags_num])  # 列表推导式
            print(flags_num_str)

结果:通过得到的值对比网页数据,就能找到对应的关系

在这里插入图片描述

在这里插入图片描述

下面就是对应的字体,然后我们进行匹配

 map_num = {
     "10100100100101010010010010": '0',
     "100110101001010101011110101000": '2',
     "111111111111111": '4',
     "1110101001001010110101010100101011111": '5',
     "1001101111": '1',
     "10010101001110101011010101010101000100100": '9',
     "101010101101010001010101101010101010010010010101001000010": '8',
     "10101100101000111100010101011010100101010100": '3',
     "1111111": '7',
     "10101010100001010111010101101010010101000": '6'
 }
  • 获取字体编码映射值
def get_xml(data):
   	font = TTFont('font.woff') # 打开woff文件
    font.saveXML('movie.xml') # 保存xml文件
    data_value = data.get('value').strip().replace('&#x', 'uni') # 替换&#x为uni
    data_value_list = data_value.split(" ") # 去除空格
    map_num_list = []
    for data_v in data_value_list:
        map_num = {
            "10100100100101010010010010": '0',
            "100110101001010101011110101000": '2',
            "111111111111111": '4',
            "1110101001001010110101010100101011111": '5',
            "1001101111": '1',
            "10010101001110101011010101010101000100100": '9',
            "101010101101010001010101101010101010010010010101001000010": '8',
            "10101100101000111100010101011010100101010100": '3',
            "1111111": '7',
            "10101010100001010111010101101010010101000": '6'
        }  # 映射值
        flags_num = list(font['glyf'][data_v].flags)  # 每个编码对应的值
        flags_num_str = "".join([str(flag) for flag in flags_num])  # 列表推导式
        map_num_list.append(map_num[flags_num_str]) # 匹配成功后,添加到刘表
    return "".join(map_num_list)

成果

在这里插入图片描述

完整源码

from fontTools.ttLib import TTFont
import requests
import json
import base64


def get_html(page):

    headers = {
        "user-agent": "yuanrenxue.project",
    }
    url = f"https://match.yuanrenxue.com/api/match/7?page={page}"
    print(url)
    req = requests.get(url=url, headers=headers).json()
    return req


def with_ttf(woff):
    b64_code = woff
    with open('font.woff', 'wb') as f:
        f.write(base64.decodebytes(b64_code.encode()))


def get_xml(data):
    font = TTFont('font.woff')
    font.saveXML('movie.xml')
    data_value = data.get('value').strip().replace('&#x', 'uni')
    data_value_list = data_value.split(" ")
    map_num_list = []
    for data_v in data_value_list:
        map_num = {
            "10100100100101010010010010": '0',
            "100110101001010101011110101000": '2',
            "111111111111111": '4',
            "1110101001001010110101010100101011111": '5',
            "1001101111": '1',
            "10010101001110101011010101010101000100100": '9',
            "101010101101010001010101101010101010010010010101001000010": '8',
            "10101100101000111100010101011010100101010100": '3',
            "1111111": '7',
            "10101010100001010111010101101010010101000": '6'
        }
        flags_num = list(font['glyf'][data_v].flags)
        flags_num_str = "".join([str(flag) for flag in flags_num])
        map_num_list.append(map_num[flags_num_str])
    return "".join(map_num_list)


if __name__ == '__main__':
    for page in range(1,6):
        res = get_html(page)
        woff = res.get('woff')
        with_ttf(woff)
        data_num = []
        for data in res.get('data'):
            map_num_str = get_xml(data)
            data_num.append(map_num_str)

        print(data_num)
        print(f'最大值:{max(data_num)}')

点关注不迷路,本文若对你有帮助,烦请三连支持一下 ❤️❤️❤️
各位的支持和认可就是我最大的动力❤️❤️❤️

在这里插入图片描述

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值