【每周小項目】使用 puppeteer 插件爬取動態網站


0. 前言

這兩天對爬蟲開始感興趣,最開始是源於天涯的一個房價神貼,蓋了上萬層,追着讀了好久。天涯網頁端的“只看樓主”需要會員,手機端可以“只看樓主”,但是體驗不太好,記錄也不方便,於是決定把樓主發言單獨爬下來,既可以保存,也可以檢索。

最開始想法很簡單,對每一頁進行元素檢索,發帖人與樓主名字匹配的,就把里面的content拷出來。

首先在網上找到的工具是cheerio插件,它在讀取網站之后,將網站內容存下來,通過元素選擇器進行內容選取。在使用遞歸后,還能解決翻頁問題。

事實上也確實如此,通過簡單幾步操作,就把樓主的發言保存了下來,也讓我對爬蟲產生了興趣。

問題

cheerio確實簡單好用,在應對簡單靜態網頁時沒有問題。但對付具備一定反爬機制的網站就無能為力了。比如cheerio解決翻頁問題,靠的是動態修改url鏈接。但是有的網站,比如我最愛的煎蛋,它的網頁鏈接頁碼是亂碼,就沒辦法實現自動翻頁。再比如有的房產網站,在羅列在售資源時,為了用戶體驗,使用了懶加載,只有將頁面滾動到底部后,才能觸發加載。

以上種種實際上就是cheerio對於網頁操作是無能為力的。

解決

在網上查找對付懶加載的方法時,發現了puppeteer插件。谷歌瀏覽器在17年自行開發了Chrome Headless特性,並與之同時推出了puppeteer,本質上就是一個不含界面的瀏覽器,有點像電腦的終端,所有操作都通過代碼進行操作。

這樣,我們就可以在對網站進行檢索之前,操作指定元素滾動到底部,以觸發更多信息。或者在需要翻頁的時候,操作代碼對翻頁按鈕進行點擊,然后對翻頁后的頁面進行相關處理。

1. 下載與引包

// 下載
npm i puppeteer

// 引包
const puppeteer = require('puppeteer')

2. 使用步驟

// 將整個操作放置在一個閉包的異步函數中,以便於進行異步操作
(async () => {

    // 1. 使用puppetee插件啟動一個瀏覽器,並開啟一個新頁面
    const brower = await puppeteer.launch({
        args: ['--no-sandbox'],
        dumpio: false,
        headless:false, // 默認為true,設為false時,可以顯示可視化瀏覽器界面
    })
    const page = await brower.newPage() // 開啟一個新頁面

    // 2. 打開指定網頁
    await page.goto('http://jandan.net/ooxx', {
        waitUntil: 'networkidle2' // 網絡空閑說明已加載完畢
    });

    // 3. 對動態網站進行自動化操作,這一步是其精髓所在
    
    // 由於我們監控的是動態網頁,剛打開網頁時,所需元素也許還未出現,所以需要進行監聽,例如“下一頁按鈕”
    
    await page.waitForSelector('a.previous-comment-page'); // 括號內是元素選擇器
    
    // 當下一頁按鈕出現時,模擬點擊
    await page.click('a.previous-comment-page')
    

    // 4. 這時我們可以執行爬取我們需要的數據了,我們可以去審查頁面的dom結果,來循環遍歷這些數據。
        // page.evaluate() 為在瀏覽器中執行函數,相當於在控制台中執行函數,返回一個 Promise
    const result = await page.evaluate(() => {
        // 拿到頁面上的jQuery
        var $ = window.$;
        // 在這里進行熟悉的 DOM 操作
        // Do something
    });
    
    // 5. 關閉瀏覽器,在console里面打印我們需要的數據
    brower.close();

    // 6. 對結果進行處理
    console.log(result);
})();

3. 爬過的幾個坑

page.evaluate 的傳參問題

因為打開的這個 page 只是一個木偶,並不是真正的瀏覽器頁面,所以在這個頁面上的操作與一般頁面上的操作有差異。

官方文檔里說,這個參數是這樣的。在實際使用中,可以傳一個字符串變量,但是到更復雜一點的,比如‘fs’,自定義外部函數時,都無法讀取。

這也是我建議在第6步,對頁面操作完成后,統一對結果進行處理。(主要是因為我沒有解決這個問題,所以認慫繞開走了……)

元素操作問題

puppeteer中,最重要的函數執行和要素選擇都與一般瀏覽器上操作有些區別,這里有些坑要爬,現在我也說不清楚。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM