NodeJS爬蟲入門

1. 寫在前面

往常都是利用 Python/.NET 語言實現爬蟲,然如今做爲一名前端開發人員,天然須要熟練 NodeJS。下面利用 NodeJS 語言實現一個糗事百科的爬蟲。另外,本文使用的部分代碼是 es6 語法。javascript

實現該爬蟲所須要的依賴庫以下。html

  1. request: 利用 get 或者 post 等方法獲取網頁的源碼。
  2. cheerio: 對網頁源碼進行解析,獲取所需數據。

本文首先對爬蟲所需依賴庫及其使用進行介紹,而後利用這些依賴庫,實現一個針對糗事百科的網絡爬蟲。前端

2. request 庫

request 是一個輕量級的 http 庫,功能十分強大且使用簡單。可使用它實現 Http 的請求,而且支持 HTTP 認證, 自定請求頭等。下面對 request 庫中一部分功能進行介紹。vue

安裝 request 模塊以下:java

npm install request

在安裝好 request 後,便可進行使用,下面利用 request 請求一下百度的網頁。node

const req = require('request');

req('http://www.baidu.com', (error, response, body) => {
  if (!error && response.statusCode == 200) {
    console.log(body)
  }
})

在沒有設置 options 參數時,request 方法默認是 get 請求。而我喜歡利用 request 對象的具體方法,使用以下:git

req.get({
  url: 'http://www.baidu.com'
},(err, res, body) => {
  if (!err && res.statusCode == 200) {
    console.log(body)
  }
});

然而不少時候,直接去請求一個網址所獲取的 html 源碼,每每得不到咱們須要的信息。通常狀況下,須要考慮到請求頭和網頁編碼。es6

  1. 網頁的請求頭
  2. 網頁的編碼

下面介紹在請求的時候如何添加網頁請求頭以及設置正確的編碼。github

req.get({
    url : url,
    headers: {
        "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
        "Host" : "www.zhihu.com",
        "Upgrade-Insecure-Requests" : "1"
    },
    encoding : 'utf-8'
}, (err, res, body)=>{
    if(!err)
       console.log(body);
})

設置 options 參數, 添加 headers 屬性便可實現請求頭的設置;添加 encoding 屬性便可設置網頁的編碼。須要注意的是,若 encoding:null ,那麼 get 請求所獲取的內容則是一個 Buffer 對象,即 body 是一個 Buffer 對象。npm

上面介紹的功能足矣知足後面的所需了,更多功能請參看官網的文檔 request

3. cheerio 庫

cheerio 是一款服務器端的 Jquery,以輕、快、簡單易學等特色被開發者喜好。有 Jquery 的基礎後再來學習 cheerio 庫很是輕鬆。它可以快速定位到網頁中的元素,其規則和 Jquery 定位元素的方法是同樣的;它也能以一種很是方便的形式修改 html 中的元素內容,以及獲取它們的數據。下面主要針對 cheerio 快速定位網頁中的元素,以及獲取它們的內容進行介紹。

首先安裝 cheerio 庫

npm install cheerio

下面先給出一段代碼,再對代碼進行解釋 cheerio 庫的用法。對博客園首頁進行分析,而後提取每一頁中文章的標題。

首先對博客園首頁進行分析。以下圖:

對 html 源代碼進行分析後,首先經過 .post_item 獲取全部標題,接着對每個 .post_item 進行分析,使用 a.titlelnk 便可匹配每一個標題的 a 標籤。下面經過代碼進行實現。

const req = require('request');
const cheerio = require('cheerio');

req.get({
    url: 'https://www.cnblogs.com/'
  }, (err, res, body) => {
    if (!err && res.statusCode == 200) {
      let cnblogHtmlStr = body;
      let $ = cheerio.load(cnblogHtmlStr);
      $('.post_item').each((index, ele) => {
        let title = $(ele).find('a.titlelnk');
        let titleText = title.text();
        let titletUrl = title.attr('href');
        console.log(titleText, titletUrl);
      });
    }
  });

固然,cheerio 庫也支持鏈式調用,上面的代碼也可改寫成:

