免責聲明:不要拿爬蟲在法律邊緣試探
簡單的說,就是進行如下設定:
webclient.getOptions().setUseInsecureSSL(true);
// 禁用css ,一般來說 css沒啥用
webclient.getOptions().setCssEnabled(false);
webclient.getOptions().setThrowExceptionOnFailingStatusCode(false);
// 設定支援 js
webclient.getOptions().setJavaScriptEnabled(true);
// 不抛出異常
webclient.getOptions().setThrowExceptionOnScriptError(false);
webclient.getOptions().setDoNotTrackEnabled(true);
// 最好設定一下逾時
webclient.getOptions().setTimeout(5*1000);
// 支援ajax
webclient.setAjaxController(new NicelyResynchronizingAjaxController());
如果簡單的看,這樣也就解決了,但是我們試着回想一下,當我們自己打開網頁的時候是不是都得等半天(有一段時間,肯定不是半天),于是問題來了,如果我們直接寫代碼
HtmlPage page = webclient.getPage(url);
可以确定,如果是比較小的js操作,在你餘下操作完成之前,js可能能夠執行完成,如果是那種比較繁瑣的js操作或者是ajax還需要去請求其他伺服器的操作,多半在于你餘下操作(例如解析html等)之前,js是無法執行完的,那麼我們得到的就是不完成的網頁。是以設定一個逾時時間是很有必要的,也就是這樣:
HtmlPage page = webclient.getPage(url);
// TODO: 2019/1/4 10秒鐘是為了讓js能夠充分執行(特别是ajax)
webclient.waitForBackgroundJavaScript(10*1000);
webclient.setJavaScriptTimeout(5*1000);
将上訴代碼放到項目中進行測試,我們可以發現可以得到結果了(這裡隻能說得到了結果,誰也不清楚正确的網頁到底爬下來是怎麼樣的,這是為做一個通用的爬蟲準備的,沒人能夠預測這些不同網站的網頁裡面的Js究竟會執行多久)。
于是,又出現了幾個問題:
1、如何确定某類網站的js平均響應時間,一般來說,同種類型的網站(由于跟響應需求有關,有些網站沒必要性能很好這個可以了解)可以算個js平均響應時間。這個時間用來設定 。
2、是否可以優化js 執行時間
3、如果每個頁面都等待那麼長時間(而且這種等待是必然每個頁面都會等待),那麼如果量比較大,比如爬取一千個不同的網站,該如何優化,使得整體的性能不至于非常差。
留坑:
1、第一個問題的本質是我該如何進行測試
2、第二個問題個人覺得應該從 htmlunit 選擇浏覽器,設定 js 引擎和 ajax 支援出發去優化
3、到第三個問題,是在前兩個問題已經确定的情況下進行優化的,需要對周遊算法和線程池進行優化
第一個問題,我的解決辦法是 寫一個動态的方法來記錄頁面的加載時間,主要在資料庫中存儲這幾個字段
字段名 | 字段含義 | 字段類型 |
url | 網頁位址 | string |
size | 目前檢查的網頁大小 | double |
waittime | 等待js的執行時間 | double |
netaffect | 上一次結果是否受網絡影響 | int |
maxsize | 記錄中頁面最大是多大 | double |
作為維護的中間變量,然後每一次通過一定的算法自動調節等待的時間,也就是說,對于每一個頁面動态的根據相關算法調整等待js的執行時間。代碼例子如下:
@Override
public HtmlPage executeReq(double handletime) {
int time = 1;
ArrayList<String> cache = redisDao.getMultiValue(url);
double lastpagesize = 0.0*-1;
double lasthandletime = 1000;
double maxsize = 0.0*-1;
int affect = -1;
if (cache != null){
lastpagesize = Double.parseDouble(cache.get(0));
lasthandletime = Double.parseDouble(cache.get(1));
maxsize = Double.parseDouble(cache.get(2));
affect = Integer.parseInt(cache.get(3));
}
if (affect == 1){
handletime = handletime/2.0;
}
while (time <= 5) {
try {
HtmlPage page = webclient.getPage(url);
// TODO: 2019/1/4 線程休息五秒鐘是為了讓js能夠充分執行(特别是ajax)
webclient.waitForBackgroundJavaScript((long) (handletime*1000));
webclient.setJavaScriptTimeout(5*1000);
// TODO: 2019/1/7 判斷目前page大小與上一次page大小的關系,如果小于之前page的一半就重新嘗試,并且保留最大的那一次
double pagesize = page.asXml().length();
if ((lastpagesize - 2*pagesize) < -1*0.0001){
endpage = pagesize>endsize?page:endpage;
endsize = Math.max(pagesize, endsize);
jiangeshijian = pagesize>endsize?handletime:jiangeshijian;
if (time < 5){
handletime = handletime + 1.0;
continue;
}
}else {
endpage = page;
endsize = pagesize;
jiangeshijian = handletime;
}
ArrayList<String> value = new ArrayList<String>();
String toredissize = "";
toredissize = toredissize + endsize;
String toredistime = "";
toredistime = toredistime + jiangeshijian;
value.add(toredissize);
value.add(toredistime);
if (maxsize < endsize){
value.add(toredissize);
}else {
String toredismaxsize = "" + maxsize;
value.add(toredismaxsize);
if (jiangeshijian > lasthandletime){
value.add("1");
}else {
value.add("0");
}
}
redisDao.setMultiValue(url,value);
return endpage;
} catch (IOException e) {
e.printStackTrace();
}
time++;
}
return null;
}
這裡我将維護的資訊放到redis裡面的