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 相关文章推荐
让js弹出窗口居前显示的实现方法
Jul 10 Javascript
javascript 3d 逐侦产品展示(核心精简)
Mar 26 Javascript
JS实现带有3D立体感的银灰色竖排折叠菜单代码
Oct 20 Javascript
JavaScript常用判断写法大全(推荐)
May 30 Javascript
无缝滚动的简单实现代码(推荐)
Jun 07 Javascript
jquery插入兄弟节点的操作方法
Dec 07 Javascript
ES6新特性六:promise对象实例详解
Apr 21 Javascript
详解如何让InstantClick兼容MathJax、百度统计等
Sep 12 Javascript
vue实现微信分享功能
Nov 28 Javascript
javascript中的闭包概念与用法实践分析
Jul 26 Javascript
学习LayUI时自研的表单参数校验框架案例分析
Jul 29 Javascript
微信小程序实现多张图片上传功能
Nov 18 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/10/06 PHP
ExtJS与PHP、MySQL实现存储的方法
2010/04/02 PHP
php连接mssql的一些相关经验及注意事项
2013/02/05 PHP
PHP处理Json字符串解码返回NULL的解决方法
2014/09/01 PHP
php使用gettimeofday函数返回当前时间并存放在关联数组里
2015/03/19 PHP
javascript 建设银行登陆键盘
2008/06/10 Javascript
JavaScript下通过的XMLHttpRequest发送请求的代码
2011/06/28 Javascript
简单的两种Extjs formpanel加载数据的方式
2013/11/09 Javascript
eclipse导入jquery包后报错的解决方法
2014/02/17 Javascript
jquery等待效果示例
2014/05/01 Javascript
JsRender实用入门教程
2014/10/31 Javascript
jQuery获取父元素节点、子元素节点及兄弟元素节点的方法
2016/04/14 Javascript
jQuery ajax中使用confirm,确认是否删除的简单实例
2016/06/17 Javascript
微信小程序 解决swiper不显示图片的方法
2017/01/04 Javascript
解决VUE框架 导致绑定事件的阻止冒泡失效问题
2018/02/24 Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
2018/11/11 Javascript
Python中unittest用法实例
2014/09/25 Python
解决python3 urllib 链接中有中文的问题
2018/07/16 Python
Centos下实现安装Python3.6和Python2共存
2018/08/15 Python
PyCharm配置mongo插件的方法
2018/11/30 Python
pandas每次多Sheet写入文件的方法
2018/12/10 Python
python3实现钉钉消息推送的方法示例
2019/03/14 Python
基于python实现操作redis及消息队列
2020/08/27 Python
matplotlib运行时配置(Runtime Configuration,rc)参数rcParams解析
2021/01/05 Python
美国娱乐和流行文化商品店:FYE
2017/09/14 全球购物
trivago美国:全球最大的酒店价格比较网站
2018/01/18 全球购物
简述使用ftp进行文件传输时的两种登录方式?它们的区别是什么?常用的ftp文件传输命令是什么?
2016/11/20 面试题
业务内勤岗位职责
2014/04/30 职场文书
五水共治一句话承诺
2014/05/30 职场文书
大学运动会加油稿200字(5篇)
2014/09/27 职场文书
有限责任公司股东合作协议书范本
2014/10/30 职场文书
实习护士自荐信
2015/03/25 职场文书
酒店开业主持词
2015/07/02 职场文书
5种 JavaScript 方式实现数组扁平化
2021/10/05 Javascript
python基础之模块的导入
2021/10/24 Python
CentOS7环境下MySQL8常用命令小结
2022/06/10 Servers