時至今日,大前端思想已經深刻人心,不少知識都要涉及到。因此對於如今的前端兒來講也是來着不拒的,練就吸星大法的時候,儘可能多的吸取知識,最後達到物盡其用的效果html
最近,我也是一直在學習關於爬蟲方面的知識,源於以前項目中須要用到的地鐵信息數據並非用爬蟲爬下來的數據,而是直接copy的前端
儘管這些數據一時半會確實不會有太大的變化,不過總以爲仍是有些low的。因而學習了關於爬蟲的知識後,打算和你們一塊兒探討交流一番,下面直接進入正題node
首先,先來講一下爬蟲和Robots協議是什麼mysql
而後再來介紹爬蟲的基本流程git
最後根據實際栗子爬一個豆瓣最近上映的電影來小試牛刀一把github
先看定義:爬蟲,是一種自動獲取網頁內容的程序。是搜索引擎的重要組成部分,所以搜索引擎優化很大程度上就是針對爬蟲而作出的優化。web
再看下Robots協議的介紹,robots.txt是一個文本文件,robots.txt是一個協議不是一個命令sql
robots.txt是爬蟲要查看的第一個文件,robots.txt告訴爬蟲在服務器上什麼文件是能夠被查看的,爬蟲機器人就會按照文件中的內容來肯定訪問範圍數據庫
下圖是豆瓣電影頁面關於robots協議列出來的訪問範圍 express
爬蟲和Robots協議是緊密相連的,圖上看到的不容許爬的頁面就不要去爬,萬一涉及到一些用戶隱私等方面的東西,以後會被發現而走到法律途徑的因此在業內你們也都是承認這個Robots協議的,不讓你爬的頁面就不要爬,還互聯網一片安寧便可了
有點跑偏了,下面再看一張圖,來簡單梳理一下上面說的內容
其實有的人會問,爬蟲到底 爬的是什麼?這是有一個頗有見地的問題,說白了爬蟲拿到的一段是html代碼,因此說這個對於咱們來講並不陌生了,只要咱們把它轉換成DOM樹就能夠了
那麼,如今再看上圖的右半部份,這是一個對比圖
左邊的是沒有限定Robots協議的,按道理來講admin/private和tmp這三個文件夾是不能抓的,可是因爲沒有Robots協議,人家就能夠肆無忌憚的爬
再看右邊的是限定了Robots協議的,與之相反,像Google這樣的搜索引擎也是經過Robots.txt文件去看一下哪些是不能抓的,而後到admin或private這裏的時候就直接跳過,不去抓取了
好了,介紹的內容就說到這裏吧,不來點真刀真槍的東西全都是紙上談兵了
其實對於使用爬蟲來講,流程無外乎這四步
下面就進入激動人心的環節了,你們不要停,跟着我一塊兒手敲出一個爬取豆瓣電影的頁面出來供本身欣賞欣賞
先來看一下總體目錄結構
既然是 抓取數據,咱們就得使用業界較爲出名的 神器------request那麼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文件中,一步到位開始擼了// 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();
})();
複製代碼
完畢,執行一下看看是什麼效果,直接上圖
代碼已經執行完了,接下來再回到Navicat那裏,看看數據到底有沒有寫進去呢,仍是用圖說話吧 至此數據抓取及入庫操做咱們都搞定了,不過彷佛還差點什麼?那就是咱們須要寫個頁面來給展現出來了,因爲抓取和寫入數據都是在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