字体反爬

在没有什么css的影响下,html上显示 != 常人页面上显示的,应该是字体反爬。

获取自定义字体库的url/文件

font-family这个关键字,利用正则匹配出来,下载下来

字体文件使用 Font Creator 查看

解析字体

小解fonttools的xml

fonttools

1
2
3
4
from fontTools.ttLib import TTFont
font = TTFont('/path/to/font.ttf')
# 保存xml
font.saveXML('test.xml')

font name

  • xml

GlyphOrder.GlyphID['name]就是html里面显示的样子 => font name

  • code

font.getGlyphOrder() 按序获取,按照需要切片

font info

  • xml

glyf.TTGlyph.contour font name轮廓坐标应该是独一无二font name一一对应

  • code

font['glyf'][gly_key].coordinates

解析原理

明确一些关系 两个字体库 base 已知所有映射关系的字体库 求 新字体库的映射关系

  • base字体库(base Font library bfl) 页面显示字符(F) => base font name(fb) => base坐标(coor)
  • 由于字体库通常是动态的我们会获得 new字体库(nbl) 页面显示字符(F) => new font name(fn) => base坐标(coor)

基本思路 利用font name轮廓坐标一致,通过旧映射bfl获取新映射nbl的关系 目标是 fn => F

  • 通过页面明确F => fb的映射关系 (这是最蛋疼的一步,这步错了后面就完了,当字体库庞大各种花里胡哨,可能很难找齐对应关系)
  • 解析bfl获取 fb => coor 反推 coor => F的关系A
  • 解析nfl获取 fn=>coor映射关系B
  • 通过 关系A、B 得到 fn => coor => F 的转换 解析出页面字体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def get_base_flag(name):
    # html real
    base_dict = {
        'two': '1',
        'three': '5',
        'one': '0',
        'nine': '6',
        'seven': '2',
        'eight': '7',
        'six': '8',
        'five': '3',
        'zero': '4',
        'four': '9',
    }
    font_base = get_font(name)
    base_gly = font_base.getGlyphOrder()[1:-1]

    base_flag = {}
    for gly_key in base_gly:
        flags = font_base['glyf'][gly_key].coordinates
        flags = ''.join(map(str, list(flags)))
        base_flag[flags] = {'html': gly_key, 'real': base_dict[gly_key]}

    return base_flag


def get_flag(name, base_flag):
    font = get_font(name)
    gly_list = font.getGlyphOrder()[1:-1]

    flag = {}
    for gly_key in gly_list:
        flags = font['glyf'][gly_key].coordinates
        flags = ''.join(map(str, list(flags)))

        flag[gly_key] = base_flag[flags]['real']

    return flag