一、引入
1.1、在一些网页中,往往会有这样的需求:防止用户通过爬虫手段快速批量获取需要显示但是又极为敏感的数据。
- 比如:电商网站的价格、文献资料、具有版权性质的图库文库等。
- 这时候往往可以通过 字体混淆 技术来达到一定的防范效果。
-
例如:(图片来自网络)
0adb84d965924fa18d2684bd14266283.png
1.2、原理解释
- 首先,字体混淆仅仅是增加了爬虫困难,通过恢复字体等手段依然能获得原始文本。
- 字体文件内部存在这一种字符与字形的对应关系,或者说就是字符unicode与字形编码的对应关系。我们只要把这个对应关系偏移或打乱,使用打乱后的字符+打乱后的字体文件,即可在web端不影响显示的情况下达到混淆效果。
二、实现代码
2.1、本代码实现了如下功能:
根据需要截取字体文件
修改字符与字形的关系映射
保存新字体文件和输出混淆后的字符串
-
python实现:
##### # Created by 王圣滔[贤圣] # Email: w778899wst@live.cn ##### from fontTools.ttLib import TTFont from fontTools import subset """ 根据字符需要裁剪 字体文件内字符-字形映射关系的修改 输出编辑过的字体文件+偏移后的文本 """ # 需要混淆的字符串 myStr = "今天你真好看" # 加载字体文件: font = TTFont('./ttf/Alibaba-PuHuiTi-Light.ttf') # 截取字符串内字符使用到的字体(为了减小体积) subsetter = subset.Subsetter() subsetter.populate(text=myStr) subsetter.subset(font) # 获得字符、字形映射 cmap = font.getBestCmap() print("--------------原始映射关系-----------------") print(cmap) # 修改映射关系,这里从文本的第一个字符开始,依次替换为A、B、C、D及往后字符的unicode # 实际项目中可以根据自己的喜好去编写混淆算法,这里仅做基本演示 baseCode = 0x0041 # 初始code newStr = "" # 新的偏移后文本 oldAndNewKeyMap = {} #存放修改前、修改后的key对应关系,以便在发生字符重复时找到原先的key再修改新key for st in myStr: code = ord(st) # 原始字符的unicode try: cmap[baseCode] = cmap.pop(code) # 删除原有映射,并以新key存储映射 newStr += chr(baseCode) # 连接新文本 oldAndNewKeyMap[code] = baseCode # 录入新旧code baseCode = baseCode + 0x0001 except Exception as err: print("发生异常:Unicode %s 字符已做过偏移映射" % err) newCode = oldAndNewKeyMap[code] newStr += chr(newCode) # 连接新文本 continue print("--------------修改后的映射关系-------------") print(cmap) print("--------------修改后偏移文本-------------") print(newStr) # 保存新ttf文件: font.save('./output/Alibaba-PuHuiTi-Light-cut-change-test.ttf') # 保存woff等其他文件 # options = subset.Options() # options.flavor = 'woff' # subset.save_font(font, './output/Alibaba-PuHuiTi-Light-min.woff', options)
-
web端操作:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> <!--这里要定义混淆后的字体文件样式--> @font-face { font-family: "myFont"; src: url("./output/Alibaba-PuHuiTi-Light-cut-change-test.ttf") format("truetype"); } </style> </head> <body> <h3>原文本</h3> <p>今天你真好看</p> <h3>混淆文本</h3> <p>ABCDEF</p> <h3>混淆文本复原</h3> <!--这里设置了该字体的样式才能生效--> <p style="font-family: myFont">ABCDE</p> </body> </html>
2.2、上效果
-
控制台输出:
575b0dcd6cf24b10b64b47743f0caf42.png -
网页内容:
8adca6189fe3494e85e42406bd73baf2.png
三、fontTools库的使用
3.1、开始之前,建议先了解ttf字体内部结构
- 见Apple或Microsoft官网查看文档(由这两家公司制定的规范)
- https://developer.apple.com/fonts/TrueType-Reference-Manual/
- https://docs.microsoft.com/zh-cn/typography/about
from fontTools.ttLib.ttFont import TTFont
from fontTools import subset
3.1、使用
-
打开字体文件
font = TTFont('TestFont.ttf')
-
转存xml文件(方便查看内部结构使用)
font.saveXML('TestFont.xml')
-
字体拆分截取
# 截取包含“你好嘿!”几个字符的字体内容 subsetter = subset.Subsetter() subsetter.populate(text="你好嘿嘿!") subsetter.subset(font)
-
保存与其他格式保存
# 保存新ttf文件: font.save('TestFont-new.ttf') # 保存woff等其他文件 options = subset.Options() options.flavor = 'woff' subset.save_font(font, 'TestFont.woff', options)
-
获取所有节点名
print(font.keys())
b3deab7d41364c3baa62fb013b3bb0af.png -
获取cmap节点unicode与name值映射(字符和字形映射)
print(font.getBestCmap())
5e8f8f9a90584e768925b24f47875162.png4b2f3d8118ab48adb21f506b337d468a.png