神箭手爬虫归纳总结
神箭手爬虫归纳总结-初级开发,分享爬取一点资讯网站视频的demo
https://www.jianshu.com/p/eb7371f960d7
神箭手爬虫通过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.神箭手回调函数
(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 = '
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();