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