博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Node.js开发一个小爬虫
阅读量:6853 次
发布时间:2019-06-26

本文共 4795 字,大约阅读时间需要 15 分钟。

前言

很多程序猿在最开始学习开发的时候应该都有一个想要自己开发一个爬虫的想法(至少我是有的)。所以国内网络上也是爬虫盛行!学了node.js之后发现比较适合写爬虫,不过一直没有动手去写,正好这段时间比较闲,就写个爬虫玩下。

想着爬个什么东西呢?正好都比较喜欢看电影,那就从时光网爬下国内的票房排行榜吧。

Talk is cheap. Show me the code

如何"食用"

git clone https://github.com/XNAL/node-MovieSpidercd node-MovieSpider npm initnode index.js

搭建环境

  • 开发语言:
  • 发出http请求:
  • 并发控制:
  • 分析网页内容:

开始撸代码

1. 代码主体

作为一个简单的示例,此次就不开启node服务了,我这里就直接来个自执行的方法。如果有需要,可以根据自己的需求扩展。

// 启动时直接执行代码 (function spider() {    util.fetch_data_get(reqUrl, reqParams)        .then((result) => {            // 根据页面结构获取总的页数,然后再分页获取数据            let $ = cheerio.load(result.body.html);            let pageTotal = $('.bocontent .pagesize a:last-child').data('page') || 0;            console.log('电影数据总页数:', pageTotal);            return pageTotal;        })        .then((pageTotal) => {            // 分页获取数据            getMovieData(0, pageTotal);        })        .catch((err) => {            console.log('获取链接失败:', err);        })})();

2. 发送请求

因为代码中需要多次发送http请求,所以把http请求写成一个公共方法会比较好。使用上面提到superagent库来实现。

// 公共方法:通过get请求获取数据function fetch_data_get(url, queryParams) {    return new Promise((reslove, reject) => {        superagent            .get(url)            .set(setting.header)            .query(queryParams)            .end((err, result) => {                err ? reject(err) : reslove(result);            })    })}

3. 分析目标网站api

根据人工操作得来的apihttp://movie.mtime.com/boxoffice/?year=2017&area=china&type=MovieRankingYear&category=all&page=0&display=list&timestamp=1505818638620&version=07bb781100018dd58eafc3b35d42686804c6df8d&dataType=json可以得到以下参数:

// 根据网站api得到相应的url和参数const reqUrl = 'http://movie.mtime.com/boxoffice/';const reqParams = {    'year': 2017,    'area': 'china',    'type': 'MovieRankingYear',    'category': 'all',    'page': 0,    'display': 'list',    'timestamp': 1501576013654,    'version': '07bb781100018dd58eafc3b35d42686804c6df8d',    'dataType': 'json'};

因为此次要获取的是2017年内地票房排行榜。根据分析可知:需要变动的主要是page参数,那这里就需要根据页面返回的内容来取得总的page

4. 使用cheerio获取所需参数

api返回的页面内容可查看:。

这里需要用到cheerio来取页码总数的代码,cheerio可以理解为服务器端的jQuery,用法也类似:

// 根据页面结构获取总的页数,然后再分页获取数据let $ = cheerio.load(result.body.html);let pageTotal = $('.bocontent .pagesize a:last-child').data('page') || 0;

5. 开始分页取目标数据

<1> 调用上面所说的公共方法fetch_data_get获取数据,然后取页面内容,图片地址都先保存在movieImgs中,最后再统一下载图片:

// 根据页面结构获取所需数据let $ = cheerio.load(result.body.html);$('.bocontent .boxofficelist dd').each((idx, elem) => {    $(elem).find('div.movietopmod').each((i, el) => {        let _this = $(el);        let arrLeadActor = [];        _this.find('.txtbox b p').eq(1).find('a').each((idx, ela) => {            arrLeadActor.push($(ela).text());        })        movieData.push({            rank: _this.find('.picbox i').text(),            img: _this.find('.picbox img').attr('src').replace(/\/u\//, ""),            name: _this.find('.txtbox h3').text(),            director: _this.find('.txtbox b p').eq(0).find('a').text(),            leadActor: arrLeadActor.join(','),            point: _this.find('.gradebox .point').text(),            total: _this.find('.totalbox .totalnum').text()        }),        movieImgs.push(_this.find('.picbox img').attr('src').replace(/\/u\//, ""));    })})