let cnblogHtmlStr = body;
let $ = cheerio.load(cnblogHtmlStr);
let titles = $('.post_item').find('a.titlelnk');
titles.each((index, ele) => {
    let titleText = $(ele).text();
    let titletUrl = $(ele).attr('href');
    console.log(titleText, titletUrl);

上面的代碼很是簡單,就再也不用文字進行贅述了。下面總結一點本身認爲比較重要的幾點。

  1. 使用 find() 方法獲取的節點集合 A,若再次以 A 集合中的元素爲根節點定位它的子節點以及獲取子元素的內容與屬性,需對 A 集合中的子元素進行 $(A[i]) 包裝,如上面的$(ele) 同樣。
  2. 在上面代碼中使用 $(ele) ,其實還可使用 $(this) 可是因爲我使用的是 es6 的箭頭函數,所以改變了 each 方法中回調函數的 this 指針,所以,我使用 $(ele);
  3. cheerio 庫也支持鏈式調用,如上面的 $('.post_item').find('a.titlelnk') ,須要注意的是,cheerio 對象 A 調用方法 find(),若是 A 是一個集合,那麼 A 集合中的每個子元素都調用 find() 方法,並放回一個結果結合。若是 A 調用 text() ,那麼 A 集合中的每個子元素都調用 text() 並返回一個字符串,該字符串是全部子元素內容的合併(直接合並,沒有分隔符)。

最後在總結一些我比較經常使用的方法。

  1. first()
  2. last()
  3. children([selector]): 該方法和 find 相似,只不過該方法只搜索子節點,而 find 搜索整個後代節點。

關於更多 cheerio 庫的用法,請參考文檔 cheerio

4. 糗事百科爬蟲

經過上面對 requestcheerio 類庫的介紹,下面利用這兩個類庫對糗事百科的頁面進行爬取。

一、在項目目錄中,新建 httpHelper.js 文件,經過 url 獲取糗事百科的網頁源碼,代碼以下:

//爬蟲
const req = require('request');

function getHtml(url){
    return new Promise((resolve, reject) => {
        req.get({
            url : url,
            headers: {
                "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
                "Referer" : "https://www.qiushibaike.com/"
            },
            encoding : 'utf-8'
        }, (err, res, body)=>{
            if(err) reject(err);
            else resolve(body);
        })
    });
}
exports.getHtml = getHtml;

二、在項目目錄中,新建一個 Splider.js 文件,分析糗事百科的網頁代碼,提取本身須要的信息,而且創建一個邏輯經過更改 url 的 id 來爬取不一樣頁面的數據。

const cheerio = require('cheerio');
const httpHelper = require('./httpHelper');

function getQBJok(htmlStr){
    let $ = cheerio.load(htmlStr);
    let jokList = $('#content-left').children('div');
    let rst = [];
    jokList.each((i, item)=>{
        let node = $(item);
        let titleNode = node.find('h2');
        let title = titleNode ? titleNode.text().trim() : '匿名用戶';
        let content = node.find('.content span').text().trim();
        let likeNumber = node.find('i[class=number]').text().trim();
        rst.push({
            title : title,
            content : content,
            likeNumber : likeNumber
        });
    });
    return rst;
}

async function splider(index = 1){
    let url = `https://www.qiushibaike.com/8hr/page/${index}/`;
    let htmlStr = await httpHelper.getHtml(url);
    let rst = getQBJok(htmlStr);
    return rst;
}

splider(1);

在獲取糗事百科網頁信息的時候,首先在瀏覽器中對源碼進行分析,定位到本身所須要標籤,而後提取標籤的文本或者屬性值,這樣就完成了網頁的解析。

Splider.js 文件入口是 splider 方法,首先根據傳入該方法的 index 索引,構造糗事百科的 url,接着獲取該 url 的網頁源碼,最後將獲取的源碼傳入 getQBJok 方法,進行解析,本文只解析每條文本笑話的做者、內容以及喜歡個數。

直接運行 Splider.js 文件,便可爬取第一頁的笑話信息。而後能夠更改 splider 方法的參數,實現抓取不一樣頁面的信息。

在上面已有代碼的基礎上,使用 koavue2.0 搭建一個瀏覽文本的頁面,效果以下:

源碼已上傳到 github 上。下載地址:https://github.com/StartAction/SpliderQB ;

項目運行依賴 node v7.6.0 以上, 首先從 Github 上面克隆整個項目。

git clone https://github.com/StartAction/SpliderQB.git

克隆以後,進入項目目錄,運行下面命令便可。

node app.js

5. 總結

經過實現一個完整的爬蟲功能,加深本身對 Node 的理解,且實現的部分語言都是使用 es6 的語法,讓本身加快對 es6 語法的學習進度。另外,在此次實現中,遇到了 Node 的異步控制的知識,本文是採用的是 asyncawait 關鍵字,也是我最喜歡的一種,然而在 Node 中,實現異步控制有好幾種方式。關於具體的方式以及原理,有時間再進行總結。

相關文章
相關標籤/搜索