[关闭]
@xudongh 2017-04-23T15:48:56.000000Z 字数 6131 阅读 2164

从零开始写一个简单的小爬虫

前端开发


这篇文章教大家如何使用node从零开始写一个简单的小爬虫,这是篇简单、通俗的教程,帮助我们对爬虫有基本了解 - -

在阅读这篇文章之前我们需要什么:


一、什么是爬虫

简单来说,爬虫是一种动物 一种自动获取网页内容的程序。
在我们浏览网页的时候,加载完网页后,网页的数据已经被我们从服务器端下载下来了,那爬虫就是抓取网页数据,把我们需要的内容提取出来的过程。比如,我们想下载某个网页中的所有图片,那就可以利用爬虫获取这个页面的所有数据,然后对页面数据中的所有图片抽出来。
以上是一个简单的单页面爬虫的原理。而实际上,爬虫被应用于搜索引擎,所以其实“爬虫”这个名称非常形象生动,它就像一只虫子一样,在网络上爬啊爬,那它想要的数据。


二、如何利用爬虫抓取数据

1、抓取文本数据

下面我们以厚朴网站的一个页面为例子:
厚朴〖HOPE〗工作室 >> 前端开发

现在我想获取这个页面所有文章的标题:
image_1be8cc5m014nq1md81p8cdbm1gh59.png-314.3kB

