写点什么

【字体反爬】猫 X 眼 YingShi,我们又来欺负你了,用到了 OCR 识别技术

作者:梦想橡皮擦
  • 2022 年 7 月 20 日
  • 本文字数:3768 字

    阅读完需:约 12 分钟

📢📢📢📢📢📢💗 你正在阅读 【梦想橡皮擦】 的博客👍 阅读完毕,可以点点小手赞一下🌻 发现错误,直接评论区中指正吧📆 橡皮擦的第 <font color=red>648</font> 篇原创博客


⛳️ Python 反爬实战场景

本篇博客开始,我们正式进入字体反爬的战场,今天的目标站点是猫眼,一个很经典的字体实例,案例仅供学习使用,请勿用于非法用途。


进入网站首页之后,随机选择一部影视作品,进入详情页。


在页面的响应中可以看到,数字相关信息无法直接获取。



如果在爬虫分析逻辑中,出现上述内容,都属于字体反爬类站点。


我们在用 Element 抓取一下元素内容,可看到目标数据引用了一个特殊的字体样式 mtsi-font



在网页源码中检索该文本,得到如下内容:


@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">&#xe36c;.&#xf8df;</span> 就是要解除反爬的位置,其中


&#xe36c; 主要看后 4 位,两张图片对比就可以知道,这个值对应的字体编码是 uniE36C,即数字 9 ,所以在后续编写字体解密的代码时,需要将获取的文字编码转换成字体编码。


&#xe36c; - > uniE36C


接下来我们使用 Python 中的模块读取该字体,尝试获取字体编码内容。


安装 fontTools 模块,用于解析字体文件。


pip install fontTools -i https://pypi.tuna.tsinghua.edu.cn/simple
复制代码


手动下载 2 个字体文件,然后运行下述代码:


from fontTools.ttLib import TTFont
TTFont('03337c30.woff').saveXML('font1.xml')TTFont('e5389ac6.woff').saveXML('font2.xml')
复制代码


得到 2 个 XML 文件,其中包含字体解析之后的数据。



这里是字体编号的缩写,字体具体的绘制内容在代码下侧,对比一下可以发现如下内容。


对比两个文件中关于数字 3 的编码,可以看到出现了细节差异,此时得到的一个结论就是,MAOYAN 除了字体编码变化外,字体渲染的坐标也发生了变化。



接下来完成本案例的第一步,下载一个参考字体文件,例如上文得到的内容。



然后建立一个字典,用于创建数字与编码的对应关系。


font_dict = {        'uniE415': '3',        'uniF41A': '8',        'uniF078': '7',        'uniE5BF': '0',        'uniE36C': '9',        'uniF8DF': '1',        'uniE5A5': '6',        'uniEF4D': '4',        'uniE6E0': '2',        'uniED3D': '5',}
复制代码


下面编写数字提取代码即可。


from fontTools.ttLib import TTFont
base_font = TTFont('03337c30.woff')print(base_font)
base_list = base_font.getGlyphOrder()[2:]
print(base_list)
# 假设抓取到的评分字符串如下所示# &#xe6e0;&#xe5bf;&#xe415;&#xf078;&#xf078;font_str = '&#xe6e0;&#xe5bf;&#xe415;&#xf078;&#xf078;'
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 文件,后续通过该文件进行渲染。


字体文件格式转换


from fontTools.ttLib import TTFontfrom 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 模块绘制一张字体图片。


绘制一张空图片


# 图片宽度和高度img_size = 512# 实例化一个图片对象img = Image.new('1', (img_size, img_size), 255)# 绘制图片draw = ImageDraw.Draw(img)img.show()
复制代码


![【字体反爬】猫 X 眼 YingShi,我们又来欺负你了,用到了 OCR 识别技术](https://img-blog.csdnimg.cn/51f39570ff9040c59e3c003068a9b431.png =200x)


解析字体文件中的编码对应关系。


font = TTFont('./font.ttf')
for cmap_code, glyph_name in font.getBestCmap().items(): print(cmap_code,glyph_name)
复制代码


代码运行之后,得到对应关系如下所示


120 x58220 uniE36C58389 uniE41558789 uniE5A558815 uniE5BF59104 uniE6E060733 uniED3D61261 uniEF4D61560 uniF07862490 uniF41A63711 uniF8DF
复制代码


此时又发现了 uni 开头的编码,而且前面出现了一堆数字,接下来就将这个数字进行绘制。


from fontTools.ttLib import TTFontfrom fontTools.ttLib.woff2 import decompressfrom 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 识别即可完成。


接下来就是识别部分的代码了,将数字生成的二进制字节流读取到程序中,直接进行识别。


bytes_io = BytesIO()img.save(bytes_io, format="PNG")word = ocr.classification(bytes_io.getvalue())  # 识别字体print(word)
复制代码


完整代码如下所示:


from fontTools.ttLib import TTFontfrom fontTools.ttLib.woff2 import decompressfrom PIL import ImageFont, Image, ImageDrawfrom io import BytesIOimport 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)
复制代码


运行之后,得到如下输出内容


2 extra bytes in post.stringData array120 x58220 uniE36C 958389 uniE415 358789 uniE5A5 658815 uniE5BF 059104 uniE6E0 260733 uniED3D 561261 uniEF4D 461560 uniF078 762490 uniF41A 863711 uniF8DF 1
复制代码


此时在结合上文,将两部分代码(第一部分是将 &#xe6e0; 转换为 uni编码 )集成到一起 ,则完成猫眼的字体反爬识别案例。


再次去猫眼随机获取一个字体文件,成功解析如下结果,本次反爬案例成功解决。



📣📣📣📣📣📣右下角有个大拇指,点赞的漂亮加倍


欢迎大家订阅专栏:⭐️ ⭐️ 《Python 爬虫 120》⭐️ ⭐️

发布于: 刚刚阅读数: 3
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
【字体反爬】猫X眼YingShi,我们又来欺负你了,用到了 OCR 识别技术_Python_梦想橡皮擦_InfoQ写作社区