使用Java抓取解析汽车之家车型配置数据

因为公司业务需求,需要获取汽车之家的车型配置数据如下图:

使用Java抓取解析汽车之家车型配置数据_第1张图片

        由于汽车之家没做防爬策略,只是数据给混淆了,这里主要说解析数据。

        通过保存页面,配置项的数据是通过JS动态生成的。在页面的第572行左右,有配置项的json格式数据

使用Java抓取解析汽车之家车型配置数据_第2张图片

        主要的配置数据是config和option,车身颜色color和内部颜色innerColor。一开始以为汽车之家的数据还挺好抓取,直接页面上就有。但是通过格式化工具后发现,配置项的名称和值的部分字,被随机的用span标签加CSS给替换了,例如下面一个:

        那就去页面里面看看有没有.hs_kw72_configKE的CSS样式,然而并没有找到,然后再找找引入的CSS文件里也没有,这就头疼了。最后发现这些用于替代文字的CSS样式是通过JS代码生成的,并且JS代码也在页面中,在保存下来页面中有两行。


上面这个第一部分,下面这个是第二部分,注意第二部分是两个,我这只截取了开头


        一开始到这步的时候觉得没戏了,一开始也想着怎么能执行这段JS代码,Java可以执行JS的脚步,但是这些JS脚步里面有浏览器对象,例如document,window等,也尝试着通过Java写这些相关的对象,然后放入JavaScript执行引擎的上下文中,但是没能成功,后来在网上看到七月流光一个相关的文章,也是这个问题,给出了一个比较巧的解决方法(七月流光的博客地址)。他的方法是通过创建几个JS对象替代,脚步执行所需要的对象,包括document和window对象。

var rules = "";
var document = {};
document.createElement = function() {
    return {
        sheet: {
            insertRule: function(rule, i) {
                if (rules.length == 0) {
                    rules = rule;
                } else {
                    rules = rules + "|" + rule;
                }
            }
        }
    }
};
document.getElementsByTagName = function() {};
document.querySelectorAll = function() {
    return {};
};
document.head = {};
document.head.appendChild = function() {};
var window = {};
window.decodeURIComponent = decodeURIComponent;
window.location = {};
window.location.href = "car.m.autohome.com.cn";

 将上面这段JS放到用于生成CSS的JS代码前,然后通过Java的脚步引擎执行这段JS,就能获取到如下结果

把CSS样式破解出来,那么再把上面的json数据里面的span给替换成汉字,再解析json数据就OK了。

    但是注意一点,生成CSS的JS代码好像有两种,一种是里面有汉字的,一种里面没汉字,没汉字的Java在执行代码是加上-Xss=5m,否则会报StackOverflowError

Java测试代码如下,用到Jsoup

package cn.iautos.crawler.analysis.util.test;

import com.alibaba.fastjson.JSONObject;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.junit.Test;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
* Created on 2017/10/13 16:25. * * @author zhubenle */ public class UtilTest { public final static String SCRIPT_PRE = "var rules = '';var document = {};document.createElement = function() {return {sheet: {insertRule: " + "function(rule, i) {if (rules.length == 0) {rules = rule;} else {rules = rules + '|' + rule;}}}}};document.getElementsByTagName = " + "function() {};document.querySelectorAll = function() {return {};};document.head = {};document.head.appendChild = function() " + "{};var window = {};window.decodeURIComponent = decodeURIComponent;window.location = {};window.location.href = 'car.m.autohome.com.cn';"; public final static Pattern CSS_PATTERN = Pattern.compile("\\.(.*)::before.*content:\"(.*)\".*"); @Test public void testScript() throws Exception { String url = "https://car.autohome.com.cn/config/series/692.html"; Connection.Response response = Jsoup.connect(url).validateTLSCertificates(false).ignoreContentType(true).ignoreHttpErrors(true).execute(); System.out.println(response.statusCode()); Document document = response.parse(); Elements scripts = document.select("script:containsData(insertRule)"); ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); Map cssKeyValue = new HashMap<>(); System.out.println("------------css数据------------"); scripts.forEach(element -> { String script = SCRIPT_PRE + element.html(); try { engine.eval(script); } catch (ScriptException e) { e.printStackTrace(); } String css = (String) engine.get("rules"); System.out.println(css); for (String str : css.split("\\|")) { Matcher cssMatcher = CSS_PATTERN.matcher(str); if (cssMatcher.find()) { cssKeyValue.put("", cssMatcher.group(2)); } } }); Elements contents = document.select("script:containsData(keyLink)"); String content = contents.html(); System.out.println("------------用css混淆的配置数据------------"); System.out.println(content); //把混淆数据里的样式用上面解析的样式给替代 for(Map.Entry entry : cssKeyValue.entrySet()) { content = content.replaceAll(entry.getKey(), entry.getValue()); } System.out.println("------------用css替换后的数据------------"); System.out.println(content); engine.eval(content); System.out.println("------------每个配置结果------------"); String keyLink = JSONObject.toJSONString(engine.get("keyLink")); String config = JSONObject.toJSONString(engine.get("config")); String option = JSONObject.toJSONString(engine.get("option")); String bag = JSONObject.toJSONString(engine.get("bag")); String color = JSONObject.toJSONString(engine.get("color")); String innerColor = JSONObject.toJSONString(engine.get("innerColor")); System.out.println(keyLink); System.out.println(config); System.out.println(option); System.out.println(bag); System.out.println(color); System.out.println(innerColor); //最后的数据,解析json就ok } }

(谢谢,有问题发邮箱联系[email protected])







你可能感兴趣的:(使用Java抓取解析汽车之家车型配置数据)