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 面向对象全新理练之数据的封装
Dec 03 Javascript
JavaScript 加号(+)运算符号
Dec 06 Javascript
jQuery+.net实现浏览更多内容(改编php版本)
Mar 28 Javascript
Javascript实现页面跳转的几种方式分享
Oct 26 Javascript
js字符串转成JSON
Nov 07 Javascript
JS与C#编码解码
Dec 03 Javascript
jQuery表单域属性过滤器用法分析
Feb 10 Javascript
jquery悬浮提示框完整实例
Jan 13 Javascript
js中最容易被忽视的事件问题大总结
May 15 Javascript
JavaScript九九乘法口诀表的简单实现
Oct 04 Javascript
10个最优秀的Node.js MVC框架
Aug 24 Javascript
Vue组件模板的几种书写形式(3种)
Feb 19 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
新浪SAE搭建PHP项目教程
2015/01/28 PHP
浅谈php+phpStorm+xdebug配置方法
2015/09/17 PHP
php排序算法实例分析
2016/10/17 PHP
JavaScript 加号(+)运算符号
2009/12/06 Javascript
JS小功能(操作Table--动态添加删除表格及数据)实现代码
2013/11/28 Javascript
一个不错的js html页面倒计时可精确到秒
2014/10/22 Javascript
JavaScript将Web页面内容导出到Word及Excel的方法
2015/02/13 Javascript
js带缩略图的图片轮播效果代码分享
2015/09/14 Javascript
深入解析JavaScript的闭包机制
2015/10/20 Javascript
深入浅析search 搜索框的写法
2016/08/02 Javascript
Nodejs中Express 常用中间件 body-parser 实现解析
2017/05/22 NodeJs
React根据宽度自适应高度的示例代码
2017/10/11 Javascript
Angular实现搜索框及价格上下限功能
2018/01/19 Javascript
js+canvas实现验证码功能
2020/09/21 Javascript
vue微信分享插件使用方法详解
2020/02/18 Javascript
如何阻止移动端浏览器点击图片浏览
2020/08/29 Javascript
解决vscode进行vue格式化,会自动补分号和双引号的问题
2020/10/26 Javascript
[46:47]2014 DOTA2国际邀请赛中国区预选赛 DT VS HGT
2014/05/22 DOTA
Python Numpy 数组的初始化和基本操作
2018/03/13 Python
python+unittest+requests实现接口自动化的方法
2018/11/29 Python
Django中从mysql数据库中获取数据传到echarts方式
2020/04/07 Python
Django 解决开发自定义抛出异常的问题
2020/05/21 Python
python如何运行js语句
2020/09/09 Python
python3代码输出嵌套式对象实例详解
2020/12/03 Python
详解java调用python的几种用法(看这篇就够了)
2020/12/10 Python
python爬虫今日热榜数据到txt文件的源码
2021/02/23 Python
python FTP编程基础入门
2021/02/27 Python
生物工程专业求职信
2014/09/03 职场文书
七一慰问简报
2015/07/20 职场文书
培训感想范文
2015/08/07 职场文书
中秋节祝酒词
2015/08/12 职场文书
2016年安全生产先进个人事迹材料
2016/02/29 职场文书
大学迎新生的欢迎词
2019/06/25 职场文书
python爬虫之selenium库的安装及使用教程
2021/05/23 Python
彻底弄懂Python中的回调函数(callback)
2022/06/25 Python
HttpClient实现表单提交上传文件
2022/08/14 Java/Android