互联网医院研发部-柴进
前言
提起web字体前端工程师应该都比较熟悉,通过它可以实现UI视觉稿中的特殊字体,让页面文字展示更加的个性化。而对于后端工程师来说web字体还有其他应用场景:数据安全领域的反数据爬取(后续简称“反爬”)。
实例分析
我们以猫眼电影PC端电影票价格为例,获取某部影片的相关影院信息如下图所示,乍看起来与普通页面并没什么区别。
通过F12打开审查元素的界面就能发现并不像看起来那样简单,定位元素发现价格「38」是类似「口口」的乱码而不是数字。
为什么页面显示的内容与审查元素时完全不同呢?
带着这个疑问我们直接查看一下页面源代码,观察下HTML中价格数据会有什么变化。
查看源码我们发现价格「38」变成了类似乱码的字符「」,这到底是什么呢?
查阅相关资料我们了解到这其实是HTML、XML等标准通用标记语言(SGML)的转义序列,严格意义上来说并不是一种「编码」格式。通常以[]开头[;]结尾,用来表示字符值的引用(Numeric characterreference,简称NCR)。以[]开头后接十进制数字,以[]开头后接十六进制数字,数字取值为目标字符集的Unicodecode point。
从HTML 4开始,NCR以Unicode为准,并且与HTML文档编码格式无关。举个例子:「中国」二字的 Unicode 字符为「U+4E2D」和「U+56FD」,换算成十进制为「20013」和「22269」,而十六进制为「4E2D」和「56FD」,在HTML中用NCR十六进制表示则为「中」和「国」。
猫眼电影通过NCR的方式将「38」转换为十六进制符号「」,浏览器获取页面后渲染成「38」。但这只能解释源码中的NCR字符,而无法解释在页面上显示的是「38」,审查元素时却是「口口」。
在审查元素时我们发现价格元素有个「.stonefont」类名,并且「font-family」属性为「stonefont」字体,这显然不是常规字体。通过点击类名对应的「cinemas?mov_=1217023」文件,我们发现「stonefont」是「@font-face」自定义的字体,配置了对应的字体文件
到这里我们就找到了答案,浏览器获取含有NCR格式数据的HTML文档,通过CSS的方式将文档中的「」转换并渲染成「38」。
为了验证假设,我们下载「@font-face」中的woff格式的字体文件,并用字体工具「FontCreator」打开检查,正如前面所说E31A代表3,EA9F代表8。
以上就是猫眼电影针对价格反爬的实现方案,通过web字体文件与NCR字符映射的方式来实现数据的安全加密,并达到看到与下载内容不一致的效果,从而实现数据安全的目的,接下来让我们回到后端开发。
方案梳理
我们来思考下如何搭建一套这样的系统?经过前文的铺垫,我们对需要做的事情应该有了大致的了解,前端展示上至少需要两部分内容:
1、字体文件。
2、与字体文件对应的NCR字符串。
如何制作字体文件?字体文件是什么样子的?我们用python提供的类库fontTools将woff的字体文件解析成XML格式,由于解析后的文件内容较多,我们仅挑选关键部分来说明。
这部分内容描述了字体文件在画布上相关联的点坐标,name属性就是该文字的映射编码,后面的属性xMin,yMin,xMax,yMax是描述画布作用域的x轴与y轴最小值、最大值的坐标点,contour标记了具体的像素点的集合。
我们可通过修改name值来做到动态的改变文字与编码的映射关系,也可以通过微调pt标签的x和y属性来改变文字的形状。至此我们就完成了字体文件的生成,同时我们也得到了文字与NCR字符的映射关系。
将生成的字体文件以及对应的NCR映射关系同步给前端,前端需要替换「@font-face」中的字体文件,最终完成页面内容的动态反爬处理,实现维护项目数据安全的目的。
小结
文章最后让我们来回顾一下整体反爬方案的实现流程:
当用户发起获取带有价格数据的请求时,前端获取带有NCR数据的HTML的同时,去获取字体文件。后端收到请求后,将初始文本数据用NCR编码替换后返回给前端,再给前端返回对应字体文件。前端根据字体文件渲染对应的文字样式,最终用户看到对应的页面内容。
至此我们初步完成了字体反爬的后端实现,后续还有很多可以优化的内容,由于篇幅有限就不做赘述了,感兴趣的同学可以随时关注相关内容的更新。
一起学习nginx(二)
堆排序及其应用
一起学习nginx(一)
Vue项目开发入过的坑
京东互医架构解耦设计的思考
从无到有-打造页面监控系统