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实现

     /**
* font-secret字符串专用解密工具
*
* @param key 密匙
* @param encodeString 加密后的字符串
* @return 解密后的字符串
*/
public static String decodeString(String key, String encodeString) {
try {
//base64解码,初始化字体
byte[] ss = Base64.decodeBase64(key);
InputStream inputStream = new ByteArrayInputStream(ss);
Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, inputStream);
FontRenderContext fontRenderContext = new FontRenderContext(new AffineTransform(), false, false);
GlyphVector glyphVector = dynamicFont.createGlyphVector(fontRenderContext, ""); //获取font中字形的映射关系,字段为private,使用反射
Class<?> clazz = Font.class;
Field[] fs = clazz.getDeclaredFields();
Font2DHandle font2DHandle = null;
for (int i = 0; i < fs.length; i++) {
fs[i].setAccessible(true);// 将目标属性设置为可以访问
if (fs[i].getName().equals("font2DHandle")) {
font2DHandle = (Font2DHandle) fs[i].get(dynamicFont);
} } //得到映射关系
Font2D font2D = font2DHandle.font2D;
TrueTypeFont trueTypeFont = (TrueTypeFont) font2D;
TrueTypeGlyphMapper charToGlyphMapper = (TrueTypeGlyphMapper) trueTypeFont.getMapper(); //开始解密,encodeString为加密后的字符串
StringBuffer buffer = new StringBuffer();
char[] chars = encodeString.toCharArray();
for (int i = 0; i < chars.length; i++) {
buffer.append(charToGlyphMapper.charToGlyph(chars[i]) - 1);
}
return buffer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}

4.demo

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

      

JAVA爬虫对font-face字体反爬虫解密的更多相关文章

  1. 深入细枝末节,Python的字体反爬虫到底怎么一回事

    内容选自 即将出版 的<Python3 反爬虫原理与绕过实战>,本次公开书稿范围为第 6 章——文本混淆反爬虫.本篇为第 6 章中的第 4 小节,其余小节将 逐步放送 . 字体反爬虫开篇概 ...

  2. Python 爬虫工程师必看,深入解读字体反爬虫

    字体反爬虫开篇概述 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...

  3. 基于C#.NET的高端智能化网络爬虫(一)(反爬虫哥必看)

    前两天朋友发给我了一篇文章,是携程网反爬虫组的技术经理写的,大概讲的是如何用他的超高智商通过(挑衅.怜悯.嘲讽.猥琐)的方式来完美碾压爬虫开发者.今天我就先带大家开发一个最简单低端的爬虫,突破携程网超 ...

  4. python网络爬虫 - 如何伪装逃过反爬虫程序

    有的时候,我们本来写得好好的爬虫代码,之前还运行得Ok, 一下子突然报错了. 报错信息如下: Http 800 Internal internet error 这是因为你的对象网站设置了反爬虫程序,如 ...

  5. Python爬虫学习笔记——防豆瓣反爬虫

    开始慢慢测试爬虫以后会发现IP老被封,原因应该就是单位时间里面访问次数过多,虽然最简单的方法就是降低访问频率,但是又不想降低访问频率怎么办呢?查了一下最简单的方法就是使用转轮代理IP,网上找了一些方法 ...

  6. 自动更改IP地址反爬虫封锁,支持多线程(转)

    8年多爬虫经验的人告诉你,国内ADSL是王道,多申请些线路,分布在多个不同的电信机房,能跨省跨市更好,我这里写好的断线重拨组件,你可以直接使用. ADSL拨号上网使用动态IP地址,每一次拨号得到的IP ...

  7. WebSocket 反爬虫

    目录 WebSocket握手验证反爬虫 WebSocket 消息校验反爬虫 WebSocket Ping 反爬虫 总结 WebSocket握手验证反爬虫 ! HTTP协议 请求头 服务器端创建 soc ...

  8. 温故知新,.Net Core利用UserAgent+rDNS双解析方案,正确识别并反爬虫/反垃圾邮件

    背景 一般有价值的并保有数据的网站或接口很容易被爬虫,爬虫会占用大量的流量资源,接下来我们参考历史经验,探索如何在.Net Core中利用UserAgent+rDNS双解析方案来正确识别并且反爬虫. ...

  9. k 近邻算法解决字体反爬手段|效果非常好

    字体反爬,是一种利用 CSS 特性和浏览器渲染规则实现的反爬虫手段.其高明之处在于,就算借助(Selenium 套件.Puppeteer 和 Splash)等渲染工具也无法拿到真实的文字内容. 这种反 ...

随机推荐

  1. python运行出现TypeError: super() takes at least 1 argument (0 given)错误

    在写继承子类的时候出现了TypeError: super() takes at least 1 argument (0 given)的error: 源代码(python3中完美可运行): class ...

  2. python 将字符串中的unicode字符码转换成字符

    将字符串str =’\u98ce\u534e\u7684\u51b2\u950b'转换成汉字显示 可以直接print输出 print u'\u98ce\u534e\u7684\u51b2\u950b' ...

  3. 【robotframework】robotframework环境搭建

    一.基于python3.6环境 在dos命令输入 pip install robotframework 在线安装robotframework在dos命令输入 pip install Pypubsub= ...

  4. c语言int类型的变量输入一个字符出错

    今天遇到一个C语言的小问题,就是写一个简单的计算器,定义一个%f%c%f的三个变量,作2+3,2-3这种可以不断输入并输入“OFF”跳出循环的计算器功能,便会出现错误: 错误的示例代码如下: #inc ...

  5. JS知识体系【JQ】附加理论+视频地址铺助学习

    理论部分:https://www.jianshu.com/p/e10792076c6e  //不吃鱼的猫_8e95---简书平台 https://www.cnblogs.com/hongqin/p/5 ...

  6. DDD:架构思想的旧瓶新酒

    DDD.DSL 和 DCI DDD 概念最早提出于 2004 年,作为一种软件开发的指导思想,DDD 对软件开发带来了诸多可能与方向,张晓龙认为 DDD 为软件开发带来的好处主要有以下几点: 首先,最 ...

  7. MySQL进阶16 - 视图的创建/修改/删除/更新--可更新性的不适用条件

    #进阶16 : 视图 /* 含义: 虚拟表,和普通表一样使用;(从5.1开始使用的:)是通过表动态生成的数据 创建语法: create view 视图名 as 查询语句; ---------- 作用: ...

  8. Robot Framework--变量

    Robot Framework的变量分为标量, 列表和字典, 分别使用语法格式 ${SCALAR}, @{LIST} 和 &{DICT} 来定义. 此外, 环境变量可以直接使用语法 %{ENV ...

  9. 「数据结构与算法之链表(Python)」(四)

    什么是链表 顺序表的储存分为一体式结构和分离式结构,但总的来说存储数据的内存是一块连续的单元,每次申请前都要预估所需要的内存空间大小.这样就不能随意的增加我们需要的数据了.链接就是为了解决这个问题.它 ...

  10. 判断字符串是否是IP地址

    #include <stdio.h>#include <string.h> bool isIP(const char* str); int main(){ char str[] ...