58同城招聘字体反爬

在这里插入图片描述
在这里插入图片描述
可以发现对应的某些字体是以某种编码的形式存在的
对应的,在网页里面搜索不到ttf文件,但可以通过network发现@font-face这个东西于是找这个东西
在这里插入图片描述
发现是经过base64加密的,于是要用正则把这一段提取出来并进行base64的解密并下载,就可以得到字体对应的ttf文件。

在这里插入图片描述
令人费解的是,对于用TTFont加载了这个文件并打印对应的camp发现它的键和值都是在变的,这就变得没有规律可循的,之前做的起点中文网的字体反爬是在键值里面有一个是定值的,但是它肯定是有什么规律的,所有保存为xml进行观察,对ttf文件内部进行分析

打开通过文件下载好的两个xml文件进行比对
在这里插入图片描述
在这里插入图片描述
可以发现name是不同的,但是里面的参数是一样的,也就是说可以根据里面的参数去判断字体。所有先下载好一个ttf文件,把里面的对应字体写在一个列表里,在以同一个会话发送请求再下载一个新的ttf文件并解析,把两个ttf文件的on字段进行比对从而得出字体的映射关系

import requests
import re
from lxml import etree
from io import BytesIO
import base64
from fontTools.ttLib import TTFont

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
}

def get_ttf_file(web_orgin):
    # 从不经过解码的源代码中提取base64加密后的编码
    font_encoded_data = re.search('base64,(.*?)\)', web_orgin, re.S).group(1)
    font_decoded_data = base64.b64decode(font_encoded_data)
    # 保存反爬的字体文件到本地
    ttf_file_name = 'D://font_58_new.ttf'
    with open(ttf_file_name, 'wb') as fp:
        fp.write(font_decoded_data)

# 因为这里的编码和字体都是在变化的,不可直接找到定值
# 解决的方法是:先下载好一个ttf文件作为对照,再在新请求的时候
# 新下载一个新的ttf文件,对比两个ttf文件内部每个编码对应的on字段的所有
# 0或1是否是匹配的
def parse_ttf():
    # 根据事先下载好的字体文件font_58对应顺序写出里面的字体
    font_list = [
		u'张', u'4', u'E', u'高', u'2', u'校', u'生', u'科',
		u'黄', u'3', u'以', u'技', u'0', u'验', u'1', u'本', u'女',
		u'陈', u'5', u'大', u'专', u'届', u'M', u'无', u'硕', u'士',
		u'6', u'男', u'B', u'经', u'中', u'杨', u'吴', u'王', u'李',
		u'下', u'9', u'8', u'博', u'周', u'赵', u'刘', u'7', u'应', u'A'
	]
    font_base = TTFont('D://font_58.ttf')# 这个文件是先前下载好的,并对应上面这个列表的顺序
    # font_base.saveXML('D://font_base.xml')
    font_base_order = font_base.getGlyphOrder()[1:]
    print(font_base_order)

    new_font = TTFont('D://font_58_new.ttf')
    new_font.saveXML('D://new_font.xml')
    new_font_order = new_font.getGlyphOrder()[1:]
    print(new_font_order)

    base_flag_list = list()
    for i in font_base_order:
        flags = font_base['glyf'][i].flags# 返回一个字符对应的on字段的0和1组成的列表
        base_flag_list.append(list(flags))# 二维列表,每一行是对应一个字体的所有on字段的信息

    new_flag_list = list()
    for i in new_font_order:
        flags = new_font['glyf'][i].flags# 返回一个字符对应的on字段的0和1组成的列表
        new_flag_list.append(list(flags))

    memory_dict = dict()
    # 双重循环对每一行进行进行比较,并对里面的对位元素进行一一比对
    for index1,x in enumerate(base_flag_list):
        for index2,y in enumerate(new_flag_list):
            if compare(x,y):# 如果找到相对应的
                key = new_font_order[index2]# key为对应的编码
                key = '&#x' + key.replace('uni','').lower() + ';'
                memory_dict[key] = font_list[index1]# 让其对应于font_58文件写好的字符

    return memory_dict

def compare(list1,list2):
    len1 = len(list1)
    len2 = len(list2)
    if len1 != len2:
        return False
    for i in range(len1):
        if list1[i] != list2[i]:
            return False
    return True

def use_parse_result(memory_dict,web_orgin):
    print(memory_dict)
    # selector = etree.HTML(web_orgin)
    # dd_list = selector.xpath("//dl[@class='top-resume']")
    # for dd in dd_list:
    #     name = dd.xpath("dd[@class='w70 stonefont resumeName']/text()")[0]
    #     # sex = dd.xpath("dd[@class='w48 stonefont']/text()")
    #     # age = dd.xpath("dd[@class='w70 stonefont']/text()")
    #     # year = dd.xpath("dd[@class='w80 stonefont']/text()")
    names = re.findall('</dt>.*?<dd.*?stonefont resumeName">(.*?)</dd>',web_orgin,re.S)
    print(names)
    for name in names:
        print(name,end=' ')
        for key in memory_dict.keys():
            if key in name:
                name = name.replace(key,memory_dict[key])
        print(name)

if __name__ == '__main__':
    url = 'https://gz.58.com/qztech/'
    session = requests.session()# 维持会话
    response = session.get(url, headers=headers)
    web_orgin = response.text
    web_data = response.content.decode()
    get_ttf_file(web_data)
    mdict = parse_ttf()
    use_parse_result(mdict,web_orgin)

因为每次请求获取的网页的源代码会不同,所有有时可以成功有时不行,记住解析网页时要维持同一个会话。不然字体的加密编码就不对应了

结果
在这里插入图片描述
参考文章:https://mp.weixin.qq.com/s?__biz=MzI2MDA0MjM1NQ==&mid=2651626624&idx=1&sn=3cff561e6080c380838cfe4c0b91f1c6&chksm=f197879cc6e00e8a94cf4b55ee47af615a906b73fbfc4a52f0161ca99f4891e1be8410990afb&mpshare=1&scene=23&srcid=#rd

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值