使用 node 进行简单爬虫学习

  最近在公众号看到篇 node 爬虫的文章,比较简单,本着学习的态度看完觉得可以加大点难度试一试。

  Github:data-crawler

一、Top250 - 爬取页面

  豆瓣电影 Top250 是基于网页爬取,每页25条数据,访问 URL 有一定规律。思路是获取 DOM 节点的内容,写入到 json 文件,下载电影的封面图片。cheerio用来解析 html 非常方便,写法可参考 抓取当前页面

1、入口文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const getFilmsInfo = require('./tools/getFilmsInfo')
const downloadImages = require('./tools/downloadImages')

getAllFilms()

/**
* https://movie.douban.com/top250 页面分页的规则
* get 请求,参数为 start,含义是每页25条数据,从第几条开始
* 如 https://movie.douban.com/top250?start=25, https://movie.douban.com/top250?start=100
*/
// 根据 url 抓取当前页面所有电影的信息
async function getAllFilms () {
for (let pageNum = 0; pageNum < 10; pageNum++) {
// 爬取数据并将需要的数据写到 json 文件
let films = await getFilmsInfo(`https://movie.douban.com/top250?start=${ pageNum * 25 }`, pageNum)

// 下载图片
await downloadImages(films.map(i => i.pic), pageNum)
}
}

2、抓取当前页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 根据 url 抓取当前页面所有电影的信息
*/
const fs = require('fs')
const https = require('https')
const cheerio = require('cheerio')

// url 爬取的网址,pageNum 已存在多少条数据
module.exports = async function getFilmsInfo (url, pageNum) {
return new Promise((resolve, reject) => {
https.get(url, res => {
// 分段返回的 自己拼接
let html = ''

// 有数据产生的时候 拼接
res.on('data', chunk => {
html += chunk
})

// 拼接完成
res.on('end', async () => {
const $ = cheerio.load(html)
let films = []
$('li .item').each(function () {
const sort = pageNum * 25 + films.length + 1
const title = $('.title', this).text()
const star = $('.rating_num', this).text()
const slogan = $('.inq', this).text()
const pic = $('.pic img', this).attr('src')
films.push({ sort, title, star, slogan, pic })
})

// 按页码写入 json 文件
let fileName = pageNum + 1
await fs.writeFile(`./result/top250/page-${ fileName }.json`, JSON.stringify(films, null, 2), err => {
if (err) {
reject(err)
} else {
console.log(`第${ fileName }页数据保存成功`)
resolve(films)
}
})
})
})
})
}

3、下载电影封面图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 下载图片
*/
const fs = require('fs')
const https = require('https')

module.exports = function downloadImage (pics, pageNum) {
for (let i in pics) {
https.get(pics[i], res => {
res.setEncoding('binary')
let str = ''

res.on('data', chunk => {
str += chunk
})
res.on('end', function () {
fs.writeFile(`./result/top250/images/page-${ pageNum + 1 }-${ Number(i) + 1 }.jpg`, str, 'binary', err => {
err && console.log(err)
})
})
})
}
}

二、电影列表 - 调用接口

  豆瓣电影全量列表(9900+条信息) 是通过接口去请求的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const fs = require('fs')
const request = require('request')

let sleepTime = 5000 // 过指定时间后返回结果,可以实现相邻请求有间隔时间
let errorCount = 0 // 失败计数
let errorMaxCount = 2 // 失败后尝试指定次数,依旧失败则停止
asyncGetAllFilms()

async function asyncGetAllFilms () {
for (let i = 127; i < 500; i++) {
let res = await getAllFilms(`https://movie.douban.com/j/new_search_subjects?start=${ i * 20 }`)

// 保存数据到 json 文件
await fs.writeFile(`./result/douban/page-${ i + 1 }.json`, JSON.stringify(res.data, null, 2), err => {
if (err) {
console.log('writeFile', err)
} else {
console.log(`第${ i + 1 }页数据保存成功`)
}
})
}
}

// 获取所有电影
async function getAllFilms (url) {
return new Promise((resolve, reject) => {
request(url, (error, res, body) => {
if (!error && res.statusCode === 200) {
setTimeout(() => {
resolve(JSON.parse(body))
}, sleepTime)
} else {
console.log('error', error)
if (errorCount < errorMaxCount) {
setTimeout(() => {
errorCount++
getAllFilms(url)
}, sleepTime)
} else {
reject(error)
}
}
})
}).catch(console.log)
}

三、参考资料

你不知道的 node 爬虫原来这么简单

以上

随笔标题:使用 node 进行简单爬虫学习

随笔作者:刘先玉

发布时间:2020年08月28日 - 09:44:17

最后更新:2020年08月28日 - 09:44:17

原文链接:https://liuxianyu.cn/article/node-data-crawler.html