爬取小说
这次分享一个爬取一个网站小说的demo,刚开始学习爬虫的朋友也可以参考思路去编写自己的程序,爬取思路——
首先,找到你要爬取的网站的链接,然后获取书籍的链接;
二、每本书都有一个自己的链接;
三、然后该书的链接的内容会包含目录列表链接
四、我们便可以借用书籍的链接去获取目录链接
五、然后根据目录链接去获取目录内容,然后讲内容爬取出来并写入文件中就完成爬取了
基本就是这五部就可以完成爬取任务了
获取书籍链接代码段
var data = [];
for (var i = 1; i < 2; i++) {
//此处链接就是包含很多书籍的链接
var d = await getHttpData('http://book.zongheng.com/store/c1/c1003/b0/u0/p' + i + '/v0/s1/t0/u0/i1/ALL.html',
getBookName);
//es6的数组拼接
data = [...data, ...d];
}
//这是获取书名部分代码
function getBookName($) { //参数$是爬取的内容,下面是处理,匹配出我们要的信息
var reg = /http.+.html/g;
var url = [];
var _this = $('.store_collist .bookbox');
//获取书名数组
//这里基本就是html的DOM操作了,就不解释了
$('.store_collist .bookbox').each(function(index, ele) {
var _url = $(ele).find('.bookname').html().match(reg);
var bookName = $(ele).find('.bookname a').text();
// console.log(bookName)
var author = $(ele).find('.bookilnk a:first-child').text();
_url[0] = _url[0].replace('com/book', "com/showchapter");
//图片链接
var imgUrl = $(ele).find('.bookimg img').prop('src');
//获取本书描述
var describe = $(ele).find('.bookintro').text();
//将数据保存到数组,最终返回一个对象数组
var obj = {
bookUrl: _url[0],
bookName: bookName,
author: author,
imgSrc: imgUrl,
describe: describe,
childrenUrl: []
}
url.push(obj);
})
return url;
}
获取目录链接
for (var i = 0; i < data.length; i++) {
var d1 = await getHttpData(data[i].bookUrl, getChapterList);
data[i].childrenUrl = d1;
}
//获取目录函数,跟上面的书籍链接一样的方式
function getChapterList($) {
var url = [];
var reg = /http.+.html/g;
$('.chapter-list .col-4').each(function(index, ele) {
//用正则匹配出我们要的链接
var str_url = $(this).html().match(reg);
url.push(str_url[0]);
});
return url;
//书名数组->目录数组
}
获取内容
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].childrenUrl.length; j++) {
var obj = await getHttpData(data[i].childrenUrl[j], getChapterContent);
var p_txt = `aa/${data[i].bookName}-${data[i].author}/${obj.txtName}`
//先判断上级目录是否存在,不存在就创建
if (!fs.existsSync(`aa/${data[i].bookName}-${data[i].author}`)) {
try {
fs.mkdirSync(`aa/${data[i].bookName}-${data[i].author}`)
} catch (e) {
console.log(e)
}
}
//如果问价存在,就不写
if (!fs.existsSync(p_txt)) {
try {
fs.writeFileSync(p_txt, obj.content)
} catch (e) {
console.log(e)
}
}
//用来控制时间间隔为1s,因为如果不控制速度的话,会被服务器拒绝
for (var k = 0; k < 1000000000; k++) {}
}
}
//爬取内容方法
function getChapterContent($) {
//获取章节标题
var reg1 = /(?:\s)*/g;
var title = $('.reader_box .title_txtbox').text();
//作者
var author = $('.bookinfo a:first-child').text();
//字数
var number = $('.bookinfo span:nth-child(2)').text()
//时间
var time = $('.bookinfo span:nth-child(3)').text();
//内容
var content = $('.reader_box .content').html();
var info = `${title}-${number}字-${time}`;
content = `<h5>${info}</h5>${content}`;
//正则匹配,去掉一起不合法的字符并替换为空格
title = title.replace(/\?|\!|\"|\:|\<|\>|\||\\|\//g, " ");
var obj = {
txtName: title + ".txt",
content: content
}
console.log("获取到" + info)
return obj;
}*斜体样式*
*整个爬取就是分为三部分,首先获取书籍,然后根据书籍获取目录
,
然后根据目录获取内容,由于这三部分的数据获取需要一个顺序,所以
我采用了promise对象讲一些异步操作转换成同步,不理解如何转同步的朋友
可以先不管,直接照着用就行了,有些地方不需要同步的,只是为了让这个爬取过程编程顺序过程,会看的更仔细
注:有一段空循环代码,是为了控制每次爬取间隔为1s,加上去用来阻塞代码运行的,这里不能用setTimeout是异步的,不会阻塞,不控制爬取速度会被服务器禁止访问
以下是完整的代码
const http = require('http');
const fs = require('fs');
const cheerio = require('cheerio');
function Pac() {}
//爬取内容,用promise对象包装异步获取数据的操作
function getHttpData(http1, callback) {
//爬取内容
// console.log(this);
var p = new Promise(function(resolve, reject) {
http.get(http1, function(req, res) {
var html = '';
req.on('data', function(data) {
html += data;
})
req.on('end', function() {
// console.log(html,);
//爬取完毕装载到,并返回装载后的对象
//调用处理函数来处理装载的数据
const $ = cheerio.load(html, {
decodeEntities: false
})
var d = callback($);
resolve(d);
})
})
}).catch((err) => {
console.log(err);
})
return p;
}
//获取章节内容
function getChapterContent($) {
//获取章节标题
var reg1 = /(?:\s)*/g;
var title = $('.reader_box .title_txtbox').text();
//作者
var author = $('.bookinfo a:first-child').text();
//字数
var number = $('.bookinfo span:nth-child(2)').text()
//时间
var time = $('.bookinfo span:nth-child(3)').text();
//内容
var content = $('.reader_box .content').html();
var info = `${title}-${number}字-${time}`;
content = `<h5>${info}</h5>${content}`;
//正则匹配,去掉一起不合法的字符并替换为空格
title = title.replace(/\?|\!|\"|\:|\<|\>|\||\\|\//g, " ");
var obj = {
txtName: title + ".txt",
content: content
}
console.log("获取到" + info)
return obj;
}
//获取章节连接
function getChapterList($) {
var url = [];
var reg = /http.+.html/g;
$('.chapter-list .col-4').each(function(index, ele) {
//用正则匹配出我们要的链接
var str_url = $(this).html().match(reg);
url.push(str_url[0]);
});
return url;
//书名数组->目录数组
}
function getBookName($) {
var reg = /http.+.html/g;
var url = [];
var _this = $('.store_collist .bookbox');
//获取书名数组
$('.store_collist .bookbox').each(function(index, ele) {
var _url = $(ele).find('.bookname').html().match(reg);
var bookName = $(ele).find('.bookname a').text();
// console.log(bookName)
var author = $(ele).find('.bookilnk a:first-child').text();
_url[0] = _url[0].replace('com/book', "com/showchapter");
//图片链接
var imgUrl = $(ele).find('.bookimg img').prop('src');
//获取本书描述
var describe = $(ele).find('.bookintro').text();
var obj = {
bookUrl: _url[0],
bookName: bookName,
author: author,
imgSrc: imgUrl,
describe: describe,
childrenUrl: []
}
url.push(obj);
})
return url;
}
Pac.start = async function() {
//拿到书名数据
var data = [];
for (var i = 1; i < 2; i++) {
var d = await getHttpData('http://book.zongheng.com/store/c1/c1003/b0/u0/p' + i + '/v0/s1/t0/u0/i1/ALL.html',
getBookName);
//es6的数组拼接
data = [...data, ...d];
}
//遍历书名,获取目录连接
for (var i = 0; i < data.length; i++) {
var d1 = await getHttpData(data[i].bookUrl, getChapterList);
data[i].childrenUrl = d1;
}
//设置速度为1s爬取一次
//遍历目录链接,获取内容
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].childrenUrl.length; j++) {
var obj = await getHttpData(data[i].childrenUrl[j], getChapterContent);
var p_txt = `aa/${data[i].bookName}-${data[i].author}/${obj.txtName}`
//先判断上级目录是否存在,不存在就创建
if (!fs.existsSync(`aa/${data[i].bookName}-${data[i].author}`)) {
try {
fs.mkdirSync(`aa/${data[i].bookName}-${data[i].author}`)
} catch (e) {
console.log(e)
}
}
//如果问价存在,就不写
if (!fs.existsSync(p_txt)) {
try {
fs.writeFileSync(p_txt, obj.content)
} catch (e) {
console.log(e)
}
}
//用来控制时间间隔为1s,因为如果不控制速度的话,会被服务器拒绝
for (var k = 0; k < 1000000000; k++) {}
}
}
console.log("爬取完成")
}
Pac.start();