用fontTools解析WOFF提取cmap表最稳定;需先判别WOFF/WOFF2格式,WOFF2须解包;加载时加ignoreDecompileErrors=True;getBestCmap()返回{codepoint: glyph_name}字典,codepoint转chr()得真实字符。

直接用 fontTools 解析 WOFF 文件并提取 cmap 表,是目前最稳定、可复现的方案;别信“正则硬扒二进制”或“手动查 XML”,效率低还容易漏字形。
怎么加载woff文件不报错:区分 woff 和 woff2 格式
很多初学者卡在第一步:TTFont("xxx.woff") 报 Unsupported sfnt version。这不是代码写错了,而是你拿到的其实是 .woff2 文件——它结构不同,fontTools 原生不支持直接加载。
- 先用命令行快速判断:
file xxx.woff(Linux/macOS)或看十六进制头:woff 以woff字符串开头,woff2 是wOFF(注意大小写) - woff2 必须先解包:装
pip install woff2,再用woff2_decompress xxx.woff2得到 ttf/woff,再交给TTFont - 加载时加
ignoreDecompileErrors=True参数,避免个别表损坏导致整个解析失败
重点只看 cmap 表:getBestCmap() 返回什么
cmap 表才是字符映射的核心,其他如 glyf(轮廓)、loca(位置索引)对还原文字没用。调用 ttf['cmap'].getBestCmap() 会自动选平台最兼容的子表(通常是 platformID=3, encodingID=1,即 Windows Unicode BMP),返回一个 {codepoint: glyph_name} 字典。
- 返回的
codepoint是十进制整数,比如38451对应 UnicodeU+9633 -
glyph_name常见格式有uni9633、uniE800、glyph00123;前两者可直接转 Unicode 字符:chr(38451)→'三' - 如果返回的是
glyph00123这类无意义名,就得结合ttf.getGlyphOrder()查顺序,再对齐网页中字符出现顺序(常见于静态字体反爬)
网页里那个 “$9633” 怎么对应上真实字
这个 33 不是字体里的原始编码,而是网站前端 JS 渲染时用的占位符写法,本质是把 Unicode 码点 0x9633 十六进制转成字符串拼上去的。你不需要去匹配 $ 符号,而是把 HTML 中所有类似 阳、\u9633、或直接 UTF-8 编码的乱码字符,统一转成十进制整数,再去查 cmap 映射字典。
- Python 中安全转换:
int('9633', 16)→38451,再chr(38451) - 注意网页源码里可能是 HTML 实体:
阳(十进制)或阳(十六进制),用html.unescape()预处理后再提取数字 - 如果字体用了多套 cmap 子表(比如同时支持 Mac 和 Windows),
getBestCmap()可能选错;这时要手动遍历:for t in ttf['cmap'].tables: if t.platformID == 3 and t.encodingID == 1: mapping = t.cmap
动态字体怎么办:每次请求换 woff 文件
静态字体只需解析一次映射;但像某招聘网、采购平台这类,每次刷新就换 woff URL,且字形映射关系也变——这时候不能硬记字典,得把解析逻辑封装成函数,在每次请求后自动下载新字体、重新建映射。
- woff 文件通常藏在 CSS 的
@font-face规则里,用re.findall(r'url\("([^"]+\.woff)"\)', css_text)提取最稳 - 下载后别存本地再读,直接用
TTFont(io.BytesIO(response.content))内存解析,避免临时文件权限/路径问题 - 映射字典建议缓存到内存(如全局 dict),键用字体文件的 MD5 值,避免重复解析同一份字体
真正麻烦的不是解析本身,而是字体开发者故意把 cmap 表做空、或只填部分映射,逼你去比对 name 表里的字体家族名、或用 Pillow 渲染字形再 OCR——这些属于兜底手段,不到万不得已别碰。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











