上一篇中,把前端试图部分的架子搭了出来,这一篇我们转换下思路从后台入手,思考一下小书虫到底能做些什么,既然叫做小书虫app,构想之初就是:app的主要内容来源为网络啦,所以这篇实现一个爬虫功能,配合视图部分做成一个简单的小说搜索引擎。
我们首先找一个地方放置我们的后台项目,cd进入项目文件夹npm init初始化一个项目,并npm install需要的模块,这里先安装request(负责http请求)、cheerio(网页解析)、bluebird(promise模块处理异步)
然后就开始编辑入口文件了,默认是index.js,我改成app.js(或者entry.js都行)引入模块:
var request = require('request');//http请求
var Promise = require("bluebird");//promise模块处理异步
var cheerio = require('cheerio');//网页解析
接下来就找个网站爬爬看,既然是书虫这里就找起点中文网吧,打开起点首页,点击全部小说就进入到所有小说放置的网页了,考虑到是为搜索引擎做准备我打算只爬到书籍的书名和链接,这样最大化简化的我们的速度,F12或者反键查看元素定位到书名的源码可以看到:
<a href="//book.qidian.com/info/1004608738" target="_blank" data-eid="qd_B58" data-bid="1004608738">圣墟</a>
可以看到,每个书名和其链接都是如上的a标签,因此我们就能通过其选择器$('.book-mid-info h4 a')
来定位这个元素,为保证正确可以在浏览器提供的环境里测试一下(自带jQuery选择器):
测试发现正确输出了我们想要的a标签,再观察页面地址,和布局,得知每一页20部小说,共4万多页,包含了起点的所有小说,到这里我们大概了解了基本规则:
var request = require('request');
var Promise = require("bluebird");
var cheerio = require('cheerio');
//起点中文网
var baseUrl = "https://www.qidian.com/all?orderId=&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=";
var QiDianPages = 49145;
var rule = '.book-mid-info h4 a';
现在开始爬取页面,编写一个爬取方法,传入基本路径和页面参数组合成地址进行爬取,爬取使用request模块进行http请求,使用promise来进行异步优化,考虑好之后我们就可以编写了:
//爬取页面
function HtmlCrawl(baseUrl,page) {
//使用Promise对象确保异步的爬取顺利进行
return new Promise(function (resolve,reject) {
var url = baseUrl + page;
console.log("爬取地址:" + url);
request(url,function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body)
}else {
reject(error)
}
});
});
}
通过HtmlCrawl方法返回的的页面实际上是一大串html,我们无法进行操作,所以就要借助cheerio,它自带的选择器就像jQuery一样能帮助我们解析定位到我们想要的内容,所以下面编写cheerio解析方法:
//页面解析
function CheerioFilter(page,rule) {
var $ = cheerio.load(page); //当前的$符相当于拿到了所有的body里面的选择器
var nameEles = $(rule);
var dataArray = [];
console.log(nameEles);
for (var i = 0; i < nameEles.length; i++) {
var obj = new Object();
obj.tilte = nameEles[i].children[0].data;
obj.link = "https:" + nameEles[i].attribs.href;
dataArray.push(obj);
}
return dataArray;
}
CheerioFilter方法传入两个参数,要解析的页面、解析规则(前面我们已经找到),将解析好的内容过滤成我们想要的书名和链接后保存成对象push到数组中,最后返回数组,就完成了解析了。
下面测试一下我们的编码效果,起点有4万多页,我们先测试小一些,几十页看看,创建一个数组放置返回的promise对象数组,循环执行爬虫函数爬到每一页,然后返回的结果存到数组中,再来开始promise链:
var promiseObjectArray = [];
for (var i = 1; i <= QiDianPages; i++) {
promiseObjectArray.push(HtmlCrawl(baseUrl,i));
}
Promise
.all(promiseObjectArray)
.then(function (pages) {
var result = [];
pages.forEach(function (html) {
var bookItem = CheerioFilter(html,rule);
result.push(bookItem);
});
console.log(result[0]);
console.log(result[0].length);
})
在拿到页面数据后再解析页面保存到最终数组,就完成了基本爬取了,看看运行效果:
测试一下后发现速度还是很快的,使用promise不仅优化了爬取的速度,还优化了我们的代码风格
这一篇就到这里,这一篇完成爬虫后下面就可以写匹配查询了,合起来就是一个简易的搜索引擎,下篇再搞。