免责声明:不要拿爬虫在法律边缘试探
简单的说,就是进行如下设置:
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里面的
关于第二个问题,我在咨询了技术大佬之后,他建议我用一个真正的爬虫来做,htmlunit 是一个比较好的模拟浏览器的框架,但是在爬虫方面算不上一个比较好的
关于第三个问题,尚没有解决