字体反爬(十八)

一、字体反爬的概述

目前字体反爬的网站是猫眼,汽车之家,天眼查,起点中文网,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文件,它的结构如下
  • 这里我们主要使用到GlyphOrderglyf两个元素节点,其中GlyphOrder是编码序号表,glyf是图元数据,也就是字体轮廓定义。

注意:发现虽然字符编码会不停的变换,但是每个字的图元是固定不变的,也即是字体形状是不变的。所以可以通过比较图元信息来判断两个编码是否表示同一个字符。

  • 如下可得知不同的字体文件中,当不同的字符编码对应相同字体时,字体对应的坐标是相同的。
    (1)old.xml中uniF773字符编码对应的字体1。
    (2)new.xml中uniE6D4字符编码对应的字体1。
    (3)所以可得知当字体相同时,其标签显示的坐标是一样的。还有xml文件和html文件一样可以通过标签取到相应的元素。
    (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'])

五、字体反爬的解决步骤

经过上面的分析总结如下步骤:

    1. 首先下载一个字体文件作为基准,根据这个文件生成一个基准的编码和文字的映射。
    1. 访问页面,拿到字体数据
    1. 解码字体数据,生成字体文件
    1. 根据已有的基准字体文件和映射生成新的编码文字映射
      主要是把新生成的new.xml文件中每个字符编码对应的字体详细信息取出来,再到基准字体文件中的每个字符编码对应的字体详细信息进行对比,如果发现相同,就把基准字体文件中字符编码对应的字体作为(映射)新生成字体文件中字符编码所对应的值。
    1. 替换数据中的编码,形成新的表。

你可能感兴趣的:(字体反爬(十八))