JAVA爬虫对font-face字体反爬虫解密


         1.参考博客

         https://www.jianshu.com/p/9975de57b0ce

         https://blog.csdn.net/litang199612/article/details/83413002

         https://blog.csdn.net/m0_37156322/article/details/84658872

         https://blog.csdn.net/paul0926/article/details/96336947

         本博客重点讲解java实现反爬虫字体解密,了解具体原因请参考以上博客,Python也请参考以上博客。

         2.背景

          在针对安居客等房地产项目进行数据爬虫工作中,发现页面的显示为标准的数字,但数据抓取到确实乱码

          页面:

 

          页面审查:

       

 

          页面显示的“2500”,但数据显示的却是“龒麣龤龤”的乱码,很疑惑,最后审查发现数据显示是使用的一个特殊字体“fangchan-secret”。

 fangchan-secret

 经查询相关文档和博客,发现fang-secret是一个动态生成字体库的工具,而且每次根据不同key生成,字体库动态生成,后端又不存在相关字体库,所以获取的是乱码。key为base64,重新加载页面key为变化,具体的key可以审查页面,检索"AAAAA",比较长的一串的base64编码的就是了,浏览器每次返回页面根据动态字体库渲染相关数据。

         3.解决方案

         在博客和相关文档中,了解了相关原因,但其具体的实现却是基于python实现,最关键的是python的ttffont的库,一直想找java的解决方案没有,只好自己动手。

         拿到动态生成的字体库的key

        

 

         因为字体库基于key生成,这里实现可以通过java的爬虫工具,然后使用正则表达式实现,然后拿到以下的字符串:

         

         生成字体库,解码

         这里使用java的awt的相关jar包,关键的类Font实现

         

 1     /**
 2      * font-secret字符串专用解密工具
 3      *
 4      * @param key          密匙
 5      * @param encodeString 加密后的字符串
 6      * @return             解密后的字符串
 7      */
 8     public static String decodeString(String key, String encodeString) {
 9         try {
10             //base64解码,初始化字体
11             byte[] ss = Base64.decodeBase64(key);
12             InputStream inputStream = new ByteArrayInputStream(ss);
13             Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, inputStream);
14             FontRenderContext fontRenderContext = new FontRenderContext(new AffineTransform(), false, false);
15             GlyphVector glyphVector = dynamicFont.createGlyphVector(fontRenderContext, "");
16 
17             //获取font中字形的映射关系,字段为private,使用反射
18             Class<?> clazz = Font.class;
19             Field[] fs = clazz.getDeclaredFields();
20             Font2DHandle font2DHandle = null;
21             for (int i = 0; i < fs.length; i++) {
22                 fs[i].setAccessible(true);// 将目标属性设置为可以访问
23                 if (fs[i].getName().equals("font2DHandle")) {
24                     font2DHandle = (Font2DHandle) fs[i].get(dynamicFont);
25                 }
26 
27             }
28 
29             //得到映射关系
30             Font2D font2D = font2DHandle.font2D;
31             TrueTypeFont trueTypeFont = (TrueTypeFont) font2D;
32             TrueTypeGlyphMapper charToGlyphMapper = (TrueTypeGlyphMapper) trueTypeFont.getMapper();
33 
34             //开始解密,encodeString为加密后的字符串
35             StringBuffer buffer = new StringBuffer();
36             char[] chars = encodeString.toCharArray();
37             for (int i = 0; i < chars.length; i++) {
38                 buffer.append(charToGlyphMapper.charToGlyph(chars[i]) - 1);
39             }
40             return buffer.toString();
41         } catch (Exception e) {
42             e.printStackTrace();
43         }
44         return "";
45     }

 

      4.demo

 demo:https://gitee.com/scnucxy/spiderFontDemo

      

          


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM