上一篇我介绍了破解58同城的字体反爬
https://blog.csdn.net/BigBoy_Coder/article/details/103239672
中间遗漏了几个细节,在这边文章我做一下补充:
遗漏点一:
并不是所有的字体文件格式都是.ttf格式的文件,这里我们要针对原网页的,进行判断
怎么判断呢?
比如:https://su.58.com/qztech/ 右键查看源码,
我们发现,文件的字体格式是woff形式的,所以这里我们把文件写成.woff格式就好了
那我们在进入https://sz.58.com/chuzu/这个页面。我们查看源码
我们发现,文件的字体格式是ttf形式的,所以这里我们把文件写成.ttf格式就好了
遗漏点二:
上一篇文章我们是在cmap,获取字符编码的,但并不是所有的都是这样,猫眼是把编码对象在glyf
所以我们还是要灵活的去分析xml文件,
遗漏点三:
当我们看到字体文件,每个字可以看到字形和字形编码。
观察现在箭头指的地方和前面箭头指的地方的数字是不是一样啊,没错,就是通过这种方法进行映射的。
所以我们现在的思路似乎就是在源代码里找到箭头指的数字,然后再来字体里找到后替换就行了。
恭喜你,如果你也是这么想的,那你就掉坑里了。
因为每次访问,字体字形是不变的,但字符的编码确是变化的。因此,我们需要根据每次访问,动态解析字体文件。
好了说完这两个遗漏点之后,进入到今天的主要内容,手动构建字体映射表:
由于 操作详细步骤在上篇文章,都详细做了介绍,在这篇文章只做分析过程
网页地址:https://su.58.com/qztech/
步骤(1):打开网页把字体文件保存下载,并且转换成xml格式
FontCreator打开58.woff文件:
重复步骤一:
打开58_2.woff文件:
对比两个文件,我们发现
因为每次访问,字体字形是不变的,但字符的编码确是变化的。因此,我们需要根据每次访问,动态解析字体文件。
到这里我们想通过写死字体哪种方式是不行了:
分析一下两个字体文件的对应的xml文件,搜索 “技”,对应的编码:
xml的格式如下:
文件很长,我只截取了一部分。
仔细的观察一下,你会发现~这俩下面的x,y,on值都是一毛一样的。所以我们的思路就是以一个已知的字体文件为基本,然后将获取到的新的字体文件的每个文字对应的x,y,on值进行比较,如果相同,那么说明新的文字对就 可以在基础字体那里找到对应的文字,有点绕,下面举个小例子。
假设: “我” 在基本字体中的名为uni1,对应的x=1,y=1,n=1新的字体文件中,一个名为uni2对应的x,y, n分别于上面的相等,那么这个时候就可以确定uni2 对应的文字为”我”。
查资料的时候,发现在特殊情况下,有时候两个字体中的文字对应的x,y不相等,但是差距都是在某一个阈值之内,处理方法差不多,只不过上面是相等,这种情况下就是要比较一下。
分析到这我么就可一构建字体映射表编写代码,爬取相关信息了
# coding=utf-8
import requests
import re
import time
import lxml.html as H
import base64
from fontTools.ttLib import TTFont
def get_data(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
session = requests.session()
con = session.get(url, headers=headers)
doc = H.document_fromstring(con.content)
font_data_origin = re.search(r'base64,(.*?)\)', con.text, re.S).group(1)
font_data_after_decode = base64.b64decode(font_data_origin)
new_font_name = "font_new.woff"
with open(new_font_name, 'wb') as f:
f.write(font_data_after_decode)
map_data = tff_parse(new_font_name)
names = doc.xpath('//span[@class="infocardName fl stonefont resumeName"]/text()')
# 有的时候会找不到,可以多执行几次;
if names:
for name in names:
print('name in page source', name)
for j in map_data.keys():
name = name.replace(j, map_data[j])
print ('name actual', name)
def tff_parse(font_parse_name):
# 我这里的字体的顺序,如果你的不同,一定要修改
font_dict = [u'博', u'经', u'硕', u'届', u'大', u'刘', u'8', u'1', u'士', u'E', u'2', u'6', u'张',
u'M', u'验', u'5', u'本', u'赵', u'陈', u'吴', u'李', u'生', u'4', u'校', u'以', u'应', u'黄',
u'技', u'无', u'女', u'A', u'周', u'中', u'3', u'王', u'7', u'0', u'9', u'科', u'高', u'男',
u'杨', u'专', u'下', u'B']
font_base = TTFont('font_base.ttf')
font_base_order = font_base.getGlyphOrder()[1:]
# font_base.saveXML('font_base.xml') 调试用
font_parse = TTFont(font_parse_name)
# font_parse.saveXML('font_parse_2.xml')调试用
font_parse_order = font_parse.getGlyphOrder()[1:]
f_base_flag = []
for i in font_base_order:
flags = font_base['glyf'][i].flags
f_base_flag.append(list(flags))
f_flag = []
for i in font_parse_order:
flags = font_parse['glyf'][i].flags
f_flag.append(list(flags))
result_dict = {}
for a, i in enumerate(f_base_flag):
for b, j in enumerate(f_flag):
if comp(i, j):
key = font_parse_order[b].replace('uni', '')
key = eval(r'u"\u' + str(key) + '"').lower()
result_dict[key] = font_dict[a]
return result_dict
def comp(L1, L2):
if len(L1) != len(L2):
return 0
for i in range(len(L2)):
if L1[i] == L2[i]:
pass
else:
return 0
return 1
if __name__ == '__main__':
url = "https://su.58.com/qztech/"
get_data(url)
反扒日系更新,以下两篇文章,推荐看一看
https://www.freebuf.com/articles/web/206966.html
https://www.jianshu.com/p/4d28dd440cdd