神箭手爬虫归纳总结

来自JsRobot
跳转至: 导航搜索
                            神箭手爬虫归纳总结-初级开发,分享爬取一点资讯网站视频的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.神箭手回调函数

20180810 212128 0001.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 = '
'+img+'
'+time+'
'+keyword+'
';
         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();