时至今日,大前端思想已经深刻人心,不少知识都要涉及到。因此对于如今的前端儿来讲也是来着不拒的,练就吸星大法的时候,尽可能多的吸取知识,最后达到物尽其用的效果html
最近,我也是一直在学习关于爬虫方面的知识,源于以前项目中须要用到的地铁信息数据并非用爬虫爬下来的数据,而是直接copy的前端
尽管这些数据一时半会确实不会有太大的变化,不过总以为仍是有些low的。因而学习了关于爬虫的知识后,打算和你们一块儿探讨交流一番,下面直接进入正题node
首先,先来讲一下爬虫和Robots协议是什么mysql
而后再来介绍爬虫的基本流程git
最后根据实际栗子爬一个豆瓣最近上映的电影来小试牛刀一把github
先看定义:爬虫,是一种自动获取网页内容的程序。是搜索引擎的重要组成部分,所以搜索引擎优化很大程度上就是针对爬虫而作出的优化。web
再看下Robots协议的介绍,robots.txt是一个文本文件,robots.txt是一个协议不是一个命令sql
robots.txt是爬虫要查看的第一个文件,robots.txt告诉爬虫在服务器上什么文件是能够被查看的,爬虫机器人就会按照文件中的内容来肯定访问范围数据库
下图是豆瓣电影页面关于robots协议列出来的访问范围 express
因此在业内你们也都是承认这个Robots协议的,不让你爬的页面就不要爬,还互联网一片安宁便可了
有点跑偏了,下面再看一张图,来简单梳理一下上面说的内容
这是有一个颇有见地的问题,说白了爬虫拿到的一段是html代码,因此说这个对于咱们来讲并不陌生了,只要咱们把它转换成DOM树就能够了
那么,如今再看上图的右半部份,这是一个对比图
左边的是没有限定Robots协议的,按道理来讲admin/private和tmp这三个文件夹是不能抓的,可是因为没有Robots协议,人家就能够肆无忌惮的爬
再看右边的是限定了Robots协议的,与之相反,像Google这样的搜索引擎也是经过Robots.txt文件去看一下哪些是不能抓的,而后到admin或private这里的时候就直接跳过,不去抓取了
好了,介绍的内容就说到这里吧,不来点真刀真枪的东西全都是纸上谈兵了
其实对于使用爬虫来讲,流程无外乎这四步
下面就进入激动人心的环节了,你们不要停,跟着我一块儿手敲出一个爬取豆瓣电影的页面出来供本身欣赏欣赏
先来看一下总体目录结构
那么request到底如何用之,且听风吟一块儿看代码
// 使用起来超简单
let request = require('request');
request('http://www.baidu.com', function (error, response, body) {
console.log('error:', error); // 当有错误发生时打印错误日志
console.log('statusCode:', response && response.statusCode); // 打印响应状态码
console.log('body:', body); // 打印百度页面的html代码
});
复制代码
看完上面的代码,难道你还以为不明显嘛。朋友,html代码已经出如今眼前了,那么就别矜持了,只要转成熟悉的DOM就能够随心所欲了
因而乎,cheerio登场了,你们都称它是Node版的jq。你就彻底按照jq的习惯来操做DOM就能够了
下面也再也不绕弯子了,赶忙一块儿写爬虫吧!
首页要先根据豆瓣电影的页面来分析一下,哪些是正在热映的电影,先来看看DOM结构
// read.js文件
// request-promise是让request支持了promise的语法,能够说是request的小弟
const rp = require('request-promise');
// 将抓取页面的html代码转为DOM,能够称之为是node版的jq
const cheerio = require('cheerio');
// 这个是为了在调试时查看日志
const debug = require('debug')('movie:read');
// 读取页面的方法,重点来了
const read = async (url) => {
debug('开始读取最近上映的电影');
const opts = {
url, // 目标页面
transform: body => {
// body为目标页面抓取到的html代码
// 经过cheerio.load方法能够把html代码转换成能够操做的DOM结构
return cheerio.load(body);
}
};
return rp(opts).then($ => {
let result = []; // 结果数组
// 遍历这些热映电影的li
$('#screening li.ui-slide-item').each((index, item) => {
let ele = $(item);
let name = ele.data('title');
let score = ele.data('rate') || '暂无评分';
let href = ele.find('.poster a').attr('href');
let image = ele.find('img').attr('src');
// 影片id能够从影片href中获取到
let id = href && href.match(/(\d+)/)[1];
// 为了防止豆瓣防盗链致使裂图,换成webp格式加载图片
image = image && image.replace(/jpg$/, 'webp');
if (!name || !image || !href) {
return;
}
result.push({
name,
score,
href,
image,
id
});
debug(`正在读取电影:${name}`);
});
// 返回结果数组
return result;
});
};
// 导出方法
module.exports = read;
复制代码
代码写完了,回味一下都作了什么事情吧
这里咱们经过mysql来创建数据库存储数据,不太了解的也没有关系,先跟我一步一步作下去。咱们先安装XAMPP和Navicat可视化数据库管理工具,安装完毕后按照我下面的来操做便可
只言片语可能都不及有图有真相的实际,这块就先看看图吧
首先,咱们须要在src目录下建立一个sql文件,这里要和刚才建立的数据库同名,就叫它my_movie.sql了(固然目录结构已经建立过了)
而后,再回到db.js文件里,写入链接数据库的代码
// db.js
const mysql = require('mysql');
const bluebird = require('bluebird');
// 建立链接
const connection = mysql.createConnection({
host: 'localhost', // host
port: 3306, // 端口号默认3306
database: 'my_movie', // 对应的数据库
user: 'root',
password: ''
});
connection.connect(); // 链接数据库
// bluebird是为了方便支持promise语法化
// 而后直接把数据库的query查询语句导出方便以后调用
module.exports = bluebird.promisify(connection.query).bind(connection);
复制代码
上面代码就已经建立了链接Mysql数据库的操做了,接下来,不放缓脚步,直接把内容写进数据库吧
这时咱们来看一下write.js这个文件,没错顾名思义就是用来写入数据库的,直接上代码
// write.js文件
// 从db.js那里导入query方法
const query = require('./db');
const debug = require('debug')('movie:write');
// 写入数据库的方法
const write = async (movies) => {
debug('开始写入电影');
// movies即为read.js读取出来的结果数组
for (let movie of movies) {
// 经过query方法去查询一下是否是已经在数据库里存过了
let oldMovie = await query('SELECT * FROM movies WHERE id=? LIMIT 1', [movie.id]);
// sql查询语句返回的是一个数组,若是不为空数组的话就表示有数据存过
// 直接就进行更新操做了
if (Array.isArray(oldMovie) && oldMovie.length) {
// 更新movies表里的数据
let old = oldMovie[0];
await query('UPDATE movies SET name=?,href=?,image=?,score=? WHERE id=?', [movie.name, movie.href, movie.image, movie.score, old.id]);
} else {
// 插入内容到movies表
await query('INSERT INTO movies(id,name,href,image,score) VALUES(?,?,?,?,?)', [movie.id, movie.name, movie.href, movie.image, movie.score]);
}
debug(`正在写入电影:${movie.name}`);
}
};
module.exports = write;
复制代码
上面写完可能会有点蒙圈,毕竟纯前端仍是不多去写SQL语句的。不过不要方,待我先把上面的代码梳理以后再简单介绍一下SQL语句部分啊
write.js里到底写了哪些?
好了,上面也实现了写入数据库的方法,接下来趁热打铁,稍微讲一下SQL语句吧
?表示占位符 这里顺便简单的说一下SQL语句里会用到的语法,无处不在的增删改查
语法:
INSERT INTO 表名(列名) VALUES(列名值)
栗子:
INSERT INTO tags(name,id,url) VALUES('爬虫',10,'https://news.so.com/hotnews')
解释:
向标签表(tags)里插入一条,姓名,id和访问地址分别为VALUES内对应的值
复制代码
语法:
UPDATE 表名 SET 列名=更新值 WHERE 更新条件
栗子:
UPDATE articles SET title='你好,世界',content='世界没你想的那么糟!' WHERE id=1
解释:
更新id为1的文章,标题和内容都进行了修改
复制代码
语法:
DELETE FROM 表名 WHERE 删除条件
栗子:
DELETE FROM tags WHERE id=11
解释:
从标签表(tags)里删除id为11的数据
复制代码
语法:
SELECT 列名 FROM 表名 WHERE 查询条件 ORDER BY 排序列名
栗子:
SELECT name,title,content FROM tags WHERE id=8
解释:
查询id为8的标签表里对应信息
复制代码
到这里已经把读写的方法全写完了,想必你们看的也有些疲惫了。也是时候该检验一下成果了,否则都是在扯淡的状态
如今就来到index.js中,开始检验一番吧
// index.js文件
const read = require('./read');
const write = require('./write');
const url = 'https://movie.douban.com'; // 目标页面
(async () => {
// 异步抓取目标页面
const movies = await read(url);
// 写入数据到数据库
await write(movies);
// 完毕后退出程序
process.exit();
})();
复制代码
完毕,执行一下看看是什么效果,直接上图
那就是咱们须要写个页面来给展现出来了,因为抓取和写入数据都是在node环境下才容许。因此咱们还要建立一个web服务用来展现页面的,坚持一下立刻就OK了,加油
因为要建立web服务了,因此开始写server.js的内容吧
// server.js文件
const express = require('express');
const path = require('path');
const query = require('../src/db');
const app = express();
// 设置模板引擎
app.set('view engine', 'html');
app.set('views', path.join(__dirname, 'views'));
app.engine('html', require('ejs').__express);
// 首页路由
app.get('/', async (req, res) => {
// 经过SQL查询语句拿到库里的movies表数据
const movies = await query('SELECT * FROM movies');
// 渲染首页模板并把movies数据传过去
res.render('index', { movies });
});
// 监听localhost:9000端口
app.listen(9000);
复制代码
写完了server服务了,最后再到index.html模板里看看吧,这但是最后的东西了,写完就所有大功告成了
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>热映的电影</title>
</head>
<body>
<div class="container">
<h2 class="caption">正在热映的电影</h2>
<ul class="list">
<% for(let i=0;i<movies.length;i++){
let movie = movies[i];
%>
<li>
<a href="<%=movie.href%>" target="_blank">
<img src="<%=movie.image%>" />
<p class="title"><%=movie.name%></p>
<p class="score">评分:<%=movie.score%></p>
</a>
</li>
<% } %>
</ul>
</div>
</body>
</html>
复制代码
经过模板引擎遍历movies数组,而后进行渲染就能够了
Now,看下最终的效果
这里顺便发一下代码,以方便你们参考敲敲敲
感谢你们的观看,886