node+experss实现爬取电影天堂爬虫


Posted in Javascript onNovember 20, 2016

上周写了一个node+experss的爬虫小入门。今天继续来学习一下,写一个爬虫2.0版本。

这次我们不再爬博客园了,咋玩点新的,爬爬电影天堂。因为每个周末都会在电影天堂下载一部电影来看看。

talk is cheap,show me the code!

抓取页面分析

我们的目标:

1、抓取电影天堂首页,获取左侧最新电影的169条链接

2、抓取169部新电影的迅雷下载链接,并且并发异步抓取。

具体分析如下:

1、我们不需要抓取迅雷的所有东西,只需要下载最新发布的电影即可,比如下面的左侧栏。一共有170个,除去第一个(因为第一个里面有200部电影),一共有169部电影。

node+experss实现爬取电影天堂爬虫

2、除了抓取首页的东西,我们还要抓取点进去之后,每部电影的迅雷下载链接

node+experss实现爬取电影天堂爬虫

环境搭建

1、需要的东西:node环境、express、cherrio 这三个都是上一篇文章有介绍的,所以这里不再做介绍:点击查看

2、需要安装的新东西:

superagent:

作用:跟request差不多,我们可以用它来获取get/post等请求,并且可以设置相关的请求头信息,相比较使用内置的模块,要简单很多。

用法:

var superagent = require('superagent');
superagent
.get('/some-url')
.end(function(err, res){
  // Do something 
});

superagent-charset:

作用:解决编码问题,因为电影天堂的编码是gb2312,爬取下来的中文会乱码掉。

用法:

var superagent = require('superagent');
var charset = require('superagent-charset');
charset(superagent);

superagent
.get('/some-url')
.charset('gb2312') //这里设置编码
.end(function(err, res){
  // Do something 
});

async:

作用:Async是一个流程控制工具包,提供了直接而强大的异步功能,在这里作为处理并发来调用。

用法:这里需要用到的是:async.mapLimit(arr, limit, iterator, callback)

mapLimit可以同时发起多个异步操作,然后一起等待callback的返回,返回一个就再发起下一个。

arr是一个数组,limit并发数,将arr中的每一项依次拿给iterator去执行,执行结果传给最后的callback

eventproxy:

作用:eventproxy 起到了计数器的作用,它来帮你管理到底异步操作是否完成,完成之后,它会自动调用你提供的处理函数,并将抓取到的数据当参数传过来。

例如我首先抓取到电影天堂首页侧栏的链接,才可以接着抓取链接里面的内容。具体作用可以点这里

用法:

var ep = new EventProxy();
ep.after('got_file', files.length, function (list) {
 // 在所有文件的异步执行结束后将被执行 
 // 所有文件的内容都存在list数组中 
});
for (var i = 0; i < files.length; i++) {
 fs.readFile(files[i], 'utf-8', function (err, content) {
  // 触发结果事件 
  ep.emit('got_file', content);
 });
}
//注意got_file这两个名字必须对应

开始爬虫

主要的程序在app.js这里,所以看的话可以主要看app.js即可

1、首先定义一些全局变量,该引入的库引进来

var cheerio = require('cheerio'); //可以像jquer一样操作界面
var charset = require('superagent-charset'); //解决乱码问题:
var superagent = require('superagent'); //发起请求 
charset(superagent); 
var async = require('async'); //异步抓取
var express = require('express'); 
var eventproxy = require('eventproxy'); //流程控制
var ep = eventproxy();
var app = express();

var baseUrl = 'http://www.dytt8.net'; //迅雷首页链接
var newMovieLinkArr=[]; //存放新电影的url
var errLength=[];   //统计出错的链接数
var highScoreMovieArr=[] //高评分电影

2、开始爬取首页迅雷首页:

//先抓取迅雷首页
(function (page) {
  superagent
  .get(page)
  .charset('gb2312')
  .end(function (err, sres) {
    // 常规的错误处理
    if (err) {
     console.log('抓取'+page+'这条信息的时候出错了')
      return next(err);
    }
    var $ = cheerio.load(sres.text);
    // 170条电影链接,注意去重
    getAllMovieLink($);
    highScoreMovie($);
    /*
    *流程控制语句
    *当首页左侧的链接爬取完毕之后,我们就开始爬取里面的详情页
    */
    ep.emit('get_topic_html', 'get '+page+' successful');
  });
})(baseUrl);

在这里,我们先抓取首页的东西,把首页抓取到的页面内容传给 getAllMovieLink和highScoreMovie这两个函数来处理,

getAllMovieLink获取到了左侧栏除了第1部的电影的169电影。

highScoreMovie为左侧栏第一个链接,里面的都是评分比较高的电影。

