神箭手爬虫通过js来编写,调用神箭手框架的一些api实现某些需求和功能。整段程序的大体框架是这样的:
var configs = {
domains: ["要爬取的域名"],
scanUrls: ["爬虫的入口URL"],
contentUrlRegexes: [/http:\/\/.*/],//内容页url正则
helperUrlRegexes: [/http:\/\/.*/], //列表页url正则 可留空
fields: [
{
// 抽取项
name: "title",
selector: "抽取规则", //默认使用XPath
required: true //是否不能为空
},
{
// 抽取项
name: "content",
selector: "抽取规则"
}
]
};
var crawler = new Crawler(configs);
crawler.start();
1.认识configs对象
configs对象包括一些成员变量(domains、scanUrls、contentUrlRegexes、helperUrlRegexes)和fields抽取项。然后通过crawler对象包装,开始执行爬虫。
2.认识domains变量
定义神箭手应用爬取哪些域名下的网页, 非域名下的网页会被忽略以提高爬取速度。
// 单个域名
domains: ["i.youku.com"]
// 多个域名
domains: ["i.youku.com", "v.youku.com"],
3.认识scanUrls变量
定义神箭手应用的入口页url, 神箭手应用会从入口页url开始爬取数据
// 单个入口页url
scanUrls: ["http://wallstreetcn.com/"]
// 多个入口页url
scanUrls: ["http://baijia.baidu.com/","http://www.demo.com/" ]
- 认识内容页contentUrlRegexes正则的匹配
contentUrlRegexes是设置内容页的正则表达式,用来匹配页面中符合这一正则表达式的url。比如http://www.qiushibaike.com/article/117844937,他的正则表达式可以表示为
contentUrlRegexes:["/http:\/\/www\.qiushibaike\.com\/article\/\d+/"]
可以用在线正则查看匹配是否准确(//之间的字符串为正则表达式)。http://tool.oschina.net/regex/
5.认识列表页helperUrlRegexes正则的匹配
helperUrlRegexes是设置列表页的正则表达式,用来匹配页面中符合这一正则表达式的url。比如https://www.leiphone.com/search?s=vr&site=article,他的正则表达式可以表示为
helperUrlRegexes:["/https:\/\/www\.leiphone\.com\/search\?s=vr&site=article(&page=\d+)?/"]
6.认识fields
1). 定义内容页的抽取规则
2). 规则由一个或多个field组成, 一个field代表一个抽取项,也就是说代表一个要存储的字段。
拿雷锋网的内容页为例:
fields: [
{
// 抽取项名称,以后就是代表数据库中字段的名称
name: "title",
// 抽取项别名, 在神箭手控制台预览该条数据的时候会显示
alias: "爬取的标题",
// 抽取该项数据的表达式(默认使用XPath来选取节点的内容),其他类型不做赘述,我也只用过这一种方式
selector: "//h1[contains(@class,'headTit')]",
// required为true表示该项数据不能为空,默认值为false
required: true //如果抽取的数据为空,该条记录过滤掉
}
]
xpath语法介绍:http://www.w3school.com.cn/xpath/xpath_syntax.asp
7.神箭手回调函数
imagecallbackFlowchart.png(1)beforeCrawl:神箭手应用初始化的时候调用,做一些爬取之前的操作,给http请求加header等
(2)onChangeProxy:在神箭手应用切换代理IP后调用, 主要用来给HTTP请求添加Header和Cookie等数据,切换代理IP后, 先前HTTP请求添加的Cookie会被清除, 若打算继续使用Cookie, 强烈建议在onChangeProxy回调函数中添加Cookie
(3)isAntiSpider:判断访问网页时是否被目标网站屏蔽, 如果判断被屏蔽了, 神箭手会切换一次代理IP后自动重新爬取
(4)afterDownloadPage:在一个网页下载或JS渲染完成之后调用, 主要用来处理网页,可以自定义加入html代码。
(以上回调一般用不到,默认不写就行。特殊情况除外。)
下面3个回调是经常用到,需要书写代码的:
(5)onProcessScanPage:入口页类型的回调,不写默认返回true,在下载完页面之后,会发现符合内容页正则和列表正则的url,添加到待爬队列里。如果返回false,不会自动发现url,需要手动添加url,此url必须符合上边定义的正则表达式。
(6)onProcessHelperPage:列表页类型的回调,不写默认返回true。同上
(7)onProcessContentPage:内容页类型的回调,不写默认返回true。同上
(8)afterDownloadAttachedPage:在一个attachedUrl对应的网页下载或JS渲染完之后调用, 主要用来处理网页。就是页面中又发起的一次异步请求,返回时调用。
(9)beforeHandleImg:从内容页中抽取到一个抽取项的值之后调用, 对其中包含的img标签进行处理。很多网站对图片设置了延迟加载, 这时就需要在beforeHandleImg回调函数中处理
(10)beforeCacheImg:在对爬取到的图片url进行托管处理之前调用, 主要对托管的图片url进行处理,应该是在文件下载之前,处理一下地址,比如图片有多种大小类型,可能要的是大图。
这个回调一般会用到:
(11)afterExtractField:从内容页中抽取到一个抽取项的值后进行的回调, 在此回调中可以对该抽取项的值进行处理并返回。
(12)afterExtractPage:在内容页的所有抽取项抽取完成之后, 在此回调函数中对抽取项再进行一次处理或进行其他操作。
8.内置对象:回调函数中,会有一些参数,以下这几个对象:
site:当前正在爬取的目标网站对象
page:当前正在爬取的网页对象
console:打印控制台日志的,一般用个console.log();
还有systen、options、shenjian等对象
9.具体看几个demo例子.......
神箭手的概念还是比较好理解,但是每个网页的情况都是不同的,对应还得具体来分析,处理的方式也不一样。
糗事百科简单举例:
var configs = {
domains: ["qiushibaike.com"],
scanUrls: ["https://www.qiushibaike.com/"],
contentUrlRegexes: [/https:\/\/www\.qiushibaike\.com\/article\/\d+/],//内容页url正则
helperUrlRegexes: [/https:\/\/www\.qiushibaike\.com\/8hr\/page\/\d+/], //列表页url正则 可留空
fields: [
{
// 抽取项
name: "title",
alias : "标题",
selector: "//div[contains(@class,'author')]/a[2]/h2/text()", //默认使用XPath
required: true //是否不能为空
},
{
// 抽取项
name: "content",
alias : "内容",
selector: "//*[@id='single-next-link']/div/text()"
},
{
name : "commentCount",
alias : "评论",
selector : "//i[contains(@class,'number')]/text()"
}
]
};
//每抽取一个字段会调用,用于对该字段的再次处理,比如时间再次定义格式
configs.afterExtractField = function (fieldName, data, page, site) {
return data;
};
//抽取完这个内容页的所有字段之后再调用一次
configs.afterExtractPage = function (page, data, site) {
return data;
};
//入口页类型的回调,页面下载完会调用
configs.onProcessScanPage = function (page, content, site) {
console.log("onProcessScanPage:入口页");
return true;
};
//列表页类型的回调
configs.onProcessHelperPage = function (page, content, site) {
console.log("onProcessHelperPage:列表页");
site.addUrl("");
return false;
};
//内容页类型的回调
configs.onProcessContentPage = function (page, content, site) {
console.log("onProcessContentPage:内容页");
return false;
};
var crawler = new Crawler(configs);
crawler.start();
爬取 一点资讯-视频 爬虫源码:
var cend = 0;
var chanel = "u13746";
function getNowFormatDate() {
var date = new Date();
var seperator1 = "-";
var year = date.getFullYear();
var month = date.getMonth() + 1;
var strDate = date.getDate();
if (month >= 1 && month <= 9) {
month = "" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "" + strDate;
}
var currentdate = year + seperator1 + month + seperator1 + strDate;
return currentdate;
}
var configs = {
domains: ["yidianzixun.com"],
scanUrls: ["http://www.yidianzixun.com/channel/u13746"],
contentUrlRegexes: [
/http:\/\/www\.yidianzixun\.com\/mp\/content\?id=\d+/
],//内容页url正则
helperUrlRegexes: [
/http:\/\/www\.yidianzixun\.com\/home\/q\/news_list_for_channel\?channel_id=[a-zA-Z0-9_]+&cstart=\d+&cend=\d+&infinite=true&refresh=1&__from__=pc&multi=5&appid=web_yidian&_=\d+/
], //列表页url正则 可留空
fields: [
{
name: "title",
alias : "标题",
selector : "//div[contains(@class,'left-wrapper')]/h2/text()",
required : true
},
{
name: "publishDate",
alias : "发布时间",
selector: "//div[contains(@class,'left-wrapper')]/div[contains(@class,'meta')]/span/text()",
required : true
},
{
name : "videoUrl",
alias : "视频播放地址",
selector : "/html/body/div[2]/div[1]/div[2]/video/@src",
// selector : "//div[contains(@class,'left-wrapper')]/div[contains(@class,'video-wrapperr')]/video/@src",
required : true
},
{
name : "img",
alias : "缩略图",
selector : "//div[@id='img_view']/text()",
required : true,
sourceType : SourceType.UrlContext
},
{
name : "time",
alias : "时长",//单位:s
selector : "//div[@id='time_view']/text()",
sourceType : SourceType.UrlContext
},
{
name : "keyword",
alias : "关键词",
selector : "//div[@id='keyword_view']",
sourceType : SourceType.UrlContext
}
]
};
configs.afterExtractField = function (fieldName, data, page, site) {
if(fieldName == "publishDate") {
var isDate = /\d+\.\d+\.\d+/.exec(data);
if(!isDate) {
data = getNowFormatDate();
}
else {
data = data.replace(/\./g, "-");
}
}
if(fieldName == "keyword") {
if(data == "undefined") {
data = "";
}
}
return data;
};
configs.onProcessContentPage = function (page, content, site) {
return false;
};
configs.onProcessHelperPage = function (page, content, site) {
var jsonObj = JSON.parse(content);
console.log("jsonObj:"+jsonObj);
if(jsonObj.status != "success") {
return false;
}
var items = jsonObj.result;
for (var i = 0; i < items.length; i++) {
if(items[i].hasOwnProperty("url")) {
var detailUrl = items[i].url;
var img = items[i].image;
var time = items[i].duration;
var keyword = items[i].keywords;
var htmlStr = '<div id="img_view">'+img+'</div><div id="time_view">'+time+'</div><div id="keyword_view">'+keyword+'</div>';
var options = {
method : "GET",
contextData : htmlStr
};
site.addUrl(detailUrl,options);
}
}
if(cend != null && cend != "") {
site.addUrl("http://www.yidianzixun.com/home/q/news_list_for_channel?channel_id="+chanel+"&cstart="+cend+"&cend="+(cend+10)+"&infinite=true&refresh=1&__from__=pc&multi=5&appid=web_yidian&_="+Date.parse(new Date()));
cend = cend + 10;
}
return false;
};
configs.onProcessScanPage = function (page, content, site) {
//从入口页获得chanel_id,每个浏览器可能不同,有限制
chanel = extract(content, "//div[contains(@class,'channel-nav')]/div[contains(@class,'list')]/a[4]/@data-channelid");
console.log("chanel:"+chanel);
site.addUrl("http://www.yidianzixun.com/home/q/news_list_for_channel?channel_id="+chanel+"&cstart="+cend+"&cend="+(cend+10)+"&infinite=true&refresh=1&__from__=pc&multi=5&appid=web_yidian&_="+Date.parse(new Date()));
cend = cend + 10;
return false;
};
var crawler = new Crawler(configs);
crawler.start();
网友评论