python随记

woff字体反反爬

2021-12-31  本文已影响0人  LCSan
背景某港务公司货箱查询,返回货箱信息是这样子的: 货箱号

分析上下文报文,返现同时有一个woff字体请求,显然这里做了字体映射。这里问题变成了如何解析woff成文字。

找了一下woff格式字体资料,通俗的讲有字体id,字体Unicode码,字模信息。三者互有映射关系,其中的Unicode就是字,字模信息会经过渲染,转换成人眼看到的字。
重点就在于字模,理论上来讲一套字体文件,字模是固定的。不管Unicode怎么变,最终映射的字模应该是一样的,除非显示出来的一个字有多套字模(这么做开发成本跟时间成本上肯定得作出让步,因此这里暂不考虑)。即使一个字有多套字模,那这也是可以枚举出来的。
基于这点,只需要将一套完整的字模跟字的映射表做好,只要字模不变,就可以通过Unicode映射字模,再通过字模映射字符,得到最终结果。

  1. 先下载一个woff字体,解析导出字模跟Unicode信息。(注意:这里从cmap里面取Unicode,踢出了非字体的码。)
def CreateFontDict(woffPath, jPath):
    font = TTFont(woffPath)
    res = {}
    for k in font.getBestCmap().values():
        res[hashlib.md5(str(font["glyf"][k].coordinates).encode('utf-8')).hexdigest()] = k
    with open(jPath, 'w', encoding='utf-8') as f:
        f.write(json.dumps(res))

导出json格式文件后,人工将Unicode码替换成对应字符。
这个网站,现在查看字库映射,用来改json里面的value为映射字符:
http://blog.luckly-mjw.cn/tool-show/iconfont-preview/index.html

  1. 加载人工处理后的json映射文件
def loadFontDict(jPath):
    global jMap
    with open(jPath, encoding='utf-8') as f:
        json_str = f.read()
        jMap = json.loads(json_str)
  1. 加载字模文件,对文本进行解析。(注意:要将原始字符串里面要替换的字符串格式,转为json输出时value的格式)
def parseFont(woffPath, text, coding="utf-8"):
    global jMap
    font = TTFont(woffPath)
    for k in font.getBestCmap().values():
        if k.startswith("uni"):
            r = jMap[hashlib.md5(str(font["glyf"][k].coordinates).encode('utf-8')).hexdigest()]
            t = k.replace("uni", r"\u").lower()
            t = t.encode(coding).decode("unicode_escape")
            text = text.replace(t, r)
    return text

完整代码:

from fontTools.ttLib import TTFont
import hashlib
import json
import re

jMap = None


def CreateFontDict(woffPath, jPath):
    font = TTFont(woffPath)
    res = {}
    for k in font.getBestCmap().values():
        res[hashlib.md5(str(font["glyf"][k].coordinates).encode(
            'utf-8')).hexdigest()] = k
    with open(jPath, 'w', encoding='utf-8') as f:
        f.write(json.dumps(res))


def parseFont(woffPath, text, coding="utf-8"):
    global jMap
    text = _handleTxt(text)
    font = TTFont(woffPath)
    for k in font.getBestCmap().values():
        if k.startswith("uni"):
            r = jMap[hashlib.md5(str(font["glyf"][k].coordinates).encode('utf-8')).hexdigest()]
            t = k.replace("uni", r"\u").lower()
            t = t.encode(coding).decode("unicode_escape")
            text = text.replace(t, r)
    return text


def loadFontDict(jPath):
    global jMap
    with open(jPath, encoding='utf-8') as f:
        json_str = f.read()
        jMap = json.loads(json_str)


def _replace(reg, exp, come):
    # re.MULTILINE | re.DOTALL
    return re.sub(re.compile(reg, 8 | 16), (exp if exp.find('lambda') == -1 else eval(exp, globals(), locals())), come)


def _handleTxt(text):
    text = _replace("&#x([0-9a-f]{4});", "lambda x: '\\\\u'+x.group(1)", text)
    text = text.replace('"', r'\"')
    text = '"' + text + '"'
    text = json.loads(text, strict=False)
    return text


if __name__ == "__main__":
    # CreateFontDict(r"C:\Users\44413\Documents\UiBot\creator\Projects\抖音商家数据采集\res\aa.ttf",
    #                r"C:\Users\44413\Documents\UiBot\creator\Projects\抖音商家数据采集\res\data1.json")

    # loadFontDict(r"C:\Users\44413\Documents\UiBot\creator\Projects\抖音商家数据采集\res\data.json")
    # with open(r"C:\Users\44413\Desktop\新文件 2.txt", encoding='utf-8') as f:
    #     json_str = f.read()
    #     a = parseFont(
    #         r"C:\Users\44413\Downloads\c54f43be76094bd1ae121adfb18f3bcf-ebe761e0c99f4b289029c3634f3516e3.ttf", json_str)
    #     print(a)
    #     a = '''<div class="infocardBasic fl">                         
    # <div class="clearfix">
    # <em class="stonefont">&#xe3d1;</em>
    # <span>|</span>
    # <em class="stonefont">&#xf4ee;&#xe238;岁</em>
    # <em class="stonefont">&#xf4ee;年&#xefaf;&#xf657;</em>
    # <em class="stonefont">&#xf81a;&#xea4c;</em>
    # <span class="phoNum">1图</span>
    # </div>
    # </div>'''
    a = '''<div class="clearfix"><em class="stonefont"></em><span>|</span><em class="stonefont">岁</em><span>|</span><!-- TODO --><em class="stonefont">-年</em><span>|</span><!-- TODO --><em class="stonefont">/</em></div>'''
    loadFontDict(r"C:\Users\44413\Documents\UiBot\creator\Projects\抖音商家数据采集\res\data.json")
    a = parseFont(r"C:\Users\44413\Documents\UiBot\creator\Projects\抖音商家数据采集\res\aa.woff",a)
    print(a)

记录TTFont几个有用方法

# 取字模
font['glyf']['uniF378'].coordinates
# 十进制,unicode映射
font.getBestCmap()
# id,unicode映射
font.getGlyphOrder()
上一篇 下一篇

猜你喜欢

热点阅读