上面的代码中,我们弄了一个计数器,当它执行完之后,我们就可以执行与‘get_topic_html‘名字对应的流程了,从而可以保证在执行完首页的抓取工作之后,再执行次级页面的抓取工作。

ep.emit('get_topic_html', 'get '+page+' successful');

highScoreMovie方法如下,其实我们这里的作用不大,只是我统计一下高评分电影首页的信息,懒的继续抓取了

//评分8分以上影片 200余部!,这里只是统计数据,不再进行抓取
function highScoreMovie($){
  var url='http://www.dytt8.net'+$('.co_content2 ul a').eq(0).attr('href');
  console.log(url);
  superagent
  .get(url)
  .charset('gb2312')
  .end(function (err, sres) {
    // 常规的错误处理
    if (err) {
      console.log('抓取'+url+'这条信息的时候出错了')
    }
    var $ = cheerio.load(sres.text);
    var elemP=$('#Zoom p');
    var elemA=$('#Zoom a');
    for (var k = 1; k < elemP.length; k++) {
      var Hurl=elemP.eq(k).find('a').text();
      if(highScoreMovieArr.indexOf(Hurl) ==-1){
        highScoreMovieArr.push(Hurl);
      };
    }
  });
}

3、分离出左侧栏的信息,

如下图,首页中,详情页的链接都在这里$('.co_content2 ul a')。

因此我们将左侧栏这里的详情页链接都遍历出来,保存在一个newMovieLinkArr这个数组里面。

getAllMovieLink方法如下:

// 获取首页中左侧栏的所有链接
function getAllMovieLink($){
  var linkElem=$('.co_content2 ul a');
  for(var i=1;i<170;i++){
    var url='http://www.dytt8.net'+linkElem.eq(i).attr('href');
    // 注意去重
    if(newMovieLinkArr.indexOf(url) ==-1){
      newMovieLinkArr.push(url);
    };
  }
}

node+experss实现爬取电影天堂爬虫

4、对获取到的电影详情页进行爬虫,提取有用信息,比如电影的下载链接,这个是我们所关心的。

// 命令 ep 重复监听 emit事件(get_topic_html),当get_topic_html爬取完毕之后执行
ep.after('get_topic_html', 1, function (eps) {
  var concurrencyCount = 0;
  var num=-4; //因为是5个并发,所以需要减4

  // 利用callback函数将结果返回去,然后在结果中取出整个结果数组。
  var fetchUrl = function (myurl, callback) {
    var fetchStart = new Date().getTime();
    concurrencyCount++;
    num+=1
    console.log('现在的并发数是', concurrencyCount, ',正在抓取的是', myurl);
    superagent
    .get(myurl)
    .charset('gb2312') //解决编码问题
    .end(function (err, ssres) {

      if (err) {
        callback(err, myurl + ' error happened!');
        errLength.push(myurl);
        return next(err);
      }

      var time = new Date().getTime() - fetchStart;
      console.log('抓取 ' + myurl + ' 成功', ',耗时' + time + '毫秒');
      concurrencyCount--;

      var $ = cheerio.load(ssres.text);

      // 对获取的结果进行处理函数
      getDownloadLink($,function(obj){
        res.write('<br/>');
        res.write(num+'、电影名称--> '+obj.movieName);
        res.write('<br/>');
        res.write('迅雷下载链接--> '+obj.downLink);
        res.write('<br/>');
        res.write('详情链接--> <a href='+myurl+' target="_blank">'+myurl+'<a/>');
        res.write('<br/>');
        res.write('<br/>');
      });
      var result = {
         movieLink: myurl
      };
      callback(null, result);
    });
  };

  // 控制最大并发数为5,在结果中取出callback返回来的整个结果数组。
  // mapLimit(arr, limit, iterator, [callback])
  async.mapLimit(newMovieLinkArr, 5, function (myurl, callback) {
    fetchUrl(myurl, callback);
  }, function (err, result) {
    // 爬虫结束后的回调,可以做一些统计结果
    console.log('抓包结束,一共抓取了-->'+newMovieLinkArr.length+'条数据');
    console.log('出错-->'+errLength.length+'条数据');
    console.log('高评分电影:==》'+highScoreMovieArr.length);
    return false;
  });
  
});

首先是async.mapLimit对所有详情页做了一个并发,并发数为5,然后再爬取详情页,爬详情页的过程其实和爬首页的过程是一样的,所以这里不做过多的介绍,然后将有用的信息打印到页面上。

5、执行命令之后的图如下所示:

node+experss实现爬取电影天堂爬虫

浏览器界面:

node+experss实现爬取电影天堂爬虫

