爬蟲,其實本就是這麼簡單

吸收我無,分享我有

時至今日,大前端思想已經深刻人心,不少知識都要涉及到。因此對於如今的前端兒來講也是來着不拒的,練就吸星大法的時候,儘可能多的吸取知識,最後達到物盡其用的效果html

最近,我也是一直在學習關於爬蟲方面的知識,源於以前項目中須要用到的地鐵信息數據並非用爬蟲爬下來的數據,而是直接copy的前端

儘管這些數據一時半會確實不會有太大的變化,不過總以爲仍是有些low的。因而學習了關於爬蟲的知識後,打算和你們一塊兒探討交流一番,下面直接進入正題node

首先,先來講一下爬蟲和Robots協議是什麼mysql

而後再來介紹爬蟲的基本流程git

最後根據實際栗子爬一個豆瓣最近上映的電影來小試牛刀一把github

爬蟲及Robots協議

先看定義:爬蟲,是一種自動獲取網頁內容的程序。是搜索引擎的重要組成部分,所以搜索引擎優化很大程度上就是針對爬蟲而作出的優化。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這裏的時候就直接跳過,不去抓取了

好了,介紹的內容就說到這裏吧,不來點真刀真槍的東西全都是紙上談兵了

爬蟲的基本流程

其實對於使用爬蟲來講,流程無外乎這四步

  1. 抓取數據
  2. 數據入庫
  3. 啓動服務
  4. 渲染數據

抓取數據

下面就進入激動人心的環節了,你們不要停,跟着我一塊兒手敲出一個爬取豆瓣電影的頁面出來供本身欣賞欣賞

先來看一下總體目錄結構

既然是 抓取數據,咱們就得使用業界較爲出名的 神器------request

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;
複製代碼

代碼寫完了,回味一下都作了什麼事情吧

  • 經過request抓取了html代碼
  • cheerio將html轉成了dom
  • 將須要的內容存在數組(名稱|評分|地址|圖片|id)
  • 返回結果數組並導出read方法

數據入庫

這裏咱們經過mysql來創建數據庫存儲數據,不太瞭解的也沒有關係,先跟我一步一步作下去。咱們先安裝XAMPPNavicat可視化數據庫管理工具,安裝完畢後按照我下面的來操做便可

XAMPP啓動mysql

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裏到底寫了哪些?

  • 引入query方法用來書寫SQL語句
  • 遍歷讀取到的結果數組
  • 查詢是否有數據存過
    • 有: 更新數據
    • 沒有: 插入數據

好了,上面也實現了寫入數據庫的方法,接下來趁熱打鐵,稍微講一下SQL語句吧

SQL語句學習

?表示佔位符 這裏順便簡單的說一下SQL語句裏會用到的語法,無處不在的增刪改查

  1. 插入數據
語法: 
    INSERT INTO 表名(列名) VALUES(列名值)
栗子:
    INSERT INTO tags(name,id,url) VALUES('爬蟲',10,'https://news.so.com/hotnews')
解釋:
    向標籤表(tags)裏插入一條,姓名,id和訪問地址分別爲VALUES內對應的值
複製代碼
  1. 更新數據
語法:
    UPDATE 表名 SET 列名=更新值 WHERE 更新條件
栗子:
    UPDATE articles SET title='你好,世界',content='世界沒你想的那麼糟!' WHERE id=1
解釋:
    更新id爲1的文章,標題和內容都進行了修改
複製代碼
  1. 刪除數據
語法:
    DELETE FROM 表名 WHERE 刪除條件
栗子:
    DELETE FROM tags WHERE id=11
解釋:
    從標籤表(tags)裏刪除id爲11的數據
複製代碼
  1. 查詢
語法:
    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服務

// 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

相關文章
相關標籤/搜索