注:
1、本课完整的爬虫代码可以在神箭手示例代码中查看:http://www.shenjianshou.cn/index.php?r=demo/docs&demo_id=500006
2、如何在神箭手上运行代码,请查看文档:http://docs.shenjianshou.cn/overview/guide/develop/crawler.html
3、更详细的爬虫开发教程,请查看文档:http://docs.shenjianshou.cn/develop/summary/summary.html
大家好,我是神箭手的游牧老师~
今天继续给大家带来如何在神箭手上快速开发爬虫系列教程的第四课:如何爬取分页数据。
通过前三课的学习,相信大家爬爬文章、爬爬商品啥的已经很简单了(还不会的筒子面壁去( ̄▽ ̄)”)。那么今天呢,主要跟大家分享下爬取分页数据的方法。
分页数据指的是要爬取的数据在多个分页上,无法通过请求一个页面一次抽取出来。举个常见的栗子,就是电商商品的评论了:

如果要将每个商品的所有评论爬取为一个商品数据的一个字段,因为在商品的详情页内,评论是分页显示的,所以需要通过分别访问每页评论抽取。
如何在内容页中抽取分页数据,神箭手提供了两种方法:
第一种方法,使用field的attachedUrl,在数据抽取过程中分别获取每页评论再汇总
attachedUrl是神箭手提供的抽取异步请求数据的方式(http://docs.shenjianshou.cn/develop/configs/field.html#attachedUrl),所以可以使用attachedUrl先分别抽取不同分页的评论,然后再在回调函数中将数据汇总。
异步抽取分页评论的field代码:
{
name: "comments",
alias: "评价",
selector: "//div[@id='sjs']/span", // 3、抽取出每页评价的内容
repeated: true,
children: [
{
name: "page", // 页码
selector: "//text()",
required: true
},
{
name: "page_comments", // 该页的评价
sourceType: SourceType.AttachedUrl, // attachedUrl表示在抽取过程中另发请求,再从返回的数据中抽取数据
attachedUrl: "http://shop.mogujie.com/ajax/pc.rate.ratelist/v1?pageSize=20&sort=1&isNewDetail=1&itemId={$.product_id}&type=1&page={page}",
selectorType: SelectorType.JsonPath, // 返回的数据是json,使用JsonPath抽取数据
repeated: true,
selector: "$.data.list",
children: [
{
name: "create_time",
alias: "评价时间",
selectorType: SelectorType.JsonPath,
selector: "$.created"
},
{
name:"content",
alias: "评价内容",
selectorType: SelectorType.JsonPath,
selector:"$.content"
},
{
name:"author",
alias: "评价者",
selectorType: SelectorType.JsonPath,
selector:"$.userInfo.uname"
},
{
name:"stock",
alias: "购买信息",
selectorType: SelectorType.JsonPath,
selector:"$.stock",
repeated: true
}
]
}
把分页数据汇总到一个数组的代码:
configs.afterExtractPage = function(page, data) {
if (!data.comments) return data;
// 4、将抽取的每页评价数据拼成一个数组返回
var comments = [];
for (var i = 0; i < data.comments.length; i++) {
var pageComments = data.comments[i];
for (var j = 0; j < pageComments.page_comments.length; j++) {
comments.push(pageComments.page_comments[j]);
}
}
data.comments = comments;
return data;
};
注:需要在抽取前先获取评论的总页数:
configs.afterDownloadPage = function(page, site){
var matches = /shop\.mogujie\.com\/detail/.exec(page.url);
if(matches){
// 如果当前下载的页面是内容页,需要先将要在抽取过程中发送的请求链接(获取评价)信息添加到页面中,方便抽取
// 1、首先从内容页获取评价的总数
var commentsCount = extract(page.raw, "//span[contains(text()[1],'评价')]/span");
commentsCount = parseInt(commentsCount);
var commentsPageCount = Math.ceil(commentsCount/20); //根据总评价数算出总评价页数
// 2、然后将评价的每个页码添加到内容页中返回处理
var extraHTML = '
'; // id设置为一个特殊的值,方便抽取for(var i=1;i<=commentsPageCount;i++){
extraHTML+=''+i+'';
}
extraHTML+='';
var index = page.raw.indexOf("");
page.raw = page.raw.substring(0, index) + extraHTML + page.raw.substring(index);
}
return page;
};
第二种方法,先请求所有分页评论,再添加到内容页中进行抽取
在afterDownloadPage函数中,先通过requestUrl请求和获取所有分页评论数据,然后再把数据添加到内容页中返回:
configs.afterDownloadPage = function(page, site){
if(!page.raw){
return page;
}
var matches = /shop\.mogujie\.com\/detail\/(.+?)\?/.exec(page.url);
if(matches){
// 1、首先从内容页获取评价的总数
var commentsCount = extract(page.raw, "//span[contains(text()[1],'评价')]/span");
commentsCount = parseInt(commentsCount);
var commentsPageCount = Math.ceil(commentsCount/20); //根据总评价数算出总评价页数
var extraHTML = '
'; // id设置为一个特殊的值,方便抽取// 2、分别请求和获取所有分页的评论,并组合成一个html
for(var index=1;index<=commentsPageCount;index++){
var comment = site.requestUrl("http://shop.mogujie.com/ajax/pc.rate.ratelist/v1?pageSize=20&sort=1&isNewDetail=1&itemId="+matches[1]+"&type=1&page="+index);
if(comment){
var json = JSON.parse(comment);
json = json.data.list;
for(var i=0;i
extraHTML+='
';extraHTML+=''+json[i].created+'';
extraHTML+=''+json[i].content+'';
extraHTML+=''+json[i].userInfo.uname+'';
extraHTML+='
';for(var j=0;j
extraHTML+=''+json[i].stock[j]+'';
}
extraHTML+='';
extraHTML+='';
}
}
}
extraHTML+='';
// 3、将评论数据html添加到内容页html中返回
var bodyIndex = page.raw.indexOf("");
page.raw = page.raw.substring(0, bodyIndex) + extraHTML + page.raw.substring(bodyIndex);
}
return page;
};
然后就可以很轻松地从field中抽取出来了:
{
name: "comments",
alias: "评价",
selector: "//div[@id='sjs']/div", // 4、抽取评论
repeated: true,
children: [
{
name: "create_time",
alias: "评价时间",
selector: "//span[@id='time']"
},
{
name:"content",
alias: "评价内容",
selector: "//span[@id='content']"
},
{
name:"author",
alias: "评价者",
selector: "//span[@id='author']"
},
{
name:"stock",
alias: "购买信息",
selector: "//div[@id='stock']/span",
repeated: true
}
]
}
简单对比一下:
1、第一种方式是在抽取过程中异步请求,然后通过回调函数汇总成一个数组;
2、第二种方式是在抽取前先提前获取所有评论数据,然后修改返回的内容页内容(把评论数据添加进去),再和其他数据一样抽取即可;
3、游牧老师建议大家使用第二种方式,因为自由度更大。
完整代码请看这里:http://www.shenjianshou.cn/index.php?r=demo/docs&demo_id=500006
网友评论