§ 1. 爬虫的策略
§ 1.1 初识爬虫
§ 1.2 爬虫分类
§ 1.3 爬虫策略
§ 2. 爬虫的流程
§ 3.1 明确需求
§ 3.2 创建项目
§ 6.3 完成代码
- 爬虫的策略
1.1 初识爬虫
爬虫,因为它用于网络中,故又名网络爬虫、网络机器人,在执行时,动作像蜘蛛,又被称为网络蜘蛛;虽然称呼很多,但它的本质是一段程序或脚本代码,通过程序或代码,按照一定的既定规则,自动去抓取网络中的数据信息,因此,也是搜索引擎的组成部分。
爬虫在抓取数据时,首先从一个或多个 URL 地址入手,获取页面的初始数据,然后再根据这些既定的数据组合成新的条件,不断再生成新的 URL 地址,加入到现有的队列中,直到抓取的数据完全满足需求为止,而这一切的操作,在编写好代码之后,都是自动进行的。
1.2 爬虫分类
根据爬虫获取数据的范围和深度不同,可以分为下列 4 种类型。
1. 全网爬虫
全网是指整个万维网,先从几个指定的 URL 入手,后扩展到全部的万维网,通常是获取全网的分类数据,这种方式用于为大型的搜索公司提供数据支持,也可以为巨头公司提交数据采集。
2. 主题爬虫
与全网爬虫不同,主题爬虫有明确的抓取目标和既定的抓取规则,在有限的范围内进行数据的采集和过滤,这种方式常用于某一主题的数据收集,如股票、新闻、疫情数据等。
3. 增量爬虫
与前两种都不相同,所谓的增量爬虫是指对已获取的数据保存至数据库,并通过某些约束机制,如 id 值不能相同方式,抓取更新后和最新的数据,用于确保每一次获取的都是增量数据,
4. 深度爬虫
与前面三种类型都不同,深度爬虫需要提供用户的交互信息,如用户登录或输入关键字以后,依据相关的已知信息,进行深度页面数据的抓取和采集,如用户信息、历史数据等。
1.3 爬虫策略
不同类型的爬虫,基于不同的爬虫策略,全网爬虫,通常基于广度和深度;主题爬虫,基于内容的相关性;增加爬虫,基于更新的方法,深度爬虫,则更多基于表单中数据的提交策略,以主题爬虫为例,它的策略更多是基于内容在 DOM 中的节点分析,如下图 1-1 所示:
在上述示意图 1-1 中,无论是 URL 地址还是节点,都是基于主题内容的,当通过 URL 获取到页面后,根据内容定位到控制节点,通过控制节点,获取真正的内容数据,即爬虫节点;在完成首次 URL 数据抓取后,带将根据抓取到的节点内容,形成第二次 URL 地址,再将定位控制节点,获取二次 URL 地址中的爬虫数据,以此类推。
当然,在真正通过爬虫的方式获取业务数据时,使用的策略通常是综合性的,即既会考虑内容确定的控制点,也要考虑到抓取站点的负载,同时,为了提升抓取的效率,会启用多台电脑分担一台机器的负载,并实现并行抓取的效果,最后,为了更新抓取的需要,还会考虑再次抓取周期的策略,它是一套组合拳。
- 爬虫的流程
2.1 应用开发的流程
如果要制作一个具有爬虫功能的应用,通常在开发前期会有如下图 1-2 所示的流程:
在上述示意图 1-2 中,爬虫应用的开发与其他应用开发相同,前期需要明确需求,而这个需求就是要抓取什么内容,因为大部分的爬虫应用都属于主题型开发,确定抓取内容后,再确定使用什么开发工具进行抓取,因为现在有很多工具都可以实现爬虫的功能,一个好的开发工具,可以使用应用开发事半功倍。
在明确了需求和确定了工具之后,接下来的流程就是编码了,无论使用何种的开发工具,在编码阶段都要通过 URL 获取到 DOM,再分析 DOM 结构,抓取数据,并同时写入数据库,完成一种抓取的流程。
当代码经历了调试和测试之后,就可以正式部署到服务器上,项目开发宣告完成,后期将会做代码的性能调优和新版本的不断迭代,优化和丰富项目的功能。
2.2 代码执行的流程
爬虫的流程除了应用开发流程之外,对于程序员来说,更为重要的是,如何编写爬虫的代码,它的执行流程又是如何进行的,下面以 Node 为例,一个基于 Node 框架的爬虫代码执行流程如下图 1-3 所示:
在上述示意图 1-3 中:
· 第 1 步将初始的 URL 作为种子 URL,进入到待抓取 URL 队列中,并分别开始按队列的 URL 发送请求,获取数据,分析 DOM 节点;
· 第 2 步中将获取的主题节点数据写数据库中,完成一种抓取的操作过程;
· 与此同时,第 3 步将写入已获取到的数据的 URL 作为已抓 URL,放入队列中,以备使用;
· 当需要再次抓取时,执行第 4 步,从队列中抽出新的 URL 到待抓取 URL 中,再次进行抓取操作。
需要说明的是,上述示意图 1-3 是指 URL 较多时使用,如果就是一或二个 URL 地址,可以省略第 3 步和第 4 步,即第 1 步执行时,直接分析页面结构,获取有用的主题数据,进入第 2 步直接写入数据库。
- Superagent 请求页面
3.1 明确需求
有许多专业从事三维动画制作的人员,希望能快速寻找到已经完成的三维动画效果视频作为参考一个素材,因此,想通过爬虫的形式获取到网络中三维动画的视频文件资源,完整的需求如下:
· 以列表的形式展示抓取的视频文件资源
· 点击列表项进入详细的内容页
· 抓取的数据先保存在数据库中
· 页面发送请求获取数据库中的记录
3.2 创建项目
根据需求,使用 HTML5 + Node + MySQL 的方式来完成,其中,HTML5 负责静态页的制作和请求数据的展示,Node 负责爬虫功能的实现和数据请求 API 的处理,MySQL 负责数据的写入和保存,结构如下图 1-4 所示:
明确项目的整体结构之后,按照如下的步骤创建项目。
1. 添加一个名称为 Code 的空文件夹,并使用 npm init 初始化该文件夹
文件夹初始化成功之后,自动添加了一个名称为 package.json 的文件,包含的代码如下所示:
{
"name": "code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "tgrong",
"license": "ISC"
}
2. 分别添加两个静态页面和一个 JavaScript 文件,文件夹的整体结构如下图 1-5 所示:
在项目文件夹中,添加了两个静态的页面文件,其中名为 index.htm 的文件用于以列表的方式显示获取到的爬虫记录,另一个名为 disp.html 的文件用于显示点击列表的记录后进入的详细内容;此外,名为 api.js 文件用于实现页面爬虫功能和数据请求时的 API 支持。
3. 在项目文件夹中分别安装项目依赖功能模块
当项目文件夹中的功能文件创建完成后,接下来分别使用 yarn 安装开发时需要的依赖模块。在文件夹中,打开终端,分别执行下面的指令,安装相应的依赖模块:
$ yarn **add** cheerio
$ yarn **add** express
$ yarn **add** mysql
$ yarn **add** superagent
$ yarn **add** superagent-charset
各模块的功能将在后续内容中详细介绍,当依赖模块全部安装成功后,项目中的 package.json 文件的内容也会发生变化,新增的依赖项的内容如下所示:
...
"dependencies": {
"cheerio": "^1.0.0-rc.3",
"express": "^4.17.1",
"mysql": "^2.18.1",
"superagent": "^5.2.2",
"superagent-charset": "^1.2.0"
}
在上述的依赖模块中,除了安装“Superagent”外,还安装了“superagent-charset”,该模块是解决抓取的网站内容不是 UTF-8 编码时,将返回乱码的问题。
3.3 使用 Superagent 发送请求
接下来,通过 URL 地址访问的方式,调用 Superagent 模块,向指定的网址发送请求,并设置请求的编码格式,为了实现上述功能,首先,调用 express 模块,设置一个 URL 访问的地址,代码如下:
var express = require('express');
var app = express();
app.get("/get", (req, res) => {
//设置页面内容编码的格式
res.header("Content-Type", "text/html; charset=utf-8");
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
res.write("全部数据生成完成!");
res.end();
})
app.listen(3000);
console.log("http://localhost:3000/");
Superagent 是一个轻量级的客户端请求代理模块,用于 Node 环境中,使用时,导入到文件中,为了解决在抓取页面过程中的乱码问题,还要导入 superagent-charset 模块,在该模块的基础之上发送请求,代码如下:
const superagent = require('superagent');
let charset = require('superagent-charset');
let request = charset(superagent);
然后,设置请求的 URL 地址,定义一个发送请求的函数,调用 Superagent 模块,发送请求,代码如下:
let url_soh = "so.vjshi.com/";
let url_mp4 = "mp4.vjshi.com/";
function searchImgs(){
let url = url_soh + 'Search?wd=%E4%B8%89%E7%BB%B4%E5%8A%A8%E7%94%BB&st=y&type=v';
request.get(url)
.charset('utf-8')
.buffer(true)
.end((err, res) => {
if (err) {
console.log(抓取失败 - ${err})
} else {
console.log(res);
}
});
}
上述代码中,为了后续的复用性,先定义了一个变量 url_soh,保存请求网址的主域名,这是一个素材资源管理的网站,在这个域名的基础之上,获取“三维动画效果”的素材,因此,它的查询地址是:
使用请求模块对象 request 中的 get 方法,向上述地址发送请求,获取想要的查询数据结果,在请求的过程中,使用 charset 方法设置获取页面的编码格式,并使用 buffer 方法强制缓存响应的内容,最后调用 end 方法结束请求,并返回请求返回的内容,代码执行的格式说明如下:
request.get('请求地址').charset('编码格式').buffer('是否强制缓存').end((err, res) => {})
最后,将自定义的函数 searchImgs 放入 "/get" 请求中,当请求 "http://localhost:3000/get" 地址时,执行函数中的代码,返回查询地址页面中抓取到的内容,并输出在控制台,效果如下图 1-6 所示:
- Cheerio 加载并分析文档
4.1 什么是 Cheerio
Cheerio 又称为解析器,专门用于服务端中 DOM 结构的解析,它的核心是一个精简版的 jQuery 框架,因此,它广泛地应用于各服务端中页面元素的查找和分析,同时,还提供了遍历元素、操作遍历结构的功能 API,常用于 Node 中,加载并分析抓取到的页面文档结构,与其他模块相同,它也必须先下载,后安装,再导入文件中使用。
4.2 Cheerio 特点是什么
Cheerio 有三个非常明显的特点,具体如下:
1. 熟悉的语法:因为它是精简版的 jQuery,因此,它的语法结构与 jQuery 相似。简单代码如下:
var cheerio = require('cheerio'),
$ = cheerio.load('
$('div.title').text('Hello world!');
$('div').addClass('welcome');
$.html();
在上述代码的第 1 行,导入安装好的模块,第 2 行,使用 load 方法构建页面元素,并保存到名称为 $ 的对象变量中,第 3 行,在对象中设置元素显示的文本内容,第 4 行添加元素的类别样式,最后第 5 行,输出重置后的新内容,因此,经过第 3、4 行的操作后,页面中最终显示的元素是:
<div class="title welcome">Hello world!
2. 操作的迅速:Cheerio 是简化版的 jQuery 框架,语法使用非常简单,相同的 DOM 模型,使得它在解析、操作时非常的高效和流畅,有相关的测试也表明,使用 Cheerio 操作 DOM,比使用 JavaScript 要快至少 8 倍。
3. 惊人的灵活:Cheerio 内置强大的文档解析 API,不仅可以完成各种 HTML 文档的解析,而且还能解析各类的 XML 格式的文档,强大的灵活性,也是它深受喜爱的一个原因。
4.3 分析查询结果页文档
在初步理解了 Cheerio 的功能和基本特点之后,接下来,将使用 Superagent 请求获取的页面数据,加载到 Cheerio 容器中,分析 DOM 结构,抓取想要的内容,操作步骤如下所示。
1. 页面效果与代码结构分析
查询结果是一个样式名为 'card-body' 的 div 元素包裹的列表页,在 div 元素中包裹 a 元素和 img 元素,a 元素的 href 属性中包含 id 值,img 元素的 alt 和 data-original 属性分别包含图片的名称和图片的 URL 地址,详细的页面效果和结构如下图 1-7 所示:
2. 抓取想要的主题内容
定义一个名称为 'getImgs' 的函数,根据页面代码结构的分析,调用 Cheerio 模块加载抓取的页面内容,再分析内容的元素结构,代码如下所示:
function getImgs(res) {
let $ = cheerio.load(res.text);
let arrData = [];
$('.card-body').each((idx, ele) => {
// 主体元素
});
}
在上述代码的第 2 行中,先通过变量名为 $ 的对象保存 Cheerio 模块抓取的整个页面内容,获取后,在代码的第 3 行中,定义一个数组对象,用于保存各项抓取到的数据,此外,在代码的第 4 行,选取样式类别名为 'card-body' 的 div 元素,因为它是列表的最外围元素,所以当调用 each 方法遍历该元素集合时,则可以获取页面中每一个外围元素,为下一步的子类元素筛选打下基础。
3. 筛选写入的字段
为了获取到图片的 id、name、imgurl 和 videourl 字段值,需要在遍历的外围元素中,再次查找它的子类 a 元素 和 img 元素,通过取这两个元素的中的 href 和 alt 属性值来获取,代码如下所示:
let **(ele).children("a").attr("href");
let href.indexOf('.', 28);
let id = pos);
let name = $(ele).children("a").children(".card-inner-top-wrap")
.children("img").attr('alt');
let imgurl = $(ele).children("a").children(".card-inner-top-wrap")
.children("img").attr('data-original');
let videurl = url_mp4 + imgurl.substr(22, 43) + ".mp4";
arrData.push({ "id": id, "name": name, "imgurl": imgurl, "videurl": videurl });
console.log(idx,id,name,imgurl,videurl);
在上述代码中,$(ele) 表示遍历时的每一个外围的 div 元素,在这个元素中,通过第 1~3 行代码,找到它的子类 a 元素,并获取到该元素的 href 属性值,同时使用 substring 方法来截取需要的 id 字段的内容;以此类推,根据页面元素的结构,获取到子类 img 元素,并通过元素的属性,分别取到 name 和 imgurl 以及 videurl 字段的值,最终被保存到数组后,在控制台输出,效果如下图 1-8 所示:
需要说明的是:取什么字段的值,源于抓取的需求和数据库的字段结构,确定后,再返回页面中寻找这些数据所在元素的位置,并调用方法获取它。
- Nightmare 模拟浏览器请求
5.1 什么是 Nightmare
Nightmare 是一个自动化测试的框架,它基于 Electron 框架,而 Electron 又是专注于使用 JavaScript 开发针对浏览器的桌面应用,这使 Nightmare 框架在处理基于浏览器的用户数据交互时,更加方便和高效,也正因为这点,在测试时的响应速度上,是基于 PlantomJS 框架的两倍,而且语法更前沿。
5.2 常用功能介绍
虽然 Nightmare 是一个测试的框架,但它在测试时,模拟用户的操作也非常适合爬虫的需求,常用于异步数据的请求,模拟用户操作,结合 Node 开发环境,相同的语法,既简单又高效。
1. 模块安装
与其它在 Node 中使用的模块相同,Nightmare 在使用前也必须先安装,然后,再导入到需要的文件中,在项目文件夹的终端使用如下指令,完成 Nightmare 的安装,指令如下:
$ yarn add nightmare --save
如果成功安装,在项目中的 package.json 文件中,项目运行依赖项最终的内容如下代码所示:
{
"name": "code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "tgrong",
"license": "ISC",
"dependencies": {
"cheerio": "^1.0.0-rc.3",
"express": "^4.17.1",
"mysql": "^2.18.1",
"nightmare": "^3.0.2",
"superagent": "^5.2.2",
"superagent-charset": "^1.2.0"
}
}
在上述代码中,第 15 行就是新安装成功的 Nightmare 模块。
2. 常用 API
安装 Nightmare 模块是使用它的前提,在需求使用的文件中,导入该模块,就可以调用它的 API 了,众多的 API 中,如下几个方法最用常用,代码如下:
var Nightmare = require('nightmare');
var nightmare = Nightmare({
pollInterval: 50,
waitTimeout : 5000
});
nightmare
.goto(url,[headers]) // 要访问的网
.inject(type,url) // 访问时注入的文件类型和名称
.type(selector,[text]) // 向某个 selector 元素输入 text 内容
.click(selector) // 模拟单击某个 selector 元素
.wait(selector) // 等待某个 selector 元素出现,返回 true 结束,否则一直等待
.evaluate(fn) // fn 为回调函数,在函数中可以执行操作的功能代码
.end() // 执行完成,等待数据处理
.then(fn) // 请求和处理完成后,通过 fn 回调函数获取相关数据
.catch(fn) // 如果请求出现异步,通过 fn 回调函数返回异常信息
在上述代码中,第 1 行,先导入 Nightmare 模块以备使用,第 2 行,实例化模块,在此过程中,可以配置相关参数,如 pollInterval 用于设置轮询的时间,默认为 250ms,waitTimeout 用于设置请求超时的时间,默认值为 3000ms,这些值都可以在实例化模块对象是进行重置。
完成实例化对象之后,就可以调用对象的方法,在上述代码中,第 7~15 行都是 Nightmare 模块常用的方法,方法对应的功能,见附加的注释所示。
3. 简单示例
下面通过一个简单的示例来进一步说明 Nightmare 模块对象的使用,需要是:在一个页面中,添加一个 button 和 div 元素,当点击 button 时,div 延时 2 秒显示 "hello,world!" 内容,页面代码如下所示:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
Document
请求
let tip = document.getElementById("tip");
let btn = document.getElementById("btn");
btn.onclick = function(e){
setTimeout(function(){
tip.innerHTML = "hello,world!"
},2000)
}
使用 Nightmare 模块对象访问页面执行时的地址,模拟用户点击按钮的操作,获取 div 元素显示的内容,并将它输出在控制台,操作代码如下所示:
var nightmare = Nightmare({
pollInterval: 50,
waitTimeout : 3000
});
nightmare
.goto('http://127.0.0.1:5500/test.html')
.click("#btn")
.wait(function(){
return document.querySelector("#tip").innerText.length>3;
})
.evaluate(function(){
return document.querySelector("#tip").innerText;
})
.end()
.then(function(res){
console.log(res);
})
.catch(function (error) {
console.error('failed:', error);
});
在上述代码中,wait() 方法内的 return 返回的是一个布尔值,如果为 true 则结束,否则一直等待,直到超时,evaluate() 方法中,return 返回的是 then 方法中的 res 对象值,经过上述代码的执行,2 秒后,将在控制台面板输出 "hello,world!" 字样,表示模拟用户操作成功,并获取到了返回的数据。
5.3 自动搜索任意主题素材
在掌握了 Nightmare 模块的基本用法之后,接下来将它运用到之前的素材抓取中,在上面的小节中,通过在网站中搜索"三维动画",并在结果页中抓取与“三维动画”主题相关的素材,但如果要修改搜索的主题,则又要改变对应的 URL 链接,比较麻烦,如果改用 Nightmare 模块,自动填写任意内容,模拟用法查询,则过程要简化很多,具体来讲,分下面三个步骤,实现自动搜索并获取素材数据的效果。
1. 分析网站搜索的页面结构
在网站任意页中,都有搜索的导航栏,在导航栏的文本框中,输入任意的主题,再点击搜索按钮,即完成了搜索的工作,效果与元素代码如下图 1-9 所示:
从页面结构分析可以知道,先在类别名称为“search-input”的元素中输入需要搜索的主题内容,然后,再点击类别名称为“search-submit”的按钮,则完成了一次主题的搜索。
2. 根据结构执行自动搜索主题
依据页面的结构,调用 Nightmare 模块,在实例化的对象中,请求网站首页,并向类别名为“search-input”的元素中输入任意的搜索内容,再单击类别名称为“search-submit”的按钮,则完成了自动搜索主题内容的功能,创建一个自定义的函数 autoSearch,加入如下代码:
function autoSearch(v) {
var nightmare = Nightmare({
pollInterval: 50,
waitTimeout: 5000
});
nightmare
.goto('vjshi.com/')
.type(".search-input", v)
.click(".search-submit")
.wait(function () {
return document.querySelector("button.next") != null;
})
.evaluate(function () {
return { "text": document.querySelector("body").innerHTML };
})
.end()
.then(function (res) {
console.log(res.text);
})
.catch(function (error) {
console.error('failed:', error);
});
}
需说明的是: 参数 v 表示要查询的关键字内容,而返回的是整个 body 元素的的 HTML 格式内容,因此,当调用该函数时,则在控制台输出获取的全部页面元素内容,例如:如下代码调用函数:
autoSearch('中国人');
执行上述代码后,将在控制台输出查询到主题为"中国人"的素材记录,效果如下图 1-10 所示:
3. 筛选结果页面的数据信息
为了过滤并取到有用的数据,必须在输出 then 方法中,调用之前构建好的 getImgs 函数,则实现整个有用数据的筛选,为下一步写入数据库中作好准备,代码修改如下:
...
.then(function (res) {
//console.log(res.text);
getImgs(res)
})
...
执行上述代码的修改之后,在控制台输出的内容如下图 1-11 所示:
- 嵌套数据请求处理
6.1 功能需求描述
在搜索结果页面中,调用自定义的 getImgs 函数,分析页面的 DOM 结构,获取并保存了需要写入的字段值,为了确保数据写入时的稳定性,必须先将全部需要的数据保存完成后,才能写入数据库中,因此,这是一个嵌套的步骤,第 1 步,获取全部写入的数据,在完成第 1 步之后,再执行第 2 步,执行写入操作。
6.2 promise 对象
1. 什么是 promise 对象
promise 对象是解决异步编程的一种非常不错的方案,在目前的应用中,大部分都使用异步方式进行数据的请求和处理,它的最大核心就是解决了多个异步请求时,嵌套参数传递的问题,同时,该对象的功能和代码比传统的 JavaScript 更高效和简洁,因此,基于这此,使得这种方案深受开发者喜爱 。
2. 简单 promise 对象应用
为了进一步说明它的回调功能强大,接下来通过一个简单的示例来进行演示,比如,要在控制台输出三个字符,"1010, tgrong,18",它分别代码一个用户的编号、昵称和年龄,而这三个字符又是分别通过嵌套回调传参的方式实现的,即先取到编号,才能获取昵称,取到昵称后,才能能得到年龄,实现代码如下所示:
new Promise(function (success) {
setTimeout(function () {
success('1010');
}, 2000)
}).then(v1 => {
return v1 + ',tgrong';
}).then(v2 => {
console.log(v2 + ",18")
})
在上述代码中,每一个次调用 then() 方法时,都可以接收到上一次处理返回的值,从而实现嵌套请求。
6.3 完成代码
在掌握了 promise 对象的基本使用功能之后,接下来使用它来修改之前编写的 getImgs 函数,实现嵌套传值的效果,修改后的代码如下所示:
function getImgs(res) {
new Promise(function (success) {
//... 省略原有代码
success(arrData)
}).then(arrData => {
arrData.forEach(item => {
console.log(item)
});
})
}
在上述代码的第 2 行,先实例化了一个 promise 对象,第 3 行将原有的代码放置在成功回调函数中,第 4 行返回获取的数组对象,并作为下一步操作的传入值,第 5 行调用对象的 then 方法,接收传入的数组对象值,执行下一步操作,第 6~9 行,遍历接收到数组对象,并在控制台输出每个成员,效果如下图 1-12 所示:
- 写入 MySQL 数据库
7.1 数据库和表的构建
为了将数据永久保存,必须创建数据库,通过数据库中的表保存每一条记录,Node 框架可以非常方便地连接许多关系和非关系型的数据库,如 MySQL、MongoDB,接下来以 MySQL 为例,创建一个名称为 getimgs 的数据库,并在该数据库下,添加一个名称为 imginfo 的表,结构如下图 1-13 所示:
7.2 Node 操作表
创建好数据库和表之后,在 Node 中可以导入 MySQL 模块,使用该模块连接数据库,并操作表中的记录,如增加、编辑和删除记录,具体操作如下列步骤。
1. 连接数据库
在 api.js 文件中,为了连接创建好的数据库,加入如下代码:
const mysql = require("mysql");
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "12345678",
database: "getimgs"
})
db.connect(err => {
if (err) throw err;
console.log("数据库连接成功!")
})
在上述代码中,第 1 行导入安装好的 MySQL 模块,第 27 行,创建一个名称为 db 的新连接,用于连接名称为 getimgs 的数据库,第 811 行,执行数据库的连接操作,如果连接成功,则在控制台输出“数据库连接成功!”的字样,否则,抛出异常信息。
2. 操作表内容
接下来,调用 DB 对象的 query 方法,执行相关的 SQL 语句,如:向表 imginfo 增加一条新记录,如果增加成功,则查询记录,并将查询结果输出至控制台,加入如下的代码:
function addRows(data) {
let sql = "insert into imginfo set ?"
db.query(sql, data, (err, result) => {
if (err) {
console.log(err.sqlMessage);
} else {
console.log(result.affectedRows);
}
})
}
function seleRows() {
let sql = "select * from imginfo"
db.query(sql, (err, rows) => {
if (err) {
console.log(err.sqlMessage);
} else {
rows.forEach(item => {
console.log(item);
})
}
})
}
addRows({ "id": 1011,
"name": '张三',
"imgurl": "http://imgurl",
"videurl": "http://videurl"
});
seleRows();
在上述代码的第 1~10 行中,自定义了一个名称为 addRows 的函数,用于向表中增加指定内容的记录,如果增加失败,则输出异常信息,否则,在控制台输出成功增加的记录总数。
在上述代码的第 11~22 行中,自定义了另一个名称为 seleRows 的函数,用于获取表中的记录,如果获取失败,则输出异常信息,否则,在控制台输出获取的每条记录对象。
在上述代码的第 23~27 行中,执行 addRows 函数,增加一条指定内容的记录,第 28 行中,执行 seleRows 函数,在控制台显示获取的内容,执行后,最终在控制台输出的内容如下图 1-14 所示:
7.3 将抓取记录写入数据库
在掌握了数据库的基本操作之后,接下来,将抓取到的记录,增加到表 imginfo 中,再次改造自定义的 getImgs 函数,遍历每条记录,并增加到表中,修改代码如下所示:
function getImgs(res) {
new Promise(function (success) {
//... 省略原有代码
success(arrData)
}).then(arrData => {
arrData.forEach(item => {
addRows(item);
});
seleRows();
})
}
在上述代码中,第 7 行代码,将抓取的每一个数据对象作为自定义函数 addRows 的参数,添加到表中,第 9 行代码中,执行自定义的函数 seleRows,将增加后的数据全部显示在控制台中,输出效果如下图 1-15 所示:
需要说明的是: 现在抓取并保存的记录全是主题为 "中国人"的内容,如果需要修改主题,则只需要在调用自定义的 autoSearch 函数时,传入不同的参数,如:抓取 "美女"的内容,代码执行如下:
autoSearch('美女');
此时,数据库中的 imginfo 表示中,将保存了与 "美女"相关的内容,效果如下图 1-16 所示:
最后说明: 写到这里,我的分享暂告一段落,也衷心希望大家能通过我的分享,能在使用 Node 开发爬虫业时,思路上受到启发,技术上受到拓展,谢谢订阅我分享内容的每一位朋友,分享内容中的代码比较分散,完整的项目代码,大家点击全部源码获取,感谢大家!