本文共 3971 字,大约阅读时间需要 13 分钟。
请注意:本文章为原创文章,未经本人许可,任何人不得转载和借鉴
目前越来越多的网站升级了应对爬虫的反制手段和措施,网站使用自定义字体或者人们常说的字体反爬,就是其中一种。
这里我们以查策网()为例
打开查策网的某一篇政策文章: 看起来都很正常,但是当我们点击右键、打开检查,或者F12。 可以发现,【申报条件】这个板块里面的文字,和源代码里的文字完全不一样; 比如“申请企业应具备以下条件:”,代码里则对应为:“者合册第应具步以督法快:”; 即使你选中文字,再把它们复制出来,文字粘贴后依然为:“者合册第应具步以督法快:”。解决该问题的一种基本思路是:构建源字体与替换字体的对应关系
比如上文提到的句子: 者合册第应具步以督法快替换为申请企业应具备以下条件 其中: 者>申 | 合>请 | 册>企 假如我们能有这样的一个对应字典change_font = { '者': '申', '合': '请', '册': '企', ... , }
那么我们就能够将网站爬到的所有字体,都替换成正确的文字。
F12打开检查后,点击Network,刷新我们刚才访问的政策链接,在众多链接中,找到网站的字体文件链接()
然后在浏览器中打开该链接,并保存到本地接下来,我们需要用到百度的在线字体编辑器,网址为:
打开该网站后,点击左上角的打开,然后上传刚才保存的查策网的字体文件 打开后: 这时候我们就能看到查策网的网站字体和编码对应的关系接下来我们使用python解析字体的常用库:fontTools
>>>from fontTools.ttLib import TTFont>>>FONT_FILE = r'C:\Users\17337\Downloads\ccw.ttf'>>>font_file = TTFont(FONT_FILE)>>>for k, v in font_file['cmap'].getBestCmap().items():>>> print(k, v)33 ampersand35 greater36 c37 asciitilde38 H40 q41 S......39069 uni989D39318 uni999639564 uni9A8C39640 uni9AD840857 uni9F99>>>
这里getBestCmap返回的是一个unicode cmap字典,即编码和图形的对应关系。
请注意:这里key为字体的编码,value为每个字体对应的独特的id值,这里为了方便理解,我们可以把这个value理解成字体的注释
结合展示的字体,我们可以随便找一个字体作为切入点,比如刚才打印的最后一对键值对
40857 uni9F99
编码为:40857 ,注释为:uni9F99
在我们刚才打开的百度字体编辑器的第三页,有一个字体【八】正好也是uni9F99 除了注释uni9F99对上了,还有其他我们能着手的点吗? 仔细看【八】这个【$9F99】,和我们的编码【40857】 一个看起来像是16进制,一个看起来像是十进制,这两个数字有关联吗>>>hex(40857) # 十进制转十六进制'0x9f99'
答案是:字体编辑器的上面这个编码【$9F99】,正是【40857】的十六进制表示。
所以,现在我们知道了,【uni9F99】这个注释对应的字体就是【八】,也能通过fontTools找到这个对应关系,但是我们现在还不知道的是:哪个源代码的字体要替换成【八】
回到我们刚才打开的政策网页,【申报材料】里,正好也有一个【八】
很显然,源代码的【龙】字,实际展示时,为【八】这个汉字 所以,源代码里:龙要替换成八 那么前端代码在处理和展示的时候,怎么知道龙要替换成八,或者说替换成八这个字体图形? 显然是有一套通用的处理规则,我们现在就是要找出这套转换规则 从【龙】这个汉字入手: 【龙】utf8编码为【\xe9\xbe\x99】 【龙】gbk编码为【\xc1\xfa】 【龙】的Ascii数值为【40857】>>>ord('龙')40857
40857!刚才我们使用getBestCmap返回的字典,【40857】正好对应的值是【uni9F99】,对应的字体正好是【八】!
所以汉字【龙】> ord(‘龙’) > 40857现在我们只剩最后一个问题:
如何批量构建,类似于这种【40857/$9F99/uni9F99】与【八】的对应关系? 答案是:手动构建! 当然,也不是完全手动构建类似 {‘uni9F99’: ‘八’, ‘six’: ‘6’, … ,} 这种字典 有一种取巧的方式: fontTools库的getGlyphOrder() 方法,可以获取字体文件的所有字体注释 该方法返回的字体注释的列表,与百度字体编辑器展示的顺序是一模一样的>>>font_file.getGlyphOrder()['.notdef', 'nine', 'eight', 'three', 'four', 'seven', ..., 'uni9662', 'uni660E']
下图为手动构建的完整的FONT_LIST(要对应百度字体编辑器)
上文所说的手动构建,就是指,把百度字体编辑器展示的字体,一个一个对应着写到一个列表里,保存起来,以后若是网站字体又有所更新,只需要修改相应的元素即可。 所以这里我们只需要手动按顺序构建字体对应的列表FONT_LIST,即可生成对应的字典>>>FONT_LIST = [' ', '9', '8', '3', '4', '7', ..., '明', '局', '第']>>>font_dict = dict(zip(font_file.getGlyphOrder(), FONT_LIST))>>>font_dict{ '.notdef': ' ', 'nine': '9', 'eight': '8', 'three': '3', 'four': '4', 'seven': '7', ... 'uni7B2C': '业', 'uni3007': '院', 'uni4E1A': '⚪', 'uni5C40': '明', 'uni9662': '局', 'uni660E': '第'}
这个font_dict有什么用呢,这还要和前文的getBestCmap方法配合起来使用
>>>replace_dict = { }>>>for code, value in font_file['cmap'].getBestCmap().items():>>> before_replace = chr(code) # 源代码的汉字字符串>>> after_replace = font_dict[value] # 需要替换的汉字字体>>> replace_dict[before_replace] = after_replace>>>replace_dict{ '!': '&', '#': '>', '$': 'c', '%': '~', '&': 'H', '(': 'q', ... '革': '策', '项': '不', '须': '制', '额': '司', '首': '免', '验': '省', '高': '天', '龙': '八'}
这个replace_dict就是我们最终的用来替换文本的字典了。
到此,我们的实操就结束了,下面进入整体流程的代码部分。from fontTools.ttLib import TTFontFONT_FILE = r'C:\Users\17337\Downloads\ccw.ttf'FONT_LIST = [' ', '9', '8', '3', '4', '7', ..., '明', '局', '第']def get_font(): font_file = TTFont(FONT_FILE) font_dict = dict(zip(font_file.getGlyphOrder(), FONT_LIST)) # 注释和真实字体的对应关系字典 return { chr(k): font_dict[v] for k, v in font_file['cmap'].getBestCmap().items()}def change(text): replace_dict = get_font() return ''.join([replace_dict.get(each, each) for each in text]) if text else ''def test(): chace_str = '者合册第应具步以督法快:' result = change(chace_str) print('转换前:"{}", 转换后:"{}"'.format(chace_str, result))if __name__ == '__main__': test()
运行结果
文章中,本人提供了一种字体反爬的解决方案,当然,也有该方案的缺陷:
1.需要手动构建FONT_LIST列表,比较花费时间 2.适用于查策网,但不一定适用于其他网站,不能作为一种普适的字体反爬的解决方案感谢您的观看!