这样,我们爬虫的稍微升级版就就完成啦。可能文章写的不是很清楚,我已经把代码上传到了github上,可以将代码运行一遍,这样的话比较容易理解。后面如果有时间,可能会再搞一个爬虫的升级版本,比如将爬到的信息存入mongodb,然后再在另一个页面展示。而爬虫的程序加个定时器,定时去抓取。

备注:如果运行在浏览器中的中文乱码的话,可以将谷歌的编码设置为utf-8来解决;

node+experss实现爬取电影天堂爬虫

代码地址:https://github.com/xianyulaodi/mySpider2

有误之处,欢迎指出

Javascript 相关文章推荐
JavaScript更改class和id的方法
Oct 10 Javascript
JavaScript 设计模式学习 Singleton
Jul 27 Javascript
Javascript Jquery 遍历Json的实现代码
Mar 31 Javascript
分享10篇优秀的jQuery幻灯片制作教程及应用案例
Apr 16 Javascript
js隐式全局变量造成的bug示例代码
Apr 22 Javascript
js与css实现弹出层覆盖整个页面的方法
Dec 13 Javascript
基于JavaScript实现鼠标向下滑动加载div的代码
Aug 31 Javascript
jquery+html仿翻页相册功能
Dec 20 Javascript
微信小程序 二维码canvas绘制实例详解
Jan 06 Javascript
使用canvas及js简单生成验证码方法
Apr 02 Javascript
详解a++和++a的区别
Aug 30 Javascript
JavaScript实现网页动态生成表格
Nov 25 Javascript
JSP防止网页刷新重复提交数据的几种方法
Nov 19 #Javascript
bootstrap datetimepicker2.3.11时间插件使用
Nov 19 #Javascript
js 定位到某个锚点的方法
Nov 19 #Javascript
js 模仿锚点定位的实现方法
Nov 19 #Javascript
Javascript使用function创建类的两种方法(推荐)
Nov 19 #Javascript
js 中获取制定的cook信息实现方法
Nov 19 #Javascript
微信小程序入门教程
Nov 18 #Javascript
You might like
php 获取远程网页内容的函数
2009/09/08 PHP
PHP生成可点击刷新的验证码简单示例
2016/05/13 PHP
yii2使用gridView实现下拉列表筛选数据
2017/04/10 PHP
TP5框架简单登录功能实现方法示例
2019/10/31 PHP
javascript拓展DOM操作 prependChild insertAfert
2010/11/17 Javascript
JSDoc 介绍使用规范JsDoc的使用介绍
2011/02/12 Javascript
jQuery ready函数滥用分析
2011/02/16 Javascript
深入理解Javascript动态方法调用与参数修改的问题
2013/12/10 Javascript
ListBox实现上移,下移,左移,右移的简单实例
2014/02/13 Javascript
jquery仿搜索自动联想功能代码
2014/05/23 Javascript
浅析javascript中function 的 length 属性
2014/05/27 Javascript
jQuery模拟新浪微博首页滚动效果的方法
2015/03/11 Javascript
详细解密jsonp跨域请求
2015/04/15 Javascript
jQuery实现带动画效果的多级下拉菜单代码
2015/09/08 Javascript
详解React native fetch遇到的坑
2018/08/30 Javascript
Vue+Java+Base64实现条码解析的示例
2020/09/23 Javascript
Python三元运算实现方法
2015/01/12 Python
为Python的Tornado框架配置使用Jinja2模板引擎的方法
2016/06/30 Python
Python使用pylab库实现绘制直方图功能示例
2018/06/01 Python
python3将视频流保存为本地视频文件
2018/06/20 Python
解决python写入mysql中datetime类型遇到的问题
2018/06/21 Python
pytorch 可视化feature map的示例代码
2019/08/20 Python
python使用nibabel和sitk读取保存nii.gz文件实例
2020/07/01 Python
python实现测试工具(二)——简单的ui测试工具
2020/10/19 Python
Python绘图实现台风路径可视化代码实例
2020/10/23 Python
Python绘制词云图之可视化神器pyecharts的方法
2021/02/23 Python
Html5移动端适配IphoneX等机型的方法
2019/06/25 HTML / CSS
澳大利亚床上用品、浴巾和家居用品购物网站:Bambury
2020/04/16 全球购物
旅行社各个岗位职责
2014/03/15 职场文书
主管竞聘书范文
2014/03/31 职场文书
高考标语大全
2014/06/05 职场文书
学生顶撞老师的检讨书
2014/09/17 职场文书
自我检讨书怎么写
2015/05/07 职场文书
高二英语教学反思
2016/03/03 职场文书
2016年小学党支部创先争优活动总结
2016/04/05 职场文书
MySQL中使用or、in与union all在查询命令下的效率对比
2021/05/26 MySQL