【首发字体反爬】猫X眼YingShi,我们又来欺负你了,用到了 OCR 识别技术
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情。
📢📢📢📢📢📢 💗 你正在阅读 【梦想橡皮擦】 的博客
⛳️ Python 反爬实战场景
本篇博客开始,我们正式进入字体反爬的战场,今天的目标站点是猫眼,一个很经典的字体实例,案例仅供学习使用,请勿用于非法用途。
进入网站首页之后,随机选择一部影视作品,进入详情页。
在页面的响应中可以看到,数字相关信息无法直接获取。
如果在爬虫分析逻辑中,出现上述内容,都属于字体反爬类站点。
我们在用 Element
抓取一下元素内容,可看到目标数据引用了一个特殊的字体样式 mtsi-font
在网页源码中检索该文本,得到如下内容:
js
@font-face{font-family: "mtsi-font";src:url("//s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/e5389ac6.eot");src:url("//s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/e5389ac6.eot?#iefix") format("embedded-opentype"),url("//s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/e5389ac6.woff");}
.stonefont {
font-family: mtsi-font;
}
这里面包含了 2 个字体文件,后缀名依次是 .eot
,.woff
,后续突破点也在这里,同时发现了美 xxx 团相关信息,看来美 xxx 团系的站点,都有类似的字体反爬,后续可以研究下。
优先选择 .woff
文件下载分析,还文件的名字应该是动态的,后续爬虫的编写还需要动态获取该文件名。
woff 全名叫 Web 开发字体格式(Web Open Font Format),是一种网页字体格式标准。
下载文件之后,可以使用 FontCreator
软件打开该文件。
直接拖拽字体文件到软件中,即可进行查阅,得到如下结果。
请注意其中的编码和数字的对应关系,后续我们就是在 Python 中解析这些关系,从而获取文字信息的。
在查阅刚才字体加密的文件,得到下图所示内容。
上面的代码 <span class="stonefont">.</span>
就是要解除反爬的位置,其中

主要看后 4 位,两张图片对比就可以知道,这个值对应的字体编码是 uniE36C
,即数字 9 ,所以在后续编写字体解密的代码时,需要将获取的文字编码转换成字体编码。

