大家好,我是崔庆才。
今天给大家分享我的一位朋友写的文章,这篇文章利用了相似度算法来进行了字体反爬相关分析,解决思路比较新颖,推荐大家一看,希望对大家有帮助~
如下是正文。
一、什么是字体反爬?
前言:字体反爬就是将关键性数据对应于其他Unicode编码,浏览器使用该页面自带的字体文件加载关键性数据正常显示,而当我们将数据进行复制粘贴、爬取操作时,使用的还是标准的Unicode字符映射,解析后就是干扰性数据。简单的说,字体反爬指的就是浏览器页面上的字符和调试窗口或者源码中的内容,显示的不一样,这就是字体反爬!
二、字体反爬案例分析
1、首先打开我们本次分析的网站,搜索指定内容,截图如下所示:
说明:当我看到这个搜索结果时,脑子里一片空白,说实话这类型的反爬手段我还是第一次遇到。但是丝毫不慌,更加激发了我的兴趣,我喜欢干有挑战的事情!
2、搜索当前截图的class属性stonefont,定位到指定代码块源码,截图如下:
3、分析当前代码块片段,发现一个关键url,截图如下所示:
说明:初步怀疑这个文件定义了相关字体的编码格式,为了验证我的想法,我特意在网上搜了一下,发现很多字体反爬的网站使用的都是这种技术。我还特意查看了一下网上对当前网站的分析,发现都是很多年前的文章了。这正好激发了我的兴趣,探索一下当前新版本的加密手段到底有没有那么强。
4、为了便于分析,接下来我们直接上代码,进行本地化讲解吧,代码如下所示:
# -*- coding: utf-8 -*-
# --------------------------------------
# @author : 公众号:逆向与爬虫的故事
# --------------------------------------
import re
from typing import Dict
import requests
def start_requests():
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Referer': '',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
}
params = {
'showType': '2',
'offset': '0',
}
proxy: Dict[str, str] = {
"http": "xxx",
"https": "xxx",
}
woff_url: str = "https://xxxx/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/{}"
response = requests.get('https://xxxxx/films', params=params, headers=headers, proxies=proxy)
woff_name = re.findall(r"/font/([A-Za-z0-9]{8}\.woff)", response.text)[0]
if woff_name:
with open("index.html", 'wb') as f:
f.write(response.content)
print("The index file was successfully written. Procedure")
with open(woff_name, 'wb') as b:
response = requests.get(woff_url.format(woff_name))
b.write(response.content)
print("The woff file was successfully written. Procedure")
if __name__ == '__main__':
start_requests()
说明:我先简单点写了一个demo函数,目的是为了下载网站首页和woff文件,进行本地化调试使用。
5、接下来对index文件、woff文件进行相关字体参数分析,打开index文件,截图如下:
用正则提取相关字段,然后与当前截图进行比对分析,代码如下:
items = re.findall(r'<span class="stonefont">(.*?)</span>', response.text)
for item in items:
print(item)
将上面的正则代码编辑好后运行截图如下:
说明:我们可以发现每一个类型的文字与index截图中的int数字相对应,此刻我心里在想,我们只需要把0-9这几个数字的类型对应的&#x编码找到不就解决问题了吗?但是我经过多次实验后,发现woff文件不是固定的,他会随机变动,也就是这样的方式根本行不通!接下来,我们去woff文件中找找,说不定能找到规律呢。
6、对woff文件进行特殊处理,查看文件编码信息,代码如下所示:
from fontTools.ttLib import TTFont
def process_woff():
font = TTFont('695b7b90.woff')
font.saveXML('695b7b90.xml')
使用上面代码处理woff格式的文件,将woff文件通过TTFont模块转为xml格式文件,打开xml文件,截图如下:
总结:本来我还想在xml文件中找找规律,但是xml中的id值与我们所需要的int值根本不对应,而且没有任何规律,调试多个文件后,脑袋更乱了。正当我疑惑时,看见了一个坐标轴,顿时来了灵感!接下来,开始放大招了。
7、使用matplotlib对woff文件中的坐标轴进行绘图,代码如下所示:
8、代码运行后截图如下所示:
总结:此刻我想到了一个好方法,那就是把编码坐标轴绘图的结果保存到本地,然后使用Tesseract第三方包去识别,接下来,让我们看看效果如何。
9、使用Tesseract识别图片中文字,代码截图如下所示:
代码运行后,效果如下所示:
总结:果然没有我想象的那么简单,这里只是把X、Y轴的坐标值匹配出来了,但是图片里的9根本匹配不到,也就是说Tesseract不适用于这类数据匹配。不过也给了我们有用的信息,X和Y坐标如果变动,会干扰图片的相似度匹配。我们需要把X、Y轴里的数字在保存图片的时候去掉。接下来就进入新的步骤分析了,相似度匹配分析。
三、图片相似度匹配(方案一)
1、使用imagehash均值哈希AHash算法对png图片进行图片指纹计算,代码如下:
2、下载不同的woff文件并对字体加密编码坐标绘图后,使用imagehash计算hash指纹如下所示:
总结:分析上面两张截图,我们发现20张照片的hash指纹,有相同的结果,也有不同的结果。得出结论:通过图片hash指纹的方式去判断不准确,我们应该计算图片hash指纹的相似度阈值,这样效果应该不错。
3、先对10张图片进行人工打标签,判断出每一张图片对应的int值,截图如下所示:
4、构建代码,计算这10张图片的平均散列(不要转为字符串类型),代码如下所示:
代码运行后计算的图片hash散列如下:
说明:每一个hash散列是由Resize=>8*8的二维数组组合而成的,并且数组内容只能是True、False,也就是0、1。
5、构建函数,计算出两张图片的相似度阈值,设置最大阈值为3,截图如下:
说明:将新的图片计算平均hash散列与之前人工打好标签的hash散列值进行比较,如果阈值小于3,则表示他们极度相识。为了验证我们的算法效果,接下来进行实战演练。
6、设置好图片hash阈值代码后,接下来对需要转换的字段进行映射编写相关代码,截图如下:
7、此刻所有的代码基本已经构建完成,让我们运行起来看看效果:
打开指定网页,截图如下:
总结:观察上面两张截图,我们发现代码打印的结果和网站里的内容完全一致,此刻大功告成,我们使用imagehash相似度匹配计算阈值的方法,能够还原出经过编码后的真正字体了。接下来,让我们进入文本相似度还原字体加密环节吧!
四、文本相似度匹配(方案二)
1、使用imagehash均值哈希AHash算法对png图片进行图片指纹计算,代码如下:
def get_image_hash(self, img_file):
"""average_hash """
hash = imagehash.average_hash(Image.open(img_file))
return str(hash)
2、选择单个woff文件中的0-9图片,通过计算图片hash的方式,分别给每张图片的hash值打上标签数字,对应关系如下图所示:
3、构建代码,使用SequenceMatcher计算两张图片的hash字符串文字相识度阈值,截图如下:
说明:将新的图片计算图片平均hash值与之前人工打好标签的hash值进行文本相似度阈值计算,如果阈值大于90,则表示他们极度相识。为了验证我们的算法效果,接下来进行实战演练。为了不对之前的图片相似度数据造成干扰,我们直接请求第二页的数据。
4、相关代码构建完成后,启动文本相似度匹配算法功能,截图如下:
打开指定网页,截图如下:
总结:观察上面两张截图,我们发现代码打印的结果和网站里的内容完全一致,此刻大功告成,我们可以通过文本相似度匹配计算阈值的方法还原字体加密。到这里本次分享就结束了,感谢大家的耐心阅读!
五、心得分享和总结
通过本次案例分享,我发现有些技术点不是难,而是没接触过(hhh接触过也不一定会)。这也充分说明了要多读书、多学习,以便于遇到新的问题时能够从容应对。今天分享到这里就结束了,欢迎大家关注下期文章,我们不见不散⛽️
End
崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!
内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍
扫码购买
好文和朋友一起看~