一、字体反爬的概述
目前字体反爬的网站是猫眼,汽车之家,天眼查,起点中文网,58同城等等。还有:https://www.shixiseng.com/
- 字体反爬是一种反爬的手段,它是通过web设计师利用CSS3的自定义字体的新特性来自定义的字体(在浏览器的源代码中是看不到正正的字,因为字是由乱码形式出现的)。
- 当浏览器会下载字体信息,然后动态渲染,在html页面源码中,你看到的不再是正常字符或者是unicode,而是网站使用的自定义编码形式显示的字体。
二、字体反爬出现的现象举例
访问网址:https://bj.58.com/qzyewu/?PGTID=0d202409-0000-1aa8-92da-777b90a7dc73&ClickID=1
-
使用谷歌浏览器访问58人才网,可以看到页面正常显示的文字,在开发者调试模式中是不能正常显示的。
-
查看源码,会发现它是类似unicode的编码。
三、自定义字体及其复原
-
查看元素的css,果然使用了自定义的字体样式。
-
左键点击字体引用,发现字体信息就在当前页面的style标签中,经过观察发现是一个base64编码的数据。
- 创建字体文件,字体文件中是二进制数据。复制在源代码中的base64编码数据,转换成二进制数据,解码后写入字体文件中。
def make_font_file(base64_string):
"""
创建字体文件(new.ttf)
:param base64_string: 页码base编码数据
:return: 二进制数据
"""
bin_data = base64.decodebytes(base64_string.encode())
with open('new.ttf', 'wb') as f:
f.write(bin_data)
return bin_data
- 打开及处理字体文件,需要用到FontCreator软件工具下载地址。可以利用它来打开ttf字体文件,查看每个字符对应的编码。
-
打开后可以看到“生”的编码,跟html源码中的编码一致(字符编码为ea35对应”生“)。
四、字体文件分析
- 通过多次请求页面观察发现字体文件中的字体和字符编码都没变,变的只是它们之间的对应关系(即字符编码对应的字体不同而已)
- 对于分析字体文件,需要使用到一个专门处理字符文件的python第三方库fontTools库,利用它可以将字体文件转换成xml文件进行分析。
- 安装:
pip install fonttools
- 基本使用:
from fontTools.ttLib import TTFont
font = TTFont('new.ttf')
# 将解码的字体文件保存为xm格式
font.saveXML("new.xml")
font = TTFont('old.ttf')
# 将解码的字体文件保存为xm格式
font.saveXML("old.xml")
-
打开这个xml文件,它的结构如下
- 这里我们主要使用到
GlyphOrder
和glyf
两个元素节点,其中GlyphOrder
是编码序号表,glyf
是图元数据,也就是字体轮廓定义。
注意:发现虽然字符编码会不停的变换,但是每个字的图元是固定不变的,也即是字体形状是不变的。所以可以通过比较图元信息来判断两个编码是否表示同一个字符。
- 如下可得知不同的字体文件中,当不同的字符编码对应相同字体时,字体对应的坐标是相同的。
(1)old.xml中uniF773
字符编码对应的字体1。uniE6D4
字符编码对应的字体1。
(4)代码示例:
import base64
from fontTools.ttLib import TTFont
def make_font_file(base64_string):
bin_data = base64.decodebytes(base64_string.encode())
with open('new.ttf', 'wb') as f:
f.write(bin_data)
return bin_data
# 你看看你自己写的代码
# old 里用了new.ttf
old_font = TTFont('old.ttf')
new_font = TTFont('new.ttf')
# 为啥要这么写?它不能迭代
# for i in old_font['glyf']:
# print(i)
#old_font['glyf']是不可以迭代的
print(old_font.getGlyphOrder())
print(old_font['glyf']['uniF773'] == new_font['glyf']['uniE6D4'])
五、字体反爬的解决步骤
经过上面的分析总结如下步骤:
- 首先下载一个字体文件作为基准,根据这个文件生成一个基准的编码和文字的映射。
- 访问页面,拿到字体数据
- 解码字体数据,生成字体文件
- 根据已有的基准字体文件和映射生成新的编码文字映射
主要是把新生成的new.xml文件中每个字符编码对应的字体详细信息取出来,再到基准字体文件中的每个字符编码对应的字体详细信息进行对比,如果发现相同,就把基准字体文件中字符编码对应的字体作为(映射)新生成字体文件中字符编码所对应的值。
- 根据已有的基准字体文件和映射生成新的编码文字映射
- 替换数据中的编码,形成新的表。