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 相关文章推荐
解决AJAX中跨域访问出现'没有权限'的错误
Aug 20 Javascript
Javascript 兼容firefox的一些问题
May 21 Javascript
判断对象是否Window的实现代码
Jan 10 Javascript
qTip2 精致的基于jQuery提示信息插件
Feb 17 Javascript
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
Apr 07 Javascript
JavaScript中length属性的使用方法
Jun 05 Javascript
jquery带翻页动画的电子杂志代码分享
Aug 21 Javascript
手机端转换rem适应
Apr 01 Javascript
微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据(一)
May 11 Javascript
利用vueJs实现图片轮播实例代码
Jun 03 Javascript
layui扩展上传组件模拟进度条的方法
Sep 23 Javascript
JavaScript/TypeScript 实现并发请求控制的示例代码
Jan 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入门小知识
2008/03/24 PHP
PHP使用ODBC连接数据库的方法
2015/07/18 PHP
一个简单安全的PHP验证码类、PHP验证码
2016/09/24 PHP
一键生成各种尺寸Icon的php脚本(实例)
2017/02/08 PHP
POST一个JSON格式的数据给Restful服务实例详解
2017/04/07 PHP
php设计模式之组合模式实例详解【星际争霸游戏案例】
2020/03/27 PHP
YUI 读码日记之 YAHOO.lang.is*
2008/03/22 Javascript
JavaScript入门之对象与JSON详解
2011/10/21 Javascript
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
js批量设置样式的三种方法不推荐使用with
2013/02/25 Javascript
JS中Iframe之间传值的方法
2013/03/11 Javascript
javascript使用smipleChart实现简单图表
2015/01/02 Javascript
JavaScript中字符串(string)转json的2种方法
2015/06/25 Javascript
非常实用的js验证框架实现源码 附原理方法
2016/06/08 Javascript
EasyUI的doCellTip实现鼠标放到单元格上提示单元格内容
2016/08/24 Javascript
深入理解Vue.js源码之事件机制
2017/09/27 Javascript
react-router browserHistory刷新页面404问题解决方法
2017/12/29 Javascript
深入理解JS的事件绑定、事件流模型
2018/05/13 Javascript
JS立即执行函数功能与用法分析
2019/01/15 Javascript
使用Jenkins部署React项目的方法步骤
2019/03/11 Javascript
JavaScript实现旋转木马轮播图
2020/03/16 Javascript
vue制作抓娃娃机的示例代码
2020/04/17 Javascript
八种Vue组件间通讯方式合集(推荐)
2020/08/18 Javascript
[04:55]完美世界副总裁蔡玮:DOTA2的自由、公平与信任
2013/12/18 DOTA
Python兔子毒药问题实例分析
2015/03/05 Python
使用python实现接口的方法
2017/07/07 Python
pyhton列表转换为数组的实例
2018/04/04 Python
利用CSS3实现开门效果实例源码
2016/08/22 HTML / CSS
John Varvatos官方网站:设计师男士时装
2017/02/08 全球购物
香港莎莎官网Sasa.com:亚洲著名国际化妆品商城
2019/11/10 全球购物
Auguste The Label官网:澳大利亚一家精品女装时尚品牌
2020/06/14 全球购物
学生自我鉴定范文
2013/10/04 职场文书
初中生旷课检讨书范文
2014/10/06 职场文书
大学生自荐信怎么写
2015/03/26 职场文书
2016十一国庆节感言
2015/12/09 职场文书
利用正则表达式匹配浮点型数据
2022/05/30 Java/Android