天天看點

htmlunit設定支援js和 ajax

免責聲明:不要拿爬蟲在法律邊緣試探

簡單的說,就是進行如下設定:

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裡面的

繼續閱讀