1. 程式人生 > >Puppeteer 例項程式碼

Puppeteer 例項程式碼

啟動/關閉瀏覽器、開啟頁面

    // 啟動瀏覽器
    const browser = await puppeteer.launch({
        // 關閉無頭模式,方便我們看到這個無頭瀏覽器執行的過程
        // headless: false,
        timeout: 30000, // 預設超時為30秒,設定為0則表示不設定超時
    });

    // 開啟空白頁面
    const page = await browser.newPage();

    // 進行互動
    // ...

    // 關閉瀏覽器
    // await browser.close();

設定頁面視窗大小

    // 設定瀏覽器視窗
    page.setViewport({
        width: 1376,
        height: 768,
    });

輸入網址

    // 位址列輸入網頁地址
    await page.goto('https://google.com/', {
        // 配置項
        // waitUntil: 'networkidle', // 等待網路狀態為空閒的時候才繼續執行
    });

儲存網頁為圖片

await page.screenshot({
    path: 'path/to/saved.png',
});

儲存網頁為 pdf

await page.pdf({
     path: 'path/to/saved.pdf',
    format: 'A4', // 儲存尺寸
});

執行指令碼

要獲取開啟的網頁中的宿主環境,我們可以使用 Page.evaluate 方法:

// 獲取視窗資訊
const dimensions = await page.evaluate(() => {
    return {
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight,
        deviceScaleFactor: window.devicePixelRatio
    };
});
console.log('視窗資訊:', dimensions);

// 獲取 html
// 獲取上下文控制代碼
const htmlHandle = await page.$('html');

// 執行計算
const html = await page.evaluate(body => body.outerHTML, htmlHandle);

// 銷燬控制代碼
await htmlHandle.dispose();

console.log('html:', html);

Page.$ 可以理解為我們常用的 document.querySelector, 而 Page.$$ 則對應 document.querySelectorAll。

自動提交表單

// 位址列輸入網頁地址
await page.goto('https://google.com/', {
    waitUntil: 'networkidle', // 等待網路狀態為空閒的時候才繼續執行
});

// 聚焦搜尋框
// await page.click('#lst-ib');
await page.focus('#lst-ib');

// 輸入搜尋關鍵字
await page.type('辣子雞', {
   delay: 1000, // 控制 keypress 也就是每個字母輸入的間隔
});

// 回車
await page.press('Enter');

抓取單頁應用: 模擬餓了麼外賣下單

關鍵程式碼

const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone6 = devices['iPhone 6'];

console.log('啟動瀏覽器');
const browser = await puppeteer.launch();

console.log('開啟頁面');
const page = await browser.newPage();

// 模擬移動端裝置
await page.emulate(iPhone6);

console.log('位址列輸入網頁地址');
await page.goto(url);

console.log('等待頁面準備好');
await page.waitForSelector('.search-wrapper .search');

console.log('點選搜尋框');
await page.tap('.search-wrapper .search');

await page.type('麥當勞', {
    delay: 200, // 每個字母之間輸入的間隔
});

console.log('回車開始搜尋');
await page.tap('button');

console.log('等待搜素結果渲染出來');
await page.waitForSelector('[class^="index-container"]');

console.log('找到搜尋到的第一家外賣店!');
await page.tap('[class^="index-container"]');


console.log('等待選單渲染出來');
await page.waitForSelector('[class^="fooddetails-food-panel"]');


console.log('直接選一個菜品吧');
await page.tap('[class^="fooddetails-cart-button"]');

// console.log('===為了看清楚,傲嬌地等兩秒===');
await page.waitFor(2000);
await page.tap('[class^=submit-btn-submitbutton]');

// 關閉瀏覽器
await browser.close();

關鍵步驟是:

  • 載入頁面
  • 等待需要點選的 DOM 渲染出來後點擊
  • 繼續等待下一步需要點選的 DOM 渲染出來再點選

關鍵的幾個指令:

  • page.tap(或 page.click) 為點選
  • page.waitForSelector 意思是等待指定元素出現在網頁中,如果已經出現了,則立即繼續執行下去, 後面跟的引數為 selector 選擇器,與我們常用的document.querySelector 接收的引數一致
  • page.waitFor 後面可以傳入 selector 選擇器、function 函式或 timeout 毫秒時間,如 page.waitFor(2000) 指等待2秒再繼續執行,例子中用這個函式暫停操作主要是為了演示

以上幾個指令都可接受一個 selector 選擇器作為引數,這裡額外介紹幾個方法:

  • page.$(selector) 與我們常用的 document.querySelector(selector) 一致,返回的是一個 ElementHandle 元素控制代碼
  • page.$$(selector) 與我們常用的 document.querySelectorAll(selector) 一致,返回的是一個數組

在有頭瀏覽器上下文中,我們選擇一個元素的方法是:

const body = document.querySelector('body');
const bodyInnerHTML = body.innerHTML;
console.log('bodyInnerHTML: ', bodyInnerHTML);

而在無頭瀏覽器裡,我們首先需要獲取一個控制代碼,通過控制代碼獲取到環境中的資訊後,銷燬這個控制代碼。

// 獲取 html
// 獲取上下文控制代碼
const bodyHandle = await page.$('body');
// 執行計算
const bodyInnerHTML = await page.evaluate(dom => dom.innerHTML, bodyHandle);
// 銷燬控制代碼
await bodyHandle.dispose();
console.log('bodyInnerHTML:', bodyInnerHTML);

除此之外,還可以使用 page.$eval:

const bodyInnerHTML = await page.$eval('body', dom => dom.innerHTML);
console.log('bodyInnerHTML: ', bodyInnerHTML);

page.evaluate 意為在瀏覽器環境執行指令碼,可傳入第二個引數作為控制代碼,而 page.$eval 則針對選中的一個 DOM 元素執行操作。

https://github.com/ProtoTeam/blog/blob/master/201710/2.md