- >uniE36C
接下来我们使用 Python 中的模块读取该字体,尝试获取字体编码内容。
安装 fontTools
模块,用于解析字体文件。
python
pip install fontTools -i https://pypi.tuna.tsinghua.edu.cn/simple
手动下载 2 个字体文件,然后运行下述代码:
```python from fontTools.ttLib import TTFont
TTFont('03337c30.woff').saveXML('font1.xml') TTFont('e5389ac6.woff').saveXML('font2.xml') ```
得到 2 个 XML 文件,其中包含字体解析之后的数据。
这里是字体编号的缩写,字体具体的绘制内容在代码下侧,对比一下可以发现如下内容。
对比两个文件中关于数字 3 的编码,可以看到出现了细节差异,此时得到的一个结论就是,MAOYAN 除了字体编码变化外,字体渲染的坐标也发生了变化。
接下来完成本案例的第一步,下载一个参考字体文件,例如上文得到的内容。
然后建立一个字典,用于创建数字与编码的对应关系。
python
font_dict = {
'uniE415': '3',
'uniF41A': '8',
'uniF078': '7',
'uniE5BF': '0',
'uniE36C': '9',
'uniF8DF': '1',
'uniE5A5': '6',
'uniEF4D': '4',
'uniE6E0': '2',
'uniED3D': '5',
}
下面编写数字提取代码即可。
```python from fontTools.ttLib import TTFont
base_font = TTFont('03337c30.woff') print(base_font)
base_list = base_font.getGlyphOrder()[2:]
print(base_list)
假设抓取到的评分字符串如下所示
font_str = ''
font_list = font_str.split(';')[:-1] font_list = ['uni' + _[3:].upper() for _ in font_list]
font_dict = { 'uniE415': '3', 'uniF41A': '8', 'uniF078': '7', 'uniE5BF': '0', 'uniE36C': '9', 'uniF8DF': '1', 'uniE5A5': '6', 'uniEF4D': '4', 'uniE6E0': '2', 'uniED3D': '5', }
real_numm = [font_dict[f] for f in font_list] print(real_numm)
```
输出内容为 ['2', '0', '3', '7', '7']
,该内容可以与网页显示的数据对应上,表示基础的解析已经完成,如果网页中字体文件编码无变化,此时我们已经了问题,但是猫眼难度要高 2 个级别,它每次刷新页面都发生了变更,自然编码逻辑难度要大很多。
接下来就进入数字的识别环节,由于猫眼的字体文件动态加载,并且编码和顺序都是会变的,所以我们采用 OCR 图片识别技术。
⛳️ 数字识别实战场景
测试前下载一个 woff
文件,然后将其转换为 ttf
文件,后续通过该文件进行渲染。
字体文件格式转换
```python from fontTools.ttLib import TTFont from fontTools.ttLib.woff2 import decompress
目标站点下载的字体文件
woff_path = "./03337c30.woff"
ttf 格式文件
ttf_path = './font.ttf'
将 woff文件转成 ttf 文件
decompress(woff_path, ttf_path)
font = TTFont(ttf_path)
print(font) ```
运行代码之后,得到 ttf 文件。
接下来使用 PIL 模块绘制一张字体图片。
绘制一张空图片
```python
图片宽度和高度
img_size = 512
实例化一个图片对象
img = Image.new('1', (img_size, img_size), 255)
绘制图片
draw = ImageDraw.Draw(img) img.show() ```
解析字体文件中的编码对应关系。
```python font = TTFont('./font.ttf')
for cmap_code, glyph_name in font.getBestCmap().items(): print(cmap_code,glyph_name) ```
代码运行之后,得到对应关系如下所示
txt
120 x
58220 uniE36C
58389 uniE415
58789 uniE5A5
58815 uniE5BF
59104 uniE6E0
60733 uniED3D
61261 uniEF4D
61560 uniF078
62490 uniF41A
63711 uniF8DF
此时又发现了 uni
开头的编码,而且前面出现了一堆数字,接下来就将这个数字进行绘制。
```python from fontTools.ttLib import TTFont from fontTools.ttLib.woff2 import decompress from PIL import ImageFont, Image, ImageDraw
图片宽度和高度
img_size = 512
font = TTFont('./font.ttf') font_img = ImageFont.truetype('./font.ttf', img_size)
for cmap_code, glyph_name in font.getBestCmap().items(): # print(cmap_code,glyph_name)
# 实例化一个图片对象
img = Image.new('1', (img_size, img_size), 255)
# 绘制图片
draw = ImageDraw.Draw(img)
# 将编码读取成字节
txt = chr(cmap_code)
x, y = draw.textsize(txt, font=font_img)
draw.text(((img_size - x) // 2, (img_size - y) // 2), txt, font=font_img, fill=0)
img.show()
```
运行上述代码,程序会依次展示各种绘制数字的图片,后续我们使用 ddddocr
识别即可完成。
接下来就是识别部分的代码了,将数字生成的二进制字节流读取到程序中,直接进行识别。
python
bytes_io = BytesIO()
img.save(bytes_io, format="PNG")
word = ocr.classification(bytes_io.getvalue()) # 识别字体
print(word)
完整代码如下所示:
```python from fontTools.ttLib import TTFont from fontTools.ttLib.woff2 import decompress from PIL import ImageFont, Image, ImageDraw from io import BytesIO import ddddocr """
目标站点下载的字体文件
woff_path = "./03337c30.woff"
ttf 格式文件
ttf_path = './font.ttf'
将 woff文件转成 ttf 文件
decompress(woff_path, ttf_path)
font = TTFont(ttf_path) """
图片宽度和高度
img_size = 512
font = TTFont('./font.ttf') font_img = ImageFont.truetype('./font.ttf', img_size) ocr = ddddocr.DdddOcr() for cmap_code, glyph_name in font.getBestCmap().items(): # print(cmap_code,glyph_name)
# 实例化一个图片对象
img = Image.new('1', (img_size, img_size), 255)
# 绘制图片
draw = ImageDraw.Draw(img)
# 将编码读取成字节
txt = chr(cmap_code)
x, y = draw.textsize(txt, font=font_img)
draw.text(((img_size - x) // 2, (img_size - y) // 2), txt, font=font_img, fill=0)
bytes_io = BytesIO()
img.save(bytes_io, format="PNG")
word = ocr.classification(bytes_io.getvalue()) # 识别字体
print(cmap_code, glyph_name, word)
```
运行之后,得到如下输出内容
txt
2 extra bytes in post.stringData array
120 x
58220 uniE36C 9
58389 uniE415 3
58789 uniE5A5 6
58815 uniE5BF 0
59104 uniE6E0 2
60733 uniED3D 5
61261 uniEF4D 4
61560 uniF078 7
62490 uniF41A 8
63711 uniF8DF 1
此时在结合上文,将两部分代码(第一部分是将 
转换为 uni编码
)集成到一起 ,则完成猫眼的字体反爬识别案例。
再次去猫眼随机获取一个字体文件,成功解析如下结果,本次反爬案例成功解决。
📣📣📣📣📣📣 右下角有个大拇指,点赞的漂亮加倍
- 嗨,各位Python程序员,放弃selenium,试试年轻的Playwright如何?
- 09年的老电脑说想看一下CPU温度,作为Python工程师,怎能安装X大师?
- 如果你需要用Python搞个二维码,那应该收藏这篇博客
- 写给Python社群的第2课:Python逻辑语句,天天要写的 if 和 while
- 嗨,程序员,你知道高级工程师用的搜索引擎吗?
- 都2022年了,Python Web框架你不会只知道Django和Flask吧?
- 在座的Python爬虫工程师,你敢爬律师事务所站点吗?
- Python爬虫反爬,你应该从这篇博客开启,UA反爬,Cookie 特定参数反爬
- OpenGauss数据库在 CentOS 上的实践,配置篇
- 【Python技能树共建】动态渲染页面爬取
- 【Python技能树共建】Beautiful Soup
- 【首发字体反爬】猫X眼YingShi,我们又来欺负你了,用到了 OCR 识别技术
- 【首发】某易跟帖篇频道,接口溯源分析,反爬新技巧,必掌握一下
- 【首发】Python反爬,JS反爬串讲,从MAOX眼X开始,本文优先解决反爬参数 signKey
- 【首发】写Python爬虫,服务器返回数据加密了,套路解决法~,出版社,出版社
- 虎牙直播数据采集,为数据分析做储备,Python爬虫120例之第24例
- 我们的骄傲!非遗数据采集,来自官方的数据,Python爬虫无所不爬
- 3天掌握Flask开发项目系列博客之二,操作数据库
- 写给Python社群的第1课:初识Python,开篇知识点就选字符串吧~
- 升级【爸爸程序员】了?用Python给自己的宝贝下载200 绘本动画吧!