可以发现对应的某些字体是以某种编码的形式存在的
对应的,在网页里面搜索不到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 = '' + 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('.*?(.*?)' ,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