网络爬虫入门教程(二):configs详解
相关教程:
网络爬虫入门教程(一):概述
http://2879835984.iteye.com/blog/2306914
configs详解——之成员
爬虫的整体框架是这样:首先定义了一个configs对象, 里面设置了待爬网站的一些信息, 然后通过调用var crawler = new Crawler(configs);和crawler.start();来配置并启动爬虫.
configs对象中可以定义下面这些成员
domains
定义爬虫爬取哪些域名下的网页, 非域名下的url会被忽略以提高爬取速度
数组类型不能为空举个栗子:
domains: ["wallstreetcn.com"],
domains: ["zhihu.sogou.com", "zhihu.com"],
scanUrls
定义爬虫的入口链接, 爬虫从这些链接开始爬取,
同时这些链接也是监控爬虫所要监控的链接
数组类型不能为空
scanUrls的上限是1000,即爬虫每次最多处理1000个url
举个栗子:
scanUrls: ["http://wallstreetcn.com/news"],
scanUrls: ["http://club2011.auto.163.com/board/biaozhi308/r0a0t0g0bpeo0-n1.html", "http://club2011.auto.163.com/board/fengshen/r0a0t0g0bpeo0-n1.html"],
contentUrlRegexes
定义内容页url的规则
内容页是指包含要爬取内容的网页 比如http://www.qiushibaike.com/article/115878724就是糗事百科的一个内容页
数组类型正则表达式最好填写以提高爬取效率
举个栗子:
contentUrlRegexes: ["http://wallstreetcn\\.com/node/\\d+"],
contentUrlRegexes: ["http://club2011\\.auto\\.163\\.com/post/\\d+\\.html.*"],
特别需要注意的是,正则表达式中.和?都是需要转义的。
helperUrlRegexes
定义列表页url的规则
对于有列表页的网站, 使用此配置可以大幅提高爬虫的爬取速率
列表页是指包含内容页列表的网页 比如http://www.qiushibaike.com/8hr/page/2/?s=4867046就是糗事百科的一个列表页
数组类型正则表达式
举个栗子:
helperUrlRegexes: ["http://wallstreetcn\\.com/news(\\?/page=\\d+)?"],
helperUrlRegexes: ["http://club2011\\.auto\\.163\\.com/board/biaozhi308/r0a0t0g0bpeo0\\-n\\d+\\.html.*", "http://club2011\\.auto\\.163\\.com/board/fengshen/r0a0t0g0bpeo0\\-n\\d+\\.html.*"],
fields
定义内容页的抽取规则
规则由一个个field组成, 一个field代表一个数据抽取项
数组类型不能为空举个栗子:
fields: [
{
name: "content",
selector: "//*[@id='single-next-link']",
required: true
},
{
name: "author",
selector: "//div[contains(@class,'author')]//h2"
}
]
上面的例子从网页中抽取内容和作者, 抽取规则是针对糗事百科的内容页写的
field在configs详解——之field中作详细介绍。
enableProxy
是否使用代理
如果爬取的网站根据IP做了反爬虫, 可以设置此项为true
布尔类型可选设置举个栗子:
enableProxy: true,
interval
爬虫爬取每个网页的时间间隔
单位:毫秒
整型可选设置
interval最低设置为2000(毫秒),即2秒
举个栗子:
interval:5000,
configs详解——之field
field定义一个抽取项, 一个field可以定义下面这些东西
name
给此项数据起个变量名。变量名中不能包含.如果抓取到的数据想要以文章或者问答的形式发布到网站(WeCenter,WordPress,Discuz!等),
field的命名请参考两个完整demo中的命名, 否则无法发布成功
String类型不能为空
举个栗子:给field起了个名字叫question
{ name:"question", selector:"//*[@id='zh-question-title']/h2"}
selector
定义抽取规则, 默认使用XPath
如果使用其他类型的, 需要指定selectorType
String类型不能为空
举个栗子:使用XPath来抽取知乎问答网页的问题,selector的值就是问题的XPath
{ name:"question"
, selector:"//*[@id='zh-question-title']/h2"}
selectorType
抽取规则的类型
目前可用SelectorType.XPath,SelectorType.JsonPath,SelectorType.Regex
默认SelectorType.XPath
枚举类型
栗子1:selector默认使用XPath
{ name:"question"
, selector:"//*[@id='zh-question-title']/h2"// XPath抽取规则}
栗子2:selector使用JsonPath,如果内容是Json数据格式,则使用JsonPath抽取数据比较方便
{ name:"question_answer_content"
, selectorType: SelectorType.JsonPath, selector:"$.comment.content",// JsonPath抽取规则required:true}
栗子3:除了XPath和JsonPath之外,神箭手还支持使用正则表达式来抽取数据
{
name: "title",
selectorType: SelectorType.Regex,
selector: '
[^\\/]+<\\/h1>' // Regex抽取规则
}
required
定义该field的值是否必须, 默认false
赋值为true的话, 如果该field没有抽取到内容, 该field对应的整条数据都将被丢弃
布尔类型
举个栗子:
{ name:"article_title"
, selector:"//div[contains(@class,'location')]/text()[3]", required:true}
repeated
定义该field抽取到的内容是否是有多项, 默认false
赋值为true的话, 无论该field是否真的是有多项, 抽取到的结果都是数组结构
布尔类型
举个栗子:爬取的网页中包含多条评论,所以抽取评论的时候要将repeated赋值为true
{ name:"comments"
, selector:"//*[@id='zh-single-question-page']//a[contains(@class,'zm-item-tag')]", repeated:true}
children
为此field定义子项
子项的定义仍然是一个fields结构, 即一个field对象的数组没错, 这是一个树形结构
数组类型
举个栗子:抓取知乎问答网页的回答,每个回答爬取了内容,作者,赞同数
{
name: "answers",
selector: "//*[@id=\"zh-question-answer-wrap\"]/div",
repeated: true,
children: [
{
name: "content",
selector: "//div[contains(@class,\"zm-editable-content\")]",
required: true
},
{
name: "author",
selector: "//a[@class=\"author-link\"]"
},
{
name: "agree_count",
selector: "//button[@class=\"up\"]/span[@class=\"count\"]"
}
]
}
sourceType
该field的数据源, 默认从当前的网页中抽取数据
选择SourceType.AttachedUrl可以发起一个新的请求, 然后从请求返回的数据中抽取
选择SourceType.UrlContext可以从当前网页的url附加数据中抽取
url附加数据后面会作介绍
枚举类型
attachedUrl
当sourceType设置为SourceType.AttachedUrl时, 定义新请求的url
String类型
举个栗子:当爬取的网页中某些内容需要异步加载请求时,就需要使用attachedUrl,比如,抓取知乎回答中的评论部分,就是通过AJAX异步请求的数据
{
name: "comment_id",
selector: "//div/@data-aid",
},
{
name: "comments",
sourceType: SourceType.AttachedUrl,
// "comments"是从发送"attachedUrl"这个异步请求返回的数据中抽取的
// "attachedUrl"支持引用上下文中的抓取到的"field", 这里就引用了上面抓取的"comment_id"
attachedUrl: "https://www.zhihu.com/r/answers/{comment_id}/comments",
selectorType: SelectorType.JsonPath,
selector: "$.data",
repeated: true,
children: [
...
]
}
UrlContext
当sourceType赋值为SourceType.UrlContext时, 表示从内容页中的附加数据(是开发者自定义的一段代码,例如,html代码)中抽取数据
String类型
举个栗子:将自定义数据附加到内容页中,然后再提取到field
var configs = {
// configs中的其他成员
...
fields: [
{
name: "extra_data",
// 这里是从开发者附加的一段html代码中抽取的数据
sourceType: SourceType.UrlContext,
selector: "//span[contains(@class,'shenjianshou')]",
}
]
};
configs.onProcessHelperPage = function(page, content, site) {
// 定义附加数据
var extraData = '
100
';
// 将extraData附加到contentUrl对应的网页中,将contentUrl添加到待爬队列中
site.addUrl(contentUrl, "get", null, extraData);
...
return false;
}
transient
定义该field是否被移除, 默认false
赋值为true的话, 在调用afterExtractPage回调函数(在configs详解——之回调函数中会详细描述)后,才会移除包含transient字段的field
布尔类型
举个栗子:爬取的数据中要移除某个field时,要将transient赋值为true
{
name: "avatar",
selector: "//div[contains(@class,'bbs_detail_item')][1]//a[1]/img",
transient: true
}
configs详解——之site, page和consolesite
site
site表示当前正在爬取的网站的对象,下面介绍了可以调用的函数
site.addHeader(key, value)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Header
@param key Header的key, 如User-Agent,Referer等
@param value Header的值
举个栗子:
Referer是HTTP请求Header的一个属性,http://buluo.qq.com/p/index.html是Referer的值
configs.beforeCrawl = function(site) {
site.addHeader("Referer", "http://buluo.qq.com/p/index.html");
}
site.addCookie(key, value)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Cookie
@param key Cookie的key
@param value Cookie的值
举个栗子:
cookie是由键-值对组成的,BAIDUID是cookie的key,FEE96299191CB0F11954F3A0060FB470:FG=1则是cookie的值
configs.beforeCrawl = function(site) {
site.addCookie("BAIDUID", "FEE96299191CB0F11954F3A0060FB470:FG=1");}
site.addCookies(cookies)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Cookie
@param cookies 多个Cookie组成的字符串
举个栗子:
cookies是多个cookie的键-值对组成的字符串,用;分隔。BAIDUID和BIDUPSID是cookie的key,FEE96299191CB0F11954F3A0060FB470:FG=1和FEE96299191CB0F11954F3A0060FB470是cookie的值,键-值对用=相连
configs.beforeCrawl = function(site) {
site.addCookies("BAIDUID=FEE96299191CB0F11954F3A0060FB470:FG=1; BIDUPSID=FEE96299191CB0F11954F3A0060FB470;");
site.addUrl(url, options)
一般在onProcessScanPage和onProcessHelperPage回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来往待爬队列中添加url
@param url 待添加的url
@param options javascript对象,成员包括method、data、contextData、headers和reserve,如下所示:
@param options.method 默认为"GET"请求,也支持"POST"请求
@param options.data 发送请求时需添加的参数,可以为空
@param options.contextData 此url附加的数据, 可以为空
@param options.headers 此url的Headers, 可以为空
@param options.reserve 此url是否去重, 默认为false(设置为false,表示之前处理过的url不再插入待爬队列;设置为true,表示之前处理过的url也会插入待爬队列)
兼容之前版本site.addUrl(url, method, data, contextData)
栗子1:
configs.onProcessHelperPage = function(page, content, site) {
var regex = /https?:\\\/\\\/www\.jiemian\.com\\\/article\\\/\d+\.html/g;
var urls = [];
urls = content.match(regex);
if (urls != "") {
for (var i = 0, n = urls.length; i < n; i++) {
urls[i] = urls[i].replace(/\\/g, "");
// get请求,未添加options对象
site.addUrl(urls[i]);
}
}
...
return false;
}
栗子2:
configs.onProcessHelperPage = function(page, content, site) {
...
var nextUrl = page.url.replace("page="+currentPageNum, "page="+pageNum);
// 定义"options"对象,包括"method"和"data"两个成员
var options = {
method: "POST",
data: {
page: pageNum,
size: 18
}
};
// post请求,添加options对象
site.addUrl(nextUrl, options);
return false;
site.requestUrl(url, options)
一般在beforeCrawl, afterDownloadPage, onProcessScanPage和onProcessHelperPage回调函数(在configs详解——之回调函数中会详细描述)中调用, 下载网页, 得到网页内容
@param url 待下载的url
@param options javascript对象,成员包括method、data和headers,如下所示:
@param options.method 默认为"GET"请求,也支持"POST"请求
@param options.data 发送请求时需添加的参数,可以为空
@param options.headers 此url的Headers, 可以为空
兼容之前版本site.requestUrl(url, method, data)
举个栗子:
configs.afterDownloadPage = function(page, site) {
var url = "https://checkcoverage.apple.com/cn/zh/?sn=FK1QPNCEGRYD";
// 定义"options"对象,包括"method"和"data"两个成员
var options = {
method: "POST",
data: {
sno: "FK1QPNCEGRYD",
CSRFToken: result[1]
}
};
// 通过发送带参数的POST请求,下载网页,并将网页内容赋值给result
var result = site.requestUrl(url, options);
...
return page;
}
site.setUserAgent(userAgent)
一般在beforeCrawl等回调函数(在configs详解——之回调函数中会详细描述)中调用, 设置浏览器userAgent
@param userAgent 需添加的userAgent
默认使用userAgent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
查看“常见浏览器userAgent大全”
举个栗子:
configs.beforeCrawl = function(site) {
var userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/";
site.setUserAgent(userAgent);
}
page
page表示当前正在爬取的网页页面的对象,下面介绍了可以调用的变量和函数
page.url
当前正在爬取的网页页面的url
举个栗子:
在afterExtractPage回调函数(在configs详解——之回调函数中会详细描述)中,将url值赋给名叫article_content的field
configs.afterExtractPage = function(page, data) {
data.article_content = page.url + "";
return data;
}
page.raw
当前网页原始内容
举个栗子:
在onProcessScanPage回调函数(在configs详解——之回调函数中会详细描述)中,通过page.raw得到网页原始内容,然后转换成Json对象
configs.onProcessScanPage = function(page, content, site) {
var jsonObj = JSON.parse(page.raw);
...
return false;
}
page.skip(fieldName)
一般在afterExtractField回调函数(在configs详解——之回调函数中会详细描述)中使用, 用来忽略当前网页的抽取结果或当前上下文的一条结果
@param fieldName 根据fieldName来确定忽略范围,可以为空。当为空时,忽略当前网页的抽取结果
栗子1:
例如爬取知乎问答的时候, 想只爬取话题中包含经济学的问答, 可以这样过滤:
configs.afterExtractField = function(fieldName, data) {
if (fieldName == "topics") { // fieldName是topics
if (data.indexOf("经济学") == -1) { // data中不包含"经济学"
page.skip(); // 跳过当前爬取的网页
}
}
return data;
}
栗子2:
例如爬取知乎问答的时候, 想要只爬取赞同数大于10的回答, 可以这样过滤:
configs = {
// configs中的其他成员
...
fields: [
{
name: "answers",
selector: "//*[@id=\"zh-question-answer-wrap\"]/div",
repeated: true,
children: [
{
name: "agree_count",
selector: "//button[@class=\"up\"]/span[@class=\"count\"]"
},
...
]
}
]
};
configs.afterExtractField = function(fieldName, data, page) {
if (fieldName == "answers.agree_count") { // fieldName是answers数组中的agree_count元素名
if (parseInt(data) < 10) { // 回答的点赞数小于10
page.skip("answers"); // 跳过这个回答
}
}
return data;
}
console
console对象提供不同级别的日志打印
console.log(message)
打印日志, 调试时使用
举个栗子:
console.log("测试log日志");
console.debug(message)
打印出错级别日志, 调试时使用
举个栗子:
console.debug("正在提取文章标题数据");
console.info(message)
打印普通日志
举个栗子:
console.info("成功处理一个页面");
console.warn(message)
打印警告情况的日志
举个栗子:
console.warn("XX文件解析失败");
console.error(message)
打印错误情况的日志
举个栗子:
console.error("XPath错误");
configs详解——之回调函数
回调函数是在爬虫爬取并处理网页的过程中设置的一些系统钩子, 通过这些钩子可以完成一些特殊的处理逻辑.
回调函数需要设置到configs参数里面才起作用
下图是神箭手云爬虫爬取并处理网页的流程图,矩形方框中标识了爬虫运行过程中所使用的重要回调函数:
beforeCrawl(site)
爬虫初始化时调用, 用来指定一些爬取前的操作
@param site 当前正在爬取的网站的对象
在函数中可以调用site.addHeader(key, value), site.addCookie(key, value),site.addCookies(cookies)等
举个栗子:
在爬虫开始爬取之前给所有待爬网页添加一个Header
configs.beforeCrawl = function(site) {
site.addHeader("Referer", "http://buluo.qq.com/p/index.html");
}
isAntiSpider(url, content)
判断当前网页是否被反爬虫了, 需要开发者实现
@param url 当前网页的url
@param content 当前网页内容
@return 如果被反爬虫了, 返回true, 否则返回false
举个栗子:
configs.isAntiSpider = function(url, content) {
if (content.indexOf("404页面不存在") > -1) { // content中包含"404页面不存在"字符串
return true; // 返回当前网页被反爬虫了
}
return false;
}
afterDownloadPage(page, site)
在一个网页下载完成之后调用. 主要用来对下载的网页进行处理.
@param page 当前下载的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回处理后的网页
举个栗子:
比如下载了某个网页,希望向网页的body中添加html标签,处理过程如下:
configs.afterDownloadPage = function(page, site) {
var pageHtml = "5";
var index = page.raw.indexOf("");
page.raw = page.raw.substring(0, index) + pageHtml + page.raw.substring(index);
return page;
}
onProcessScanPage(page, content, site)
在爬取到入口url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据.
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容,调用page.skip()便不再爬取当前网页
@param content 当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回false表示不需要再从此网页中发现待爬url
此函数中通过调用site.addUrl(url, options)函数来添加新的url到待爬队列。
栗子1:
实现这个回调函数并返回false,表示爬虫在处理这个scanUrl的时候,不会从中提取待爬url
configs.onProcessScanPage = function(page, content, site) {
return false;
}
栗子2:
生成一个新的url添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url
configs.onProcessScanPage = function(page, content, site) {
var jsonObj = JSON.parse(page.raw);
for (var i = 0, n = jsonObj.data.length; i < n; i++) {
var item = jsonObj.data[i];
var lastid = item._id;
// 生成一个新的url
var url = page.url + lastid;
// 将新的url插入待爬队列中
site.addUrl(url);
}
// 通知爬虫不再从当前网页中发现待爬url
return false;
}
onProcessHelperPage(page, content, site)
在爬取到列表页url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据.
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容,调用page.skip()便不再爬取当前网页
@param content 当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回false表示不需要再从此网页中发现待爬url
此函数中通过调用site.addUrl(url, options)函数来添加新的url到待爬队列
栗子1:
实现这个回调函数并返回false,表示爬虫在处理这个helperUrl的时候,不会从中提取待爬url
configs.onProcessHelperPage = function(page, content, site) {
return false;
}
栗子2:
在onProcessHelperPage回调函数中,生成新的contentUrl并添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url
configs.onProcessHelperPage = function(page, content, site) {
var jsonObj = JSON.parse(content);
var id = 0;
for (var i = 0, n = jsonObj.data.length; i < n; i++) {
var item = jsonObj.data[i];
id = item._id;
// 将新的url插入待爬队列中
site.addUrl("http://service.chanjet.com/api/v1/message/"+id);
}
// 通知爬虫不再从当前网页中发现待爬url
return false;
}
beforeHandleImg(fieldName, img)
在抽取到field内容之后调用, 对其中包含的img标签进行回调处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param img 整个img标签的内容
@return 返回处理后的img标签的内容
很多网站对图片作了延迟加载, 这时候就需要在这个函数里面来处理
举个栗子:
汽车之家论坛帖子的图片大部分是延迟加载的,默认会使用http://x.autoimg.cn/club/lazyload.png 图片url,我们需要找到真实的图片url并替换,具体实现如下:
configs.beforeHandleImg = function(fieldName, img) {
// 通过正则判断img中的src属性是不是真实图片url,如果是,则直接返回img,如果不是,继续执行
var regex = /src="(https?:\/\/.*?)"/;
var rs = regex.exec(img);
if (!rs) return img;
var url = rs[1];
if (url == "http://x.autoimg.cn/club/lazyload.png") {
var regex2 = /src9="(https?:\/\/.*?)"/;
rs = regex2.exec(img);
// 替换成真实图片url
if (rs) {
var newUrl = rs[1];
img = img.replace(url, newUrl);
}
}
return img;
}
beforeCacheImg(fieldName, url)
由于很多网站都有图片防盗链限制, 所以神箭手会缓存爬到的图片, 在缓存之前, 可以对图片作处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param url 图片的url
@return 处理后的图片url
举个栗子:
知乎问答页面, 用户的头像链接是这样的: https://pic3.zhimg.com/xxxxxx_s.jpg
研究一下可以发现, 大一点的头像是这样的: https://pic3.zhimg.com/xxxxxx_l.jpg
configs.beforeCacheImage = function(fieldName, url) {
if (fieldName == "answers.avatar") {
return url.replace("_s.jpg", "_l.jpg"); // 对url进行字符串替换,得到较大图片的url
}
return url; // 返回图片url
}
afterExtractField(fieldName, data, page)
当一个field的内容被抽取到后进行的回调, 在此回调中可以对网页中抽取的内容作进一步处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param data 当前field抽取到的数据. 如果该field是repeated, data为数组类型, 否则是String
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.skip()便不再爬取当前网页
@return 返回处理后的数据, 注意数据类型需要跟传进来的data类型匹配
举个栗子:
比如爬取知乎用户的性别信息, 相关网页源码如下:
那么可以这样写:
configs = {
// configs的其他成员
...
fields: [
{
name: "gender",
selector: "//span[contains(@class, 'gender')]/i/@class"
}
]
...
};
configs.afterExtractField = function(fieldName, data, page) {
if (fieldName == "gender") {
if (data.indexOf("icon-profile-male") > -1) { // data中包含"icon-profile-male",说明当前知乎用户是男性
return "男";
}
else if (data.indexOf("icon-profile-female") > -1) { // data中包含"icon-profile-female",说明当前知乎用户是女性
return "女";
}
else { // data未匹配到上面的两个字符串,无法判断用户性别
return "未知";
}
}
return data;
}
afterExtractPage(page, data)
在一个网页的所有field抽取完成之后, 可能需要对field进一步处理, 以发布到自己的网站
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.skip()便不再爬取当前网页
@param data 当前网页抽取出来的所有field的数据, JavaScript对象
@return 返回处理后的数据, 注意数据类型不能变
举个栗子:
比如从某网页中得到time和title两个field抽取项, 现在希望把time的值添加中括号后拼凑到title中,处理过程如下:
configs.afterExtractPage = function(page, data) {
var title = "[" + data.time + "] " + data.title;
data.title = title;
return data;
}
nextScanUrl(url)
在处理完待爬队列中的url后,生成下一个scanUrl并插入到待爬队列中
@param url 当前的scanUrl
@return 返回下一个scanUrl或返回null
举个栗子:
某个网站有1500个scanUrl,需先处理前1000个scanUrl,再调用nextScanUrl回调函数来逐个处理后面500个,处理过程如下:
// 增加1000个"scanUrl",一个爬虫最多一次性处理1000个"scanUrl"
for (var i = 1; i <= 1000; i++) {
scanUrls.push("http://www.dianping.com/search/keyword/" + i + "/0_" + keywords);
}
var configs = {
// "configs"的其他成员
...
scanUrls: scanUrls,
...
};
configs.nextScanUrl = function(url) {
var currentPage = url.substring(url.indexOf("keyword/") + 8, url.indexOf("/0_"));
// 页面数超过1500以后,返回空值
if (currentPage > 1500) {
return null;
}
else {
var nextPage = currentPage + 1;
var nextScanUrl = url.replace("keyword/" + currentPage, "keyword/" + nextPage);
// 将下一个"scanUrl"插入待爬队列
return nextScanUrl;
}
};
对爬虫感兴趣的童鞋可以加腾讯群讨论:342953471。
网络爬虫入门教程(一):概述
http://2879835984.iteye.com/blog/2306914
configs详解——之成员
爬虫的整体框架是这样:首先定义了一个configs对象, 里面设置了待爬网站的一些信息, 然后通过调用var crawler = new Crawler(configs);和crawler.start();来配置并启动爬虫.
configs对象中可以定义下面这些成员
domains
定义爬虫爬取哪些域名下的网页, 非域名下的url会被忽略以提高爬取速度
数组类型不能为空举个栗子:
domains: ["wallstreetcn.com"],
domains: ["zhihu.sogou.com", "zhihu.com"],
scanUrls
定义爬虫的入口链接, 爬虫从这些链接开始爬取,
同时这些链接也是监控爬虫所要监控的链接
数组类型不能为空
scanUrls的上限是1000,即爬虫每次最多处理1000个url
举个栗子:
scanUrls: ["http://wallstreetcn.com/news"],
scanUrls: ["http://club2011.auto.163.com/board/biaozhi308/r0a0t0g0bpeo0-n1.html", "http://club2011.auto.163.com/board/fengshen/r0a0t0g0bpeo0-n1.html"],
contentUrlRegexes
定义内容页url的规则
内容页是指包含要爬取内容的网页 比如http://www.qiushibaike.com/article/115878724就是糗事百科的一个内容页
数组类型正则表达式最好填写以提高爬取效率
举个栗子:
contentUrlRegexes: ["http://wallstreetcn\\.com/node/\\d+"],
contentUrlRegexes: ["http://club2011\\.auto\\.163\\.com/post/\\d+\\.html.*"],
特别需要注意的是,正则表达式中.和?都是需要转义的。
helperUrlRegexes
定义列表页url的规则
对于有列表页的网站, 使用此配置可以大幅提高爬虫的爬取速率
列表页是指包含内容页列表的网页 比如http://www.qiushibaike.com/8hr/page/2/?s=4867046就是糗事百科的一个列表页
数组类型正则表达式
举个栗子:
helperUrlRegexes: ["http://wallstreetcn\\.com/news(\\?/page=\\d+)?"],
helperUrlRegexes: ["http://club2011\\.auto\\.163\\.com/board/biaozhi308/r0a0t0g0bpeo0\\-n\\d+\\.html.*", "http://club2011\\.auto\\.163\\.com/board/fengshen/r0a0t0g0bpeo0\\-n\\d+\\.html.*"],
fields
定义内容页的抽取规则
规则由一个个field组成, 一个field代表一个数据抽取项
数组类型不能为空举个栗子:
fields: [
{
name: "content",
selector: "//*[@id='single-next-link']",
required: true
},
{
name: "author",
selector: "//div[contains(@class,'author')]//h2"
}
]
上面的例子从网页中抽取内容和作者, 抽取规则是针对糗事百科的内容页写的
field在configs详解——之field中作详细介绍。
enableProxy
是否使用代理
如果爬取的网站根据IP做了反爬虫, 可以设置此项为true
布尔类型可选设置举个栗子:
enableProxy: true,
interval
爬虫爬取每个网页的时间间隔
单位:毫秒
整型可选设置
interval最低设置为2000(毫秒),即2秒
举个栗子:
interval:5000,
configs详解——之field
field定义一个抽取项, 一个field可以定义下面这些东西
name
给此项数据起个变量名。变量名中不能包含.如果抓取到的数据想要以文章或者问答的形式发布到网站(WeCenter,WordPress,Discuz!等),
field的命名请参考两个完整demo中的命名, 否则无法发布成功
String类型不能为空
举个栗子:给field起了个名字叫question
{ name:"question", selector:"//*[@id='zh-question-title']/h2"}
selector
定义抽取规则, 默认使用XPath
如果使用其他类型的, 需要指定selectorType
String类型不能为空
举个栗子:使用XPath来抽取知乎问答网页的问题,selector的值就是问题的XPath
{ name:"question"
, selector:"//*[@id='zh-question-title']/h2"}
selectorType
抽取规则的类型
目前可用SelectorType.XPath,SelectorType.JsonPath,SelectorType.Regex
默认SelectorType.XPath
枚举类型
栗子1:selector默认使用XPath
{ name:"question"
, selector:"//*[@id='zh-question-title']/h2"// XPath抽取规则}
栗子2:selector使用JsonPath,如果内容是Json数据格式,则使用JsonPath抽取数据比较方便
{ name:"question_answer_content"
, selectorType: SelectorType.JsonPath, selector:"$.comment.content",// JsonPath抽取规则required:true}
栗子3:除了XPath和JsonPath之外,神箭手还支持使用正则表达式来抽取数据
{
name: "title",
selectorType: SelectorType.Regex,
selector: '
[^\\/]+<\\/h1>' // Regex抽取规则
}
required
定义该field的值是否必须, 默认false
赋值为true的话, 如果该field没有抽取到内容, 该field对应的整条数据都将被丢弃
布尔类型
举个栗子:
{ name:"article_title"
, selector:"//div[contains(@class,'location')]/text()[3]", required:true}
repeated
定义该field抽取到的内容是否是有多项, 默认false
赋值为true的话, 无论该field是否真的是有多项, 抽取到的结果都是数组结构
布尔类型
举个栗子:爬取的网页中包含多条评论,所以抽取评论的时候要将repeated赋值为true
{ name:"comments"
, selector:"//*[@id='zh-single-question-page']//a[contains(@class,'zm-item-tag')]", repeated:true}
children
为此field定义子项
子项的定义仍然是一个fields结构, 即一个field对象的数组没错, 这是一个树形结构
数组类型
举个栗子:抓取知乎问答网页的回答,每个回答爬取了内容,作者,赞同数
{
name: "answers",
selector: "//*[@id=\"zh-question-answer-wrap\"]/div",
repeated: true,
children: [
{
name: "content",
selector: "//div[contains(@class,\"zm-editable-content\")]",
required: true
},
{
name: "author",
selector: "//a[@class=\"author-link\"]"
},
{
name: "agree_count",
selector: "//button[@class=\"up\"]/span[@class=\"count\"]"
}
]
}
sourceType
该field的数据源, 默认从当前的网页中抽取数据
选择SourceType.AttachedUrl可以发起一个新的请求, 然后从请求返回的数据中抽取
选择SourceType.UrlContext可以从当前网页的url附加数据中抽取
url附加数据后面会作介绍
枚举类型
attachedUrl
当sourceType设置为SourceType.AttachedUrl时, 定义新请求的url
String类型
举个栗子:当爬取的网页中某些内容需要异步加载请求时,就需要使用attachedUrl,比如,抓取知乎回答中的评论部分,就是通过AJAX异步请求的数据
{
name: "comment_id",
selector: "//div/@data-aid",
},
{
name: "comments",
sourceType: SourceType.AttachedUrl,
// "comments"是从发送"attachedUrl"这个异步请求返回的数据中抽取的
// "attachedUrl"支持引用上下文中的抓取到的"field", 这里就引用了上面抓取的"comment_id"
attachedUrl: "https://www.zhihu.com/r/answers/{comment_id}/comments",
selectorType: SelectorType.JsonPath,
selector: "$.data",
repeated: true,
children: [
...
]
}
UrlContext
当sourceType赋值为SourceType.UrlContext时, 表示从内容页中的附加数据(是开发者自定义的一段代码,例如,html代码)中抽取数据
String类型
举个栗子:将自定义数据附加到内容页中,然后再提取到field
var configs = {
// configs中的其他成员
...
fields: [
{
name: "extra_data",
// 这里是从开发者附加的一段html代码中抽取的数据
sourceType: SourceType.UrlContext,
selector: "//span[contains(@class,'shenjianshou')]",
}
]
};
configs.onProcessHelperPage = function(page, content, site) {
// 定义附加数据
var extraData = '
100
';
// 将extraData附加到contentUrl对应的网页中,将contentUrl添加到待爬队列中
site.addUrl(contentUrl, "get", null, extraData);
...
return false;
}
transient
定义该field是否被移除, 默认false
赋值为true的话, 在调用afterExtractPage回调函数(在configs详解——之回调函数中会详细描述)后,才会移除包含transient字段的field
布尔类型
举个栗子:爬取的数据中要移除某个field时,要将transient赋值为true
{
name: "avatar",
selector: "//div[contains(@class,'bbs_detail_item')][1]//a[1]/img",
transient: true
}
configs详解——之site, page和consolesite
site
site表示当前正在爬取的网站的对象,下面介绍了可以调用的函数
site.addHeader(key, value)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Header
@param key Header的key, 如User-Agent,Referer等
@param value Header的值
举个栗子:
Referer是HTTP请求Header的一个属性,http://buluo.qq.com/p/index.html是Referer的值
configs.beforeCrawl = function(site) {
site.addHeader("Referer", "http://buluo.qq.com/p/index.html");
}
site.addCookie(key, value)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Cookie
@param key Cookie的key
@param value Cookie的值
举个栗子:
cookie是由键-值对组成的,BAIDUID是cookie的key,FEE96299191CB0F11954F3A0060FB470:FG=1则是cookie的值
configs.beforeCrawl = function(site) {
site.addCookie("BAIDUID", "FEE96299191CB0F11954F3A0060FB470:FG=1");}
site.addCookies(cookies)
一般在beforeCrawl回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来添加一些HTTP请求的Cookie
@param cookies 多个Cookie组成的字符串
举个栗子:
cookies是多个cookie的键-值对组成的字符串,用;分隔。BAIDUID和BIDUPSID是cookie的key,FEE96299191CB0F11954F3A0060FB470:FG=1和FEE96299191CB0F11954F3A0060FB470是cookie的值,键-值对用=相连
configs.beforeCrawl = function(site) {
site.addCookies("BAIDUID=FEE96299191CB0F11954F3A0060FB470:FG=1; BIDUPSID=FEE96299191CB0F11954F3A0060FB470;");
site.addUrl(url, options)
一般在onProcessScanPage和onProcessHelperPage回调函数(在configs详解——之回调函数中会详细描述)中调用, 用来往待爬队列中添加url
@param url 待添加的url
@param options javascript对象,成员包括method、data、contextData、headers和reserve,如下所示:
@param options.method 默认为"GET"请求,也支持"POST"请求
@param options.data 发送请求时需添加的参数,可以为空
@param options.contextData 此url附加的数据, 可以为空
@param options.headers 此url的Headers, 可以为空
@param options.reserve 此url是否去重, 默认为false(设置为false,表示之前处理过的url不再插入待爬队列;设置为true,表示之前处理过的url也会插入待爬队列)
兼容之前版本site.addUrl(url, method, data, contextData)
栗子1:
configs.onProcessHelperPage = function(page, content, site) {
var regex = /https?:\\\/\\\/www\.jiemian\.com\\\/article\\\/\d+\.html/g;
var urls = [];
urls = content.match(regex);
if (urls != "") {
for (var i = 0, n = urls.length; i < n; i++) {
urls[i] = urls[i].replace(/\\/g, "");
// get请求,未添加options对象
site.addUrl(urls[i]);
}
}
...
return false;
}
栗子2:
configs.onProcessHelperPage = function(page, content, site) {
...
var nextUrl = page.url.replace("page="+currentPageNum, "page="+pageNum);
// 定义"options"对象,包括"method"和"data"两个成员
var options = {
method: "POST",
data: {
page: pageNum,
size: 18
}
};
// post请求,添加options对象
site.addUrl(nextUrl, options);
return false;
site.requestUrl(url, options)
一般在beforeCrawl, afterDownloadPage, onProcessScanPage和onProcessHelperPage回调函数(在configs详解——之回调函数中会详细描述)中调用, 下载网页, 得到网页内容
@param url 待下载的url
@param options javascript对象,成员包括method、data和headers,如下所示:
@param options.method 默认为"GET"请求,也支持"POST"请求
@param options.data 发送请求时需添加的参数,可以为空
@param options.headers 此url的Headers, 可以为空
兼容之前版本site.requestUrl(url, method, data)
举个栗子:
configs.afterDownloadPage = function(page, site) {
var url = "https://checkcoverage.apple.com/cn/zh/?sn=FK1QPNCEGRYD";
// 定义"options"对象,包括"method"和"data"两个成员
var options = {
method: "POST",
data: {
sno: "FK1QPNCEGRYD",
CSRFToken: result[1]
}
};
// 通过发送带参数的POST请求,下载网页,并将网页内容赋值给result
var result = site.requestUrl(url, options);
...
return page;
}
site.setUserAgent(userAgent)
一般在beforeCrawl等回调函数(在configs详解——之回调函数中会详细描述)中调用, 设置浏览器userAgent
@param userAgent 需添加的userAgent
默认使用userAgent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
查看“常见浏览器userAgent大全”
举个栗子:
configs.beforeCrawl = function(site) {
var userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/";
site.setUserAgent(userAgent);
}
page
page表示当前正在爬取的网页页面的对象,下面介绍了可以调用的变量和函数
page.url
当前正在爬取的网页页面的url
举个栗子:
在afterExtractPage回调函数(在configs详解——之回调函数中会详细描述)中,将url值赋给名叫article_content的field
configs.afterExtractPage = function(page, data) {
data.article_content = page.url + "";
return data;
}
page.raw
当前网页原始内容
举个栗子:
在onProcessScanPage回调函数(在configs详解——之回调函数中会详细描述)中,通过page.raw得到网页原始内容,然后转换成Json对象
configs.onProcessScanPage = function(page, content, site) {
var jsonObj = JSON.parse(page.raw);
...
return false;
}
page.skip(fieldName)
一般在afterExtractField回调函数(在configs详解——之回调函数中会详细描述)中使用, 用来忽略当前网页的抽取结果或当前上下文的一条结果
@param fieldName 根据fieldName来确定忽略范围,可以为空。当为空时,忽略当前网页的抽取结果
栗子1:
例如爬取知乎问答的时候, 想只爬取话题中包含经济学的问答, 可以这样过滤:
configs.afterExtractField = function(fieldName, data) {
if (fieldName == "topics") { // fieldName是topics
if (data.indexOf("经济学") == -1) { // data中不包含"经济学"
page.skip(); // 跳过当前爬取的网页
}
}
return data;
}
栗子2:
例如爬取知乎问答的时候, 想要只爬取赞同数大于10的回答, 可以这样过滤:
configs = {
// configs中的其他成员
...
fields: [
{
name: "answers",
selector: "//*[@id=\"zh-question-answer-wrap\"]/div",
repeated: true,
children: [
{
name: "agree_count",
selector: "//button[@class=\"up\"]/span[@class=\"count\"]"
},
...
]
}
]
};
configs.afterExtractField = function(fieldName, data, page) {
if (fieldName == "answers.agree_count") { // fieldName是answers数组中的agree_count元素名
if (parseInt(data) < 10) { // 回答的点赞数小于10
page.skip("answers"); // 跳过这个回答
}
}
return data;
}
console
console对象提供不同级别的日志打印
console.log(message)
打印日志, 调试时使用
举个栗子:
console.log("测试log日志");
console.debug(message)
打印出错级别日志, 调试时使用
举个栗子:
console.debug("正在提取文章标题数据");
console.info(message)
打印普通日志
举个栗子:
console.info("成功处理一个页面");
console.warn(message)
打印警告情况的日志
举个栗子:
console.warn("XX文件解析失败");
console.error(message)
打印错误情况的日志
举个栗子:
console.error("XPath错误");
configs详解——之回调函数
回调函数是在爬虫爬取并处理网页的过程中设置的一些系统钩子, 通过这些钩子可以完成一些特殊的处理逻辑.
回调函数需要设置到configs参数里面才起作用
下图是神箭手云爬虫爬取并处理网页的流程图,矩形方框中标识了爬虫运行过程中所使用的重要回调函数:
beforeCrawl(site)
爬虫初始化时调用, 用来指定一些爬取前的操作
@param site 当前正在爬取的网站的对象
在函数中可以调用site.addHeader(key, value), site.addCookie(key, value),site.addCookies(cookies)等
举个栗子:
在爬虫开始爬取之前给所有待爬网页添加一个Header
configs.beforeCrawl = function(site) {
site.addHeader("Referer", "http://buluo.qq.com/p/index.html");
}
isAntiSpider(url, content)
判断当前网页是否被反爬虫了, 需要开发者实现
@param url 当前网页的url
@param content 当前网页内容
@return 如果被反爬虫了, 返回true, 否则返回false
举个栗子:
configs.isAntiSpider = function(url, content) {
if (content.indexOf("404页面不存在") > -1) { // content中包含"404页面不存在"字符串
return true; // 返回当前网页被反爬虫了
}
return false;
}
afterDownloadPage(page, site)
在一个网页下载完成之后调用. 主要用来对下载的网页进行处理.
@param page 当前下载的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回处理后的网页
举个栗子:
比如下载了某个网页,希望向网页的body中添加html标签,处理过程如下:
configs.afterDownloadPage = function(page, site) {
var pageHtml = "5";
var index = page.raw.indexOf("");
page.raw = page.raw.substring(0, index) + pageHtml + page.raw.substring(index);
return page;
}
onProcessScanPage(page, content, site)
在爬取到入口url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据.
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容,调用page.skip()便不再爬取当前网页
@param content 当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回false表示不需要再从此网页中发现待爬url
此函数中通过调用site.addUrl(url, options)函数来添加新的url到待爬队列。
栗子1:
实现这个回调函数并返回false,表示爬虫在处理这个scanUrl的时候,不会从中提取待爬url
configs.onProcessScanPage = function(page, content, site) {
return false;
}
栗子2:
生成一个新的url添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url
configs.onProcessScanPage = function(page, content, site) {
var jsonObj = JSON.parse(page.raw);
for (var i = 0, n = jsonObj.data.length; i < n; i++) {
var item = jsonObj.data[i];
var lastid = item._id;
// 生成一个新的url
var url = page.url + lastid;
// 将新的url插入待爬队列中
site.addUrl(url);
}
// 通知爬虫不再从当前网页中发现待爬url
return false;
}
onProcessHelperPage(page, content, site)
在爬取到列表页url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据.
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.raw可获取当前网页内容,调用page.skip()便不再爬取当前网页
@param content 当前网页内容
@param site 当前正在爬取的网站的对象
@return 返回false表示不需要再从此网页中发现待爬url
此函数中通过调用site.addUrl(url, options)函数来添加新的url到待爬队列
栗子1:
实现这个回调函数并返回false,表示爬虫在处理这个helperUrl的时候,不会从中提取待爬url
configs.onProcessHelperPage = function(page, content, site) {
return false;
}
栗子2:
在onProcessHelperPage回调函数中,生成新的contentUrl并添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url
configs.onProcessHelperPage = function(page, content, site) {
var jsonObj = JSON.parse(content);
var id = 0;
for (var i = 0, n = jsonObj.data.length; i < n; i++) {
var item = jsonObj.data[i];
id = item._id;
// 将新的url插入待爬队列中
site.addUrl("http://service.chanjet.com/api/v1/message/"+id);
}
// 通知爬虫不再从当前网页中发现待爬url
return false;
}
beforeHandleImg(fieldName, img)
在抽取到field内容之后调用, 对其中包含的img标签进行回调处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param img 整个img标签的内容
@return 返回处理后的img标签的内容
很多网站对图片作了延迟加载, 这时候就需要在这个函数里面来处理
举个栗子:
汽车之家论坛帖子的图片大部分是延迟加载的,默认会使用http://x.autoimg.cn/club/lazyload.png 图片url,我们需要找到真实的图片url并替换,具体实现如下:
configs.beforeHandleImg = function(fieldName, img) {
// 通过正则判断img中的src属性是不是真实图片url,如果是,则直接返回img,如果不是,继续执行
var regex = /src="(https?:\/\/.*?)"/;
var rs = regex.exec(img);
if (!rs) return img;
var url = rs[1];
if (url == "http://x.autoimg.cn/club/lazyload.png") {
var regex2 = /src9="(https?:\/\/.*?)"/;
rs = regex2.exec(img);
// 替换成真实图片url
if (rs) {
var newUrl = rs[1];
img = img.replace(url, newUrl);
}
}
return img;
}
beforeCacheImg(fieldName, url)
由于很多网站都有图片防盗链限制, 所以神箭手会缓存爬到的图片, 在缓存之前, 可以对图片作处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param url 图片的url
@return 处理后的图片url
举个栗子:
知乎问答页面, 用户的头像链接是这样的: https://pic3.zhimg.com/xxxxxx_s.jpg
研究一下可以发现, 大一点的头像是这样的: https://pic3.zhimg.com/xxxxxx_l.jpg
configs.beforeCacheImage = function(fieldName, url) {
if (fieldName == "answers.avatar") {
return url.replace("_s.jpg", "_l.jpg"); // 对url进行字符串替换,得到较大图片的url
}
return url; // 返回图片url
}
afterExtractField(fieldName, data, page)
当一个field的内容被抽取到后进行的回调, 在此回调中可以对网页中抽取的内容作进一步处理
@param fieldName 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.
@param data 当前field抽取到的数据. 如果该field是repeated, data为数组类型, 否则是String
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.skip()便不再爬取当前网页
@return 返回处理后的数据, 注意数据类型需要跟传进来的data类型匹配
举个栗子:
比如爬取知乎用户的性别信息, 相关网页源码如下:
那么可以这样写:
configs = {
// configs的其他成员
...
fields: [
{
name: "gender",
selector: "//span[contains(@class, 'gender')]/i/@class"
}
]
...
};
configs.afterExtractField = function(fieldName, data, page) {
if (fieldName == "gender") {
if (data.indexOf("icon-profile-male") > -1) { // data中包含"icon-profile-male",说明当前知乎用户是男性
return "男";
}
else if (data.indexOf("icon-profile-female") > -1) { // data中包含"icon-profile-female",说明当前知乎用户是女性
return "女";
}
else { // data未匹配到上面的两个字符串,无法判断用户性别
return "未知";
}
}
return data;
}
afterExtractPage(page, data)
在一个网页的所有field抽取完成之后, 可能需要对field进一步处理, 以发布到自己的网站
@param page 当前正在爬取的网页页面的对象,调用page.url可获取当前网页的url,调用page.skip()便不再爬取当前网页
@param data 当前网页抽取出来的所有field的数据, JavaScript对象
@return 返回处理后的数据, 注意数据类型不能变
举个栗子:
比如从某网页中得到time和title两个field抽取项, 现在希望把time的值添加中括号后拼凑到title中,处理过程如下:
configs.afterExtractPage = function(page, data) {
var title = "[" + data.time + "] " + data.title;
data.title = title;
return data;
}
nextScanUrl(url)
在处理完待爬队列中的url后,生成下一个scanUrl并插入到待爬队列中
@param url 当前的scanUrl
@return 返回下一个scanUrl或返回null
举个栗子:
某个网站有1500个scanUrl,需先处理前1000个scanUrl,再调用nextScanUrl回调函数来逐个处理后面500个,处理过程如下:
// 增加1000个"scanUrl",一个爬虫最多一次性处理1000个"scanUrl"
for (var i = 1; i <= 1000; i++) {
scanUrls.push("http://www.dianping.com/search/keyword/" + i + "/0_" + keywords);
}
var configs = {
// "configs"的其他成员
...
scanUrls: scanUrls,
...
};
configs.nextScanUrl = function(url) {
var currentPage = url.substring(url.indexOf("keyword/") + 8, url.indexOf("/0_"));
// 页面数超过1500以后,返回空值
if (currentPage > 1500) {
return null;
}
else {
var nextPage = currentPage + 1;
var nextScanUrl = url.replace("keyword/" + currentPage, "keyword/" + nextPage);
// 将下一个"scanUrl"插入待爬队列
return nextScanUrl;
}
};
对爬虫感兴趣的童鞋可以加腾讯群讨论:342953471。
相关推荐
- Configs:配置文件,如代理服务器设置、爬虫参数等。 - Tests:单元测试代码,用于验证各个功能模块的正确性。 - Main:启动入口,如Console应用程序的Main方法。 通过这个项目,学习者可以深入了解网络爬虫的...
采集网站列表的配置文件为:configs/article/collectsite.php 某个网站的采集规则配置文件为:configs/article/site_网站英文标识.php 新建一个采集规则,再上传复盖那规则,完成。 加Q请注明来意。谢谢
### 杰奇CMS安装教程详解 #### 一、安装前的准备工作 在开始杰奇CMS的安装之前,确保已经完成了以下准备工作: 1. **环境搭建**:根据所使用的服务器类型不同,准备工作也会有所区别。 - **独立服务器**:需要...
#### 二、网络实现的关键文件 网络功能的实现通常涉及网络协议栈和网卡驱动两大部分。对于基于Freescale平台的设备,在U-boot环境中,关键文件主要包括: 1. **board.c**:负责定义特定板子的相关配置和初始化逻辑...
### Smarty安装与入门详解 #### 一、Smarty简介 Smarty是一款广泛应用于PHP开发中的模板引擎。它能够将逻辑层和表现层分离,使得程序结构更加清晰。通过使用Smarty,开发者可以轻松地创建复杂的动态页面而无需担心...
基于情感分析与逻辑回归的京东...- configs.py:存放所有相关的静态配置 - pipeline.py:主控制模块 - processing.py:数据预处理模块 - plotter.py:数据可视化模块 - model.py:建模与评估模块 - utils.py:工具类
在这个场景中,提到的"configs:配置文件"很可能是一个包含多个配置文件的项目或库。 "Vimscript"是一种用于编写Vim编辑器插件和配置的语言。Vim是一款高度可定制的文本编辑器,它允许用户通过.vimrc文件来配置各种...
设定档 加载和管理config / *。...如果您有config/foo.yml ,那么在需要读取文件的任何地方,都可以使用Configs[:foo]作为哈希。 例子: # config/foo.yml development: hello: world # Elsewhere (even in a confi
流利的食谱这是对原始 td-agent 说明书的重写,该说明书严重过度设计并且在某些地方失败,尤其是安装了第二个 Ruby(例如,通过 RVM)。 Cookbooks 安装 fluents 和基本配置文件。 附加配置提供了安装 fluentd 插件...
- static_configs: - targets: - "localhost:9093" ``` 2. **配置Alertmanager**:在Alertmanager的配置文件`alertmanager.yml`中定义如何接收告警信息以及如何转发这些告警。例如: ```yaml global: ...
入门安装: npm install --global generator-server-configs 使用运行它: yo server-configs 您可以通过直接指定配置来绕过提示: yo server-configs node 可能的参数:node、apache、nginx、lighttpd、gae、iis...
### PHPCMS V9 二次开发教程 #### 文件目录结构详解 **根目录**:这是整个项目的顶层目录,包含了所有核心文件以及配置信息。 - **api**:此目录存放了API相关的文件,这些文件用于处理来自外部应用或服务的请求...
lo/g/os lo/g/OS => 人人皆宜的操作系统[![Gitter 聊天]( ) #dotfiles 存储库清单 :check_box_with_check: * compton ☐ * firefox ☐ * i3 ☐ * i3bar ☐ * mpd ☐ * ncmpcpp ☐ * mpv ☐ * ranger ☐ * ...
神箭手云爬虫API文档是为开发者提供在神箭手云爬虫开发平台上使用JavaScript语言快速开发和配置网络爬虫的详细指南。文档中涵盖了API接口的详细介绍、配置说明以及如何抽取数据的示例代码,以便用户能够直接在平台上...
### PHPCMS二次开发教程详解 #### 文件目录结构解析 PHPCMS是一个广泛使用的开源内容管理系统,它提供了强大的功能和灵活的自定义选项,适用于多种Web应用开发场景。本教程旨在帮助开发者理解PHPCMS的基本架构,并...
在提供的压缩包文件"Configs-master"中,我们可以预期找到一系列配置文件,可能包括: 1. Starman服务器的配置文件:这可能包含关于服务器如何监听端口、最大并发连接数、日志设置等信息。 2. Dancer应用的配置文件...
原文链接:https://blog.csdn.net/m0_37814112/article/details/122538085 说明:基于file_sd_configs部署blackbox-exporter监控k8s服务
### U-Boot移植步骤详解:理解U-Boot与嵌入式系统引导 #### U-Boot:通用引导加载器 U-Boot,全称Universal Boot Loader,是一个遵循GPL条款的开源项目,旨在为广泛的嵌入式处理器和操作系统提供引导支持。U-Boot...
【DocSearch配置详解】 DocSearch是由Algolia提供的一项服务,旨在帮助网站实现高效、精确的文档搜索功能。这个服务依赖于一个配置文件,用于定义如何抓取、索引和展示你的文档内容。在"docsearch-configs"项目中,...