使用 node 开发时难免会爬取一些数据资料,不可避免就需要一些爬虫功能,主要使用的技术 node + express + cheerio + axios + writeFile + readFile,只是作为基本的演示记录,不涉及数据库的存储等操作,通过读取文件代替。
- node 环境搭建;
- 创建项目文件夹 并初始化 package.json;
npm init -y
(-y 设置默认信息) - 安装 express
npm install express
; - 安装 axios
npm install axios
- 安装 cheerio
npm install cheerio
入口文件
// index.js
const express = require('express')
const axios = require('axios')
const cheerio = require('cheerio')
const writeFile = require('./writeFile')
const readFile = require('./readFile')
const { splitNextId } = require('./utils')
const { PORT, TRY_NUM } = require('./config')
const app = express()
let currentId = '5708267';
let isEnd = false;
let errId = "";
let errNum = 0;
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(PORT, () => {
console.log(`Example app listening on port ${PORT}`)
// 获取数据,可以写在路由中通过接口形式调用
getData(currentId)
})
function getData(id) {
axios.get(`https://www.xxx.com.cn/xxx/xxx/${id}.html`).then(async data => {
const $ = await cheerio.load(data.data);
const OContent = $('article');
const OTitle = $('header');
const OId = $('.nr_page>.dise:nth-of-type(3)').attr('href');
const newId = splitNextId(OId)
const title = OTitle.text().trim();
const content = OContent.text().trim();
if(!content || newId === '') {
isEnd = true;
console.log('数据爬取结束')
return false;
}
const msg = `<div>当前是: ${title}, Id 是: ${currentId} ,下一章节ID是: ${newId}</div>`
let val = msg + content;
const oldFile = await readFile();
await writeFile(currentId, oldFile + val);
if(isEnd) {
await getData(newId)
currentId = newId;
} else {
console.log('数据爬取结束')
}
}).catch((err) => {
if(errId == id) {
errNum += 1;
} else {
errNum = 0;
errId = id
}
if(errNum < TRY_NUM) {
getData(id)
} else {
isEnd = true;
console.log(`重连已超过${TRY_NUM}次自动停止`)
}
})
}
读取文件
// readFile.js
const fs = require('fs');
const { FILE_URL } = require('./config');
/**
* fileUrl写入的文件地址
*/
module.exports = function readFile(fileUrl) {
return new Promise((resolve, reject) => {
fs.readFile(fileUrl || FILE_URL, (err, data) => {
if(err) {
resolve('')
}
resolve(data.toString())
})
})
}
写入文件
// writeFile.js
const fs = require('fs');
const { FILE_URL } = require('./config');
/**
* currentId: 当前写入的信息id
* text:写入的内容
* fileUrl写入的文件地址
*/
module.exports = function writeFile(currentId, text, fileUrl) {
return new Promise((resolve, reject) => {
fs.writeFile(fileUrl || FILE_URL, text, error => {
if(error) {
throw new Error('新增失败')
} else {
console.log(currentId, '新增成功...')
resolve(true)
}
})
})
}
工具类函数
// utils.js
function splitNextId(url) {
try {
const arr = url.split('/');
const [ id ] = arr.at(-1).split('.')
return id;
} catch (error) {
return '';
}
}
module.exports = {
splitNextId
}
通过 nodemon index.js
运行即可,如果未安装 nodemon 也可通过 node index.js
运行。