我正在为拥有越来越多实体的网站实施抓取工具。没有可用信息存在多少实体且没有所有实体的列表。可以使用以下URL访问每个实体:http://www.somewebsite.com/entity_{i}
其中{i}
是实体的编号,从1开始并以1递增。
要抓取每个实体,我正在运行一个循环,检查HTTP请求是否返回200
或404
。如果我得到404 NOT FOUND
,则循环停止,我确定我拥有所有实体。
串行方式如下:
def atTheEnd = false
def i = 0
while(!atTheEnd){
atTheEnd = !crawleWebsite("http://www.somewebsite.com/entity_" + i)
i++
}
crawleWebsite()
如果成功则返回true,如果出现404 NOT FOUND
错误则返回false。
问题是抓取这些实体可能需要很长时间,这就是为什么我想在多个线程中执行它但我不知道实体的总量,因此每个任务都不是独立的其他任务。
解决这个问题的最佳方法是什么?
我的方法是:使用带有REST HEAD请求的二进制搜索来获取实体总数(介于500和1000之间)并将其拆分为某些线程。
有没有更好的方法呢?
TL;博士
基本上我想告诉线程池以编程方式创建新任务,直到满足条件(发生第一个404
时)并等待每个任务完成。
注意:我使用Grails 3
实现此代码。
答案 0 :(得分:1)
正如你所说,实体的总数是未知的,可以分为数千个。在这种情况下,我只是寻找一个固定的线程池并推测性地查询URL,即使你可能已经到了最后。考虑这个例子。
size=
线程池设置为50,一旦抓取所有50个URL,我们检查是否到达终点。如果不是,我们继续。
显然,在最糟糕的情况下,您可以抓取50 @Grab(group = 'org.codehaus.gpars', module = 'gpars', version = '1.2.1')
import groovyx.gpars.GParsPool
//crawling simulation - ignore :-)
def crawleWebsite(url) {
println "$url:${Thread.currentThread().name}"
Thread.sleep (1)
Math.random() * 1000 < 950
}
final Integer step = 50
Boolean atTheEnd = false
Integer i = 0
while (true) {
GParsPool.withPool(step) {
(i..(i + step)).eachParallel{atTheEnd = atTheEnd || !crawleWebsite("http://www.somewebsite.com/entity_" + it)}
}
if (atTheEnd) {
break
}
i += step
}
秒。但我相信你可以逃脱它: - )