<2> 根据页码循环取数据

if(pageIndex <= pageTotal) {    // 设置timeout防止网站反爬虫    setTimeout(() => {        pageIndex ++;        getMovieData(pageIndex, pageTotal);    }, setting.timeout);}

<3> 全部数据取出后存储数据,并下载图片。

因为只是一个简单的示例,所以此次数据只是保存到json文件中。如果需要对数据进行后续操作的话,那就最好保存到数据库中:

fs.writeFile(dataDir + reqParams.year + '.json', JSON.stringify(movieData), (err) => {    if (err) {        console.log(err);    } else {        console.log('数据写入成功');    }});

调用下载图片的方法:

let folderName = imgPrefix + reqParams.year;util.downloadImg(movieImgs, folderName);

util.js中的downloadImg方法:这里就需要用到上面所说的async,使用async是为了进行并发控制,不然极短时间发送至少几十几百次的请求,这种情况弄不好就被网站的发爬虫程序给封了,而且大量并发也会导致出错的概率更高。

// 异步下载图片function downloadImg(urls, folderName) {    async.mapLimit(urls, setting.asyncNum, (img, callback) => {        fetch_data_get(img, {})            .then((result) => {                let fileName = path.basename(img);                let folder = imgDir + folderName;                if(!fs.existsSync(folder)) {                    fs.mkdirSync(folder);                }                fs.writeFile(folder + '/' + fileName, result.body, (err) => {                    if (err) {                        console.log(img, '图片写入失败:', err);                    } else {                        console.log(img, '图片写入成功');                        callback(null , fileName);                    }                })            })            .catch((err) => console.log(err))    }, (err, result) => {        if (err) {            console.log('图片下载失败:', err)        } else {            console.log(result);        }    })}

结语

到此为止一个简单的node.js版的小爬虫就开发完成了。其实弄懂了爬虫的原理,再回过头去看,发现开发一个简单的爬虫来说还是很容易的。

最后,欢迎大家去进行starfork

转载地址:http://nzfyl.baihongyu.com/

你可能感兴趣的文章
盘点当下最流行的 Java 工具
查看>>
《NX-OS与Cisco Nexus交换技术:下一代数据中心架构(第2版)》一2.3 PVLAN
查看>>
6月23日云栖精选夜读:重磅!阿里妈妈首次公开自研CTR预估核心算法MLR
查看>>
《Hadoop大数据分析与挖掘实战》——1.4节数据挖掘建模过程
查看>>
重拾开始菜单的 Windows 9
查看>>
2016 最常见密码排行榜出炉 “123456”稳稳上榜
查看>>
《OpenStack云计算实战手册(第2版)》一1.9 配置服务的租户和服务的用户
查看>>
Python控制多进程与多线程并发数
查看>>
《容器技术系列》一2.3 Docker命令执行
查看>>
《HTML5游戏编程核心技术与实战》一2.4 坐标变换
查看>>
《互联网产品设计》一2.5 写代码,让产品可以使用
查看>>
大数据的真正价值在哪里?
查看>>
最新版AlphaGo(Master)的60胜预示着人类将是一种过时的算法?
查看>>
美使馆9年pm2.5数据分析:雾霾到底是不是加重了?
查看>>
《嵌入式Linux开发实用教程》——4.3 块设备驱动
查看>>
《Maven官方文档》POM文件(二)
查看>>
Apache Storm 官方文档 —— 配置开发环境
查看>>
企业IT架构转型之道:阿里巴巴中台战略思想与架构实战. 2.5 为真正发挥大数据威力做好储备...
查看>>
如何通过 MySQL 的二进制日志恢复数据库数据
查看>>
【科普】数字货币的基石--区块链
查看>>