环境及工具
nodeJs:自行安装
cheerio:解析获取到的html信息(npm install cheerio)
https:用于请求数据
fs:将解析后的书籍信息以json格式存入文件内
项目gitee地址:xulixin/node-spider-topbooks
一,分析URL爬取规则
当start参数为0时,获取的是排名1-25的书籍,为25时,获取的是排名26-50的书籍,以此类推......
二,爬取数据,并保存数据
由上述分析,可以制定出以下规则,先爬取第一页的数据试试。
const https = require('https')
const fs = require('fs')
const cheerio = require('cheerio')
let rank = 1 //排名
let page = 0//从第一页开始爬取,每页有25条数据
let books = []//爬取到的书籍信息
https.get(`https://book.douban.com/top250?start=${page}`,res=>{
let data = ''
res.on('data',chunk=>{
data += chunk
})
res.on('end',()=>{
const $ = cheerio.load(data)
$('table .item').each(function(){
//书籍名称
let title = $('.pl2 a',this).attr('title')
//星级
let start = $('.star .rating_nums',this).text()
//图片地址
let img = $('.nbg img',this).attr('src')
let inq = $('.inq',this).text()
books.push({
title,inq,rank,start,img
})
rank++
})
fs.writeFile('./books.json', JSON.stringify(books),function(err){
if(!err){
console.log('书籍信息获取完毕');
}
})
})
})
运行后成功获取到数据,并存入boos.json中。
三,分页查询,获取TOP250全部书籍
我这里使用的递归方案,拿到第一页的数据后,再获取下一页的数据,防止rank乱了
const https = require('https')
const fs = require('fs')
const cheerio = require('cheerio')
let rank = 1 //排名
let page = 0//从第一页开始爬取,每页有25条数据
let books = []//爬取到的书籍信息
function getDBRank(page){
return new Promise((resolve,reject)=>{
https.get(`https://book.douban.com/top250?start=${page}`,res=>{
let data = ''
res.on('data',chunk=>{
data += chunk
})
res.on('end',()=>{
const $ = cheerio.load(data)
$('table .item').each(function(){
//书籍名称
let title = $('.pl2 a',this).attr('title')
//星级
let start = $('.star .rating_nums',this).text()
//图片地址
let img = $('.nbg img',this).attr('src')
let inq = $('.inq',this).text()
books.push({
title,inq,rank,start,img
})
rank++
resolve(`排名第${page+1}至第${page+25}加载完毕`)
})
})
})
})
}
function getResult(page){
if(page===250){
fs.writeFile('./books.json', JSON.stringify(books),function(err){
if(!err){
console.log('书籍信息获取完毕');
}
})
}else{
getDBRank(page).then(res=>{
console.log('res',res)
getResult(page+25)
})
}
}
getResult(page)
运行,获取top250所有的书籍数据
四,下载书籍对应的图片(以下为完整代码)
封装一个getImages函数,获取图片并将其保存至downImg文件夹内,注意首先得在根目录下建一个downImg文件夹。
const https = require('https')
const fs = require('fs')
const cheerio = require('cheerio')
let rank = 1 //排名
let page = 0//从第一页开始爬取,每页有25条数据
let books = []//爬取到的书籍信息
function getDBRank(page){
return new Promise((resolve,reject)=>{
https.get(`https://book.douban.com/top250?start=${page}`,res=>{
let data = ''
res.on('data',chunk=>{
data += chunk
})
res.on('end',()=>{
const $ = cheerio.load(data)
$('table .item').each(function(){
//书籍名称
let title = $('.pl2 a',this).attr('title')
//星级
let start = $('.star .rating_nums',this).text()
//图片地址
let img = $('.nbg img',this).attr('src')
let inq = $('.inq',this).text()
books.push({
title,inq,rank,start,img
})
rank++
resolve(`排名第${page+1}至第${page+25}加载完毕`)
})
})
})
})
}
function getResult(page){
if(page===250){
fs.writeFile('./books.json', JSON.stringify(books),function(err){
if(!err){
console.log('书籍信息获取完毕');
}
})
getImages(books)
}else{
getDBRank(page).then(res=>{
console.log('res',res)
getResult(page+25)
})
}
}
function getImages(imgList){
imgList.forEach(img=>{
https.get(img.img,res=>{
var imgData = ""
res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开
res.on("data", function(chunk){
imgData+=chunk;
});
res.on("end", function(){
fs.writeFile(`./downImg/${img.rank}_${img.title}.png`, imgData, "binary", function(err){
if(err){
console.log("下载失败");
}
});
});
})
})
}
getResult(page)
图片也成功下载下来了!