最近在做一个信息采集项目,需要用到爬虫,调研了市面上的爬虫框架,最终选择webmagic
首先,有一个需求是这样的,爬虫需要先去抓取列表页的href链接,抓完之后,再把这些链接放到一个队列里,爬虫再从队列里poll出来,再去抓每个链接里的详情内容,也就是addTargetRequests 的逻辑。
但是实际上,爬虫只抓了初始列表的数据,详情页的抓取逻辑没执行,一开始,看了文档,设置setSpawnUrl(true),仍然无效。只能从源码上找问题。
webmagic的启动大概有两种方法,一是调用run方法;二是调用get或者getAll
run 方法,新开一个线程去爬取数据
public void run() {
checkRunningStat();
initComponent();
while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) {
//这个scheduler 存储了要爬取的url
Request poll = scheduler.poll(this);
if (poll == null) {
if (threadPool.getThreadAlive() == 0) {
poll = scheduler.poll(this);
if (poll == null) {
if (exitWhenComplete) {
break;
} else {
try {
Thread.sleep(emptySleepTime);
continue;
} catch (InterruptedException e) {
break;
}
}
}
} else {
if (waitNewUrl())
break;
continue;
}
}
final Request request = poll;
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
//具体执行在这个方法
processRequest(request);
onSuccess(request);
} catch (Exception e) {
onError(request, e);
} finally {
pageCount.incrementAndGet();
signalNewUrl();
}
}
});
}
stat.set(STAT_STOPPED);
if (destroyWhenExit) {
close();
}
}
debug可以看到scheduler 只poll了一个request,说明二级页面的链接没加到scheduler 里,继续跟进 #processRequest
private void processRequest(Request request) {
Page page;
//这里是选择下载器
if (null != request.getDownloader()){
page = request.getDownloader().download(request,this);
}else {
page = downloader.download(request, this);
}
if (page.isDownloadSuccess()){
//这里对下载好的页面进行处理
onDownloadSuccess(request, page);
} else {
onDownloaderFail(request);
}
}
debug可知,page对象里的TargetRequests 是有数据的,说明addTargetRequests 方法执行了,跟进去 #onDownloadSuccess
private void onDownloadSuccess(Request request, Page page) {
if (site.getAcceptStatCode().contains(page.getStatusCode())){
//这里是自定义的processor的处理
pageProcessor.process(page);
//下面pipelines是处理结果了,所以问题应该在这里
extractAndAddRequests(page, spawnUrl);
if (!page.getResultItems().isSkip()) {
for (Pipeline pipeline : pipelines) {
pipeline.process(page.getResultItems(), this);
}
}
} else {
}
sleep(site.getSleepTime());
return;
}
跟进去 #extractAndAddRequests
protected void extractAndAddRequests(Page page, boolean spawnUrl) {
//
if (spawnUrl && CollectionUtils.isNotEmpty(page.getTargetRequests())) {
for (Request request : page.getTargetRequests()) {
addRequest(request);
}
}
}
private void addRequest(Request request) {
if (site.getDomain() == null && request != null && request.getUrl() != null) {
site.setDomain(UrlUtils.getDomain(request.getUrl()));
}
scheduler.push(request, this);
}
可以看到 #addRequest 实际上就是往 scheduler push request,debug可知,spawnUrl此时是false,导致根本没执行addRequest 方法,所以scheduler里面也就没有二级详情页的链接了
所以,简单的处理就是去掉 extractAndAddRequests 方法里 去掉 spawnUrl==true 的判断
protected void extractAndAddRequests(Page page, boolean spawnUrl) {
if (CollectionUtils.isNotEmpty(page.getTargetRequests())) {
for (Request request : page.getTargetRequests()) {
addRequest(request);
}
}
}
成功爬取页面