使用Java抓取解析汽车之家车型配置数据
因為公司業務需求,需要獲取汽車之家的車型配置數據如下圖:
????????由于汽車之家沒做防爬策略,只是數據給混淆了,這里主要說解析數據。
????????通過保存頁面,配置項的數據是通過JS動態生成的。在頁面的第572行左右,有配置項的json格式數據
????????主要的配置數據是config和option,車身顏色color和內部顏色innerColor。一開始以為汽車之家的數據還挺好抓取,直接頁面上就有。但是通過格式化工具后發現,配置項的名稱和值的部分字,被隨機的用span標簽加CSS給替換了,例如下面一個:
<span class='hs_kw72_configKE'></span>????????那就去頁面里面看看有沒有.hs_kw72_configKE的CSS樣式,然而并沒有找到,然后再找找引入的CSS文件里也沒有,這就頭疼了。最后發現這些用于替代文字的CSS樣式是通過JS代碼生成的,并且JS代碼也在頁面中,在保存下來頁面中有兩行。
上面這個第一部分,下面這個是第二部分,注意第二部分是兩個<script></script>塊,我這只截取了開頭
????????一開始到這步的時候覺得沒戲了,一開始也想著怎么能執行這段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;/*** <br/>* 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:\"(.*)\".*");@Testpublic 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<String, String> 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("<span class='" + cssMatcher.group(1) + "'></span>", 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<String, String> 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} }(謝謝,有問題發郵箱聯系zhubenle@gmail.com)
總結
以上是生活随笔為你收集整理的使用Java抓取解析汽车之家车型配置数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 获取地球某点最新的磁偏角
- 下一篇: java坦克大战爆炸效果_Java坦克大