简单的node.js爬虫,爬取豆瓣相册

每天下班之后,都会刷一刷豆瓣。其中的一个重要原因就是我关注的友邻们的相册经常更新好看的图片。正好这段时间在学习爬虫,就打算用node做个爬虫,把友邻的相册爬下来,下面是爬取的过程。

爬虫原理介绍

网络爬虫,又叫做网络蜘蛛、自动检索工具。是一种“自动化浏览网络”的程序,或者说是一种网络机器人。
它们被广泛用于互联网搜索引擎或其它类似网站,以获取或更新这些网站的内容和检索方式。


爬虫,蜘蛛

世界上第一个网络爬虫是由麻省理工学院(MIT)的学生马修.格雷(Matthew Gray)在1993年写成的。
他给这个程序起了个名字“互联网漫游者”(“www wanderer”)。之后的网络爬虫越来越复杂,但是原理是一样的。

爬虫的工作流程

  1. 首先选取种子URL;
  2. 将这些URL放入待抓取URL队列;
  3. 从待抓取URL队列中取出待抓取在URL,解析DNS,并且得到主机的ip,并将URL对应的网页下载下来,存储进已下载网页库中。此外,将这些URL放进已抓取URL队列。
  4. 分析已抓取URL队列中的URL,分析其中的其他URL,并且将URL放入待抓取URL队列,从而进入下一个循环。


    爬虫工作过程,图片来自网络

本次爬虫用到的模块/框架

express

可以设置中间件来响应 HTTP 请求。
定义了路由表用于执行不同的 HTTP 请求动作。
可以通过向模板传递参数来动态渲染 HTML 页面。

superagent

它是nodejs里一个客户端请求代理模块,可以很方便的处理get,post,put,delete,head请求

cheerio

可以理解为服务端的jQuery

request

用来处理http请求

fs

fs是filesystem的缩写,该模块提供本地文件的读写能力

大致的思路

先使用express搭建一个node服务;使用superagent向目标页面发送请求,并获取返回的页面;使用cheerio处理获取的页面元素,使用fs读写本地文件,把图片下载到本地。

实现的过程

首先安装需要的框架和模块;

1. 搭建一个node服务

新建一个名为index.js的文件,输入如下内容:

const express = require ('express')
const app = express()

let server = app.listen(3000, function () {
    let host = server.address().address 
    let port = server.address().port
    console.log('your app is running at http://%s:%s', host, port)
})

打开命令行,切换到代码所在目录,并执行node index.js,如果执行成功,则如下图:

your app is running.png

2. 分析相册页面的结构

以这个相册为例,无意间看到的
打开F12控制面板,查看相册图片所在的HTML结构,会发现它们都包含在一个class名字为photolst的div中。

图片所在的结构.png

3. 使用superagent向目标地址发送请求,并用cheerio处理结构

代码如下:

// 向目标地址发送请求
superagent.get('https://www.douban.com/photos/album/139203394/').end((err, res) => {
    if (err) { // 失败...
        console.log(`相册获取失败${err}`)
    } else { // 成功...
        pic = getBeautyPic(res)
    }
})

定义一个名为getBeautyPic的方法来处理获取的内容

// 处理爬取到的图片
let getBeautyPic = (res) => {
    let pic = []
    // 访问成功,包含图片的dom结构就会在res里
    // 使用cheerio的load方法,把HTMLdocument作为参数传入,就可以使用类似jQuery的方法进行操作
    let $ = cheerio.load(res.text)
    // 找到目标数据所在dom结构,获取数据
    $('.photolst .photo_wrap img').each((idx, ele) => {
        pic.push($(ele).attr('src'))
    })
    // 把图片的链接放到一个数组中返回
    return pic
}

此时的代码如下图:

const express = require ('express')
const superagent = require ('superagent')
const cheerio = require ('cheerio')
const request = require ('request')
const app = express()

// 创建一个服务
let server = app.listen(3000, function () {
    let host = server.address().address // ?
    let port = server.address().port
    console.log('you app is running at http://%s:%s', host, port)
})

// 爬取豆瓣的相册
superagent.get('https://www.douban.com/photos/album/139203394/').end((err, res) => {
    if (err) {
        console.log(`相册获取失败${err}`)
    } else {
        pic = getBeautyPic(res)
    }
})

// 处理爬取到的图片
let getBeautyPic = (res) => {
    let pic = []
    // 访问成功,包含图片的dom结构就会在res里
    // 使用cheerio的load方法,把HTMLdocument作为参数传入,就可以使用类似jQuery的方法进行操作
    let $ = cheerio.load(res.text)
    // 找到目标数据所在dom结构,获取数据
    $('.photolst .photo_wrap img').each((idx, ele) => {
        pic.push($(ele).attr('src'))
    })
    return pic
}

此时已经把目标页面的图片链接都放在了pic这个数组中,接下来,把图片下载到本地就可以了。

4. 下载图片到本地

定义一个下载图片的方法:

// 爬取图片到本地
let download = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
      request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    })
}

上面的这个方法依赖于request模块和fs模块,需要安装一下。
因为图片不止一张,所以循环获取。
整理后的代码如下:

const express = require ('express')
const superagent = require ('superagent')
const cheerio = require ('cheerio')
const request = require ('request')
const fs = require ('fs')
const app = express()

// 创建一个服务
let server = app.listen(3000, function () {
    let host = server.address().address // ?
    let port = server.address().port
    console.log('your app is running at http://%s:%s', host, port)
})
// 爬取豆瓣的相册
superagent.get('https://www.douban.com/photos/album/139203394/').end((err, res) => {
    if (err) {
        console.log(`相册获取失败${err}`)
    } else {
        pic = getBeautyPic(res)
        for (const uri of pic) { // 循环下载
            let filename = uri.substring(uri.lastIndexOf('/')+1) // 截取图片的名字
            download(uri, filename, function(){
                console.log(`${filename}下载完成`);
            });
        }
    }
})

// 爬取图片到本地
let download = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
      request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    });
};

// 处理爬取到的图片
let getBeautyPic = (res) => {
    let pic = []
    // 访问成功,包含图片的dom结构就会在res里
    // 使用cheerio的load方法,把HTMLdocument作为参数传入,就可以使用类似jQuery的方法进行操作
    let $ = cheerio.load(res.text)
    // 找到目标数据所在dom结构,获取数据
    $('.photolst .photo_wrap img').each((idx, ele) => {
        pic.push($(ele).attr('src'))
    })
    return pic
}

5. 在node中执行文件

重启node服务,会看到图片一个个都开始下载了:


图片开始下载.png

程序执行完毕后,打开本地文件夹,就会看到多了一些图片,这些图片就是刚刚下载的了。


刚刚下载的图片们,好孩子看不见.png

这个程序还有非常多的地方需要做优化,比如当前页面图片下载完后,应该自动开始下一个页面图片的下载、用promise改写、对错误的处理...等等 后面会继续改进.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容