我们得到的数据结果应该是这样的:

  1. 学习笔记:HTML基本标签及其用法整理_刘浩翔
  2. 学习笔记:经济学原理知识点整理(二)_郭叙森
  3. 学习笔记:drupal 7.5 环境搭建_雷雨
  4. 学习笔记:《LightRoom5高手之道》阅读笔记(一)
  5. 学习笔记:Java程序入门三_李佳新
  6. 学习笔记:JavaScript-BOM1_郑培钦
  7. 学习笔记:gulp简明使用指南_徐东航
  8. 学习笔记:javascript学习(3_冯泽伦
  9. 学习笔记:计算机科学导论学习小结_张春祥
  10. 学习笔记:JavaScriptArray类型_林秘海
  11. 学习笔记:经济学原理知识点整理(一)_郭叙森
  12. 学习笔记:Python基础知识备忘
  13. 学习笔记:Java程序入门二_李佳新
  14. 学习笔记:理解参数_林秘海
  15. 学习笔记:继承范式_徐东航
  16. 学习笔记:3D-Ribbon-Nav导航条尝试_黄睿
  17. 学习笔记:javascript学习(2_冯泽伦
  18. 学习笔记:Promise对象实践_崔霄
  19. 学习笔记:jQuery 源码阅读(一)_雷雨
  20. 学习笔记:函数表达���_郑培钦
  21. 学习笔记:Python操作MySQL数据库_王金伟
  22. 学习笔记:Express框架学习
  23. 厚朴前端工程项目流程简明规范(参考)
  24. 学习笔记:git简单操作学习_郑培钦(待完善)
  25. 学习笔记:函数节流与函数防抖_王金伟

在获取文章标题数据之前,我们先要获取到整个页面的数据,因此,新建一个文件crawler.js

  1. const http = require('http');
  2. const url = 'http://ce.sysu.edu.cn/hope/FrontEnd/Index.aspx';
  3. http.get(url, function(res) {
  4. let html = '';
  5. res.on('data', function(data) {
  6. html += data;
  7. })
  8. res.on('end', function() {
  9. console.log(html);
  10. })
  11. })

在上面这段代码中,首先我们引入了http模块,并使用http模块的get方法获取到了这个url的数据。fucntion(res) {...}是一个回调函数,其中res.on()是一个事件监听,用于监听数据是否有变化,而on()方法和addListener是一样的,用于监听事件,没有任何区别。

  1. EventEmitter.prototype.on = EventEmitter.prototype.addListener;

通过node.js执行代码:
image_1be8d1d312eosind453kr1doc13.png-62.2kB
整个页面的html代码已经被我们获取到了。
接下来就是对我们所需要的内容进行提取。这时我们需要cheerio这一个库,方便我们操作获取的数据。先安装cheerio

  1. npm install cheerio --save

引入cheerio模块:

  1. const http = require('http');
  2. const cheerio = require('cheerio');
  3. const fs = require('fs');
  4. const url = 'http://ce.sysu.edu.cn/hope/FrontEnd/Index.aspx';

同时我们也引入了fs模块,为了后面我们需要对数据进行文本i\o操作做准备。

查看页面的源代码发现,文章的标题都被写在一个类名为hope_list_item_content的盒子里面:

  1. <a class="hope_list_item_content" href="..." title="点击查看更多">[我们需要的数据]</a>

定义一个用于对数据进行筛选的函数:

  1. function saveTitles(data) {
  2. let $ = cheerio.load(html);
  3. let titles = $('.hope_list_item_content'); // 获取这个类名的数据
  4. let titlesData= []; //定义一个空数组用于存放数据
  5. }

此处用了cheerio的方法(如果不懂的去看cheerio文档,超级简单),这样我们就能够像Jquery一样操作dom结构。

  1. function saveTitles(data) {
  2. let $ = cheerio.load(html);
  3. let titles = $('.hope_list_item_content');
  4. let titlesData= [];
  5. titles.each(function(i, elem) {
  6. let title = $(this).text();
  7. titlesData.push(title);
  8. })
  9. titlesData = titlesData.join('\r\n'); //将数组转换成字符串,以`\r\n`分隔
  10. return titlesData
  11. }

此处使用each遍历数据,不要问为什么不是forEach,因为这是cheerio的方法而不是原生js的方法- -
通过遍历的方法,将所有文章title保存在名为titlesData的数组中。

文章标题我们已经通过上面的函数获得了,console.log出来看下:

  1. const http = require('http');
  2. const cheerio = require('cheerio');
  3. const fs = require('fs');
  4. const url = 'http://ce.sysu.edu.cn/hope/FrontEnd/Index.aspx';
  5. function saveTitles(data) {
  6. let $ = cheerio.load(data);
  7. let titles = $('.hope_list_item_content');
  8. let titlesData= [];
  9. titles.each(function(i, elem) {
  10. let title = $(this).text();
  11. titlesData.push(title);
  12. })
  13. titlesData = titlesData.join('\r\n');
  14. return titlesData
  15. }
  16. http.get(url, function(res) {
  17. let html = '';
  18. res.on('data', function(data) {
  19. html += data;
  20. })
  21. res.on('end', function() {
  22. let output = saveTitles(html);
  23. console.log(output);
  24. })
  25. })

image_1be8fhb3r1938oaiqjnov7unp9.png-51kB

此时我们已经得到我们需要的数据。通过前面引入的fs模块,将数据写入到文件:

  1. res.on('end', function() {
  2. let output = saveTitles(html);
  3. // console.log(output);
  4. //将数据写入到文件
  5. fs.writeFile(output.txt, output, function() {
  6. console.log('数据写入成功')
  7. })
  8. })

执行下代码,可以在根目录找到output.txt文件,里面就有我们想要的数据。
image_1be8hta1hj0u1ujbdqo75l1oo0m.png-55.1kB

完整的代码:

  1. const http = require('http');
  2. const cheerio = require('cheerio');
  3. const fs = require('fs');
  4. const url = 'http://ce.sysu.edu.cn/hope/FrontEnd/Index.aspx';
  5. function saveTitles(data) {
  6. let $ = cheerio.load(data);
  7. let titles = $('.hope_list_item_content');
  8. let titlesData= [];
  9. titles.each(function(i, elem) {
  10. let title = $(this).text();
  11. titlesData.push(title);
  12. })
  13. titlesData = titlesData.join('\r\n');
  14. return titlesData
  15. }
  16. http.get(url, function(res) {
  17. let html = '';
  18. res.on('data', function(data) {
  19. html += data;
  20. })
  21. res.on('end', function() {
  22. let output = saveTitles(html);
  23. // console.log(output);
  24. fs.writeFile('output.txt', output, function() {
  25. console.log('数据写入成功')
  26. })
  27. })
  28. })

2、抓取图片数据

抓取图片数据和抓取文本数据的方法大同小异,其主要区别是,在保存图片数据的时候,我们是通过二进制形式进制保存的。
我们发现这个网站Raumrot上的图片很好看,想把它们都下载下来,但由于图片比较多,手动一张张保存下来太麻烦,因此我们可以通过爬虫将图片都爬下来。
首先。创建crawlerImg.js文件:

  1. const http = require('http');
  2. const fs = require('fs');
  3. const cheerio = require('cheerio');
  4. const url = 'http://raumrot.com/photo-set-landing/';
  5. http.get(url, function(res) {
  6. let html = '';
  7. res.on('data', function(data) {
  8. return html += data;
  9. })
  10. res.on('end', function(){
  11. console.log(html);
  12. })
  13. }).on('error', function(err) {
  14. console.log('出错: ' + err);
  15. })

同样,先获取整个页面的html代码数据,可以通过console.log()看下是否抓取到。

然后,筛选出图片,从html代码中获得图片的src地址,再用http.get()方法将图片下载下来。
通过查看源代码知道,我们需要的图片均在<img> 标签内,所以可以定义一个函数:

  1. function saveImages(data) {
  2. let $ = cheerio.load(data),
  3. img = $('img'); //获得图片的标签元素
  4. console.log('总计有' + img.length + '张图片');
  5. }

遍历图片标签,获得图片src地址,通过src地址下载图片,并将其保存下来:

  1. function saveImages(data) {
  2. let $ = cheerio.load(data),
  3. img = $('img');
  4. console.log('总计有' + img.length + '张图片');
  5. img.each(function(i, elem) {
  6. let imgSrc = $(this).attr('src');
  7. http.get(imgSrc, function(res) {
  8. let imageData = ''; //定义变量用于保存图片数据
  9. res.setEncoding('binary'); //将图片转换为二进制,以便保存
  10. res.on('data', function(data) {
  11. imageData += data;
  12. })
  13. res.on('end', function() {
  14. //定义图片名称,获取原图片后缀
  15. let imgPath = 'img-' + (i+1) + '.' + imgSrc.split('.').pop();
  16. //将图片的二进制数据写入文件,通过回调函数打印成功指示
  17. fs.writeFile(__dirname + '/imgs/' + imgPath, imageData, 'binary', function() {
  18. console.log('成功下载第' + (i+1) + '张图片');
  19. })
  20. })
  21. })
  22. })
  23. }

这里与获取文本数据不一样的是,将一张图片数据转换为二进制数据进行保存,然后马上输出,接着对下一张图片采取相同操作,一张一张进行。

完整的代码为:

  1. const http = require('http');
  2. const fs = require('fs');
  3. const cheerio = require('cheerio');
  4. const url = 'http://raumrot.com/photo-set-landing/';
  5. function saveImages(data) {
  6. let $ = cheerio.load(data),
  7. img = $('img');
  8. console.log('总计有' + img.length + '张图片');
  9. img.each(function(i, elem) {
  10. let imgSrc = $(this).attr('src');
  11. http.get(imgSrc, function(res) {
  12. let imageData = '';
  13. res.setEncoding('binary');
  14. res.on('data', function(data) {
  15. imageData += data;
  16. })
  17. res.on('end', function() {
  18. let imgPath = 'img-' + (i+1) + '.' + imgSrc.split('.').pop();
  19. fs.writeFile(__dirname + '/images/' + imgPath, imageData, 'binary', function() {
  20. console.log('成功下载第' + (i+1) + '张图片');
  21. })
  22. })
  23. })
  24. })
  25. }
  26. http.get(url, function(res) {
  27. let html = '';
  28. res.on('data', function(data) {
  29. return html += data;
  30. })
  31. res.on('end', function() {
  32. saveImages(html);
  33. })
  34. }).on('error', function(err) {
  35. console.log('出错: ' + err);
  36. })

node.js上运行,经过几秒后,我们就可以在images文件夹中得到图片:
image_1be8i1j3o5nvg917ggrcc11d413.png-647.5kB


三、总结

上面提到的小爬虫,仅仅对于单页面的数据进行获取分析,仅仅用于学习使用,而爬虫实则更多应用于做数据分析,且不是针对单页面,更是整个网站或更大的范围。事实上,很多编程语言都能够实现爬虫。简单归简单,在对爬虫有基本的了解之时,明白了爬虫的工作原理。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注