1. 程式人生 > 實用技巧 >關於Puppeteer的那些事兒

關於Puppeteer的那些事兒

原文:https://www.cnblogs.com/zlforever-young/p/11594211.html

最近開始上手一個自動化測試工具Puppeteer,來談一談關於它的一些事兒。

Puppeteer中文文件:https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer&version=v1.20.0&show=api-class-puppeteer(推薦看完預熱視訊後再讀API)

Puppeteer官方文件:https://github.com/GoogleChrome/puppeteer

預熱視訊:https://www.youtube.com/watch?v=IvaJ5n5xFqU

以及 https://www.youtube.com/watch?v=ARt3zDHSsd4

線上編寫測試網站:https://try-puppeteer.appspot.com/

puppeteer社群:https://slack.com/

√安裝

安裝puppeteer

cnpm i puppeteer

安裝puppeteer-core

cnpm i puppeteer-core

√常用方法或屬性:

1.puppeteer.launch([options])

//Puppeteer 模組提供了一種啟動 Chromium 例項的方法。 下面就是使用 Puppeteer 進行自動化的一個典型示例:
const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.goto('https://www.google.com');
  // 其他操作...
  await browser.close();
});

2.Browser

當 Puppeteer 連線到一個 Chromium 例項的時候會通過 puppeteer.launch 或 puppeteer.connect 建立一個 Browser 物件。

//下面是使用 Browser 建立 Page 的例子
const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  // 儲存節點以便能重新連線到 Chromium
  const browserWSEndpoint = browser.wsEndpoint();
  // 從 Chromium 斷開和 puppeteer 的連線
  browser.disconnect();

  // 使用節點來重新建立連線
  const browser2 = await puppeteer.connect({browserWSEndpoint});
  // 關閉 Chromium
  await browser2.close();
});
//一個斷開連線和重連到 Browser 的例子:
const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  // 儲存節點以便能重新連線到 Chromium
  const browserWSEndpoint = browser.wsEndpoint();
  // 從 Chromium 斷開和 puppeteer 的連線
  browser.disconnect();

  // 使用節點來重新建立連線
  const browser2 = await puppeteer.connect({browserWSEndpoint});
  // 關閉 Chromium
  await browser2.close();
});

3.Page

Page 提供操作一個 tab 頁或者 extension background page 的方法。一個 Browser 例項可以有多個 Page 例項。

下面的例子建立一個 Page 例項,導航到一個 url ,然後儲存截圖:

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
});

4.Keyboard

Keyboard 提供一個介面來管理虛擬鍵盤. 高階介面為 keyboard.type, 其接收原始字元, 然後在你的頁面上生成對應的 keydown, keypress/input, 和 keyup 事件.

為了更精細的控制(虛擬鍵盤), 你可以使用 keyboard.down, keyboard.up 和 keyboard.sendCharacter 來手動觸發事件, 就好像這些事件是由真實的鍵盤生成的.

//持續按下 Shift 來選擇一些字串並且刪除的例子:

await page.keyboard.type('Hello World!'); await page.keyboard.press('ArrowLeft'); await page.keyboard.down('Shift'); for (let i = 0; i < ' World'.length; i++) await page.keyboard.press('ArrowLeft'); await page.keyboard.up('Shift'); await page.keyboard.press('Backspace'); // 結果字串最終為 'Hello!'
//按下A的例子
await page.keyboard.down('Shift');
await page.keyboard.press('KeyA');
await page.keyboard.up('Shift');

5.Mouse

Mouse 類在相對於視口左上角的主框架 CSS 畫素中執行。

每個 page 物件都有它自己的 Mouse 物件,使用見 page.mouse。

// 使用 ‘page.mouse’ 追蹤 100x100 的矩形。
await page.mouse.move(0, 0);
await page.mouse.down();
await page.mouse.move(0, 100);
await page.mouse.move(100, 100);
await page.mouse.move(100, 0);
await page.mouse.move(0, 0);
await page.mouse.up();

更多方法和屬性請閱讀官網

√實戰01:跳轉指定頁面

原始碼:跳轉到百度首頁

const puppeteer=require('puppeteer');

(async ()=>{

    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page=  await browser.newPage();
    await page.goto("https://www.baidu.com");

})();

效果:

√實戰02:輸入文字與元素點選

程式碼:在百度中搜索

const puppeteer=require('puppeteer');

(async ()=>{

    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page=  await browser.newPage();
    await page.goto("https://www.baidu.com");  //跳轉頁面
    const input_text= await page.$("#kw");     //獲取百度首頁的搜尋框。page.$()用來查詢元素
    await input_text.type("Hello Word!");      //type()輸入內容
    const btn_click=await page.$("#su");       //獲取百度首頁的搜尋按鈕。
    await btn_click.click();                   //點選搜尋按鈕。

})();

效果:

√實戰03:獲取文字元素值

原始碼:獲取百度詞條的值

const puppeteer=require('puppeteer');

(async ()=>{

    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page=  await browser.newPage();
    await page.goto("https://www.baidu.com");  //跳轉頁面
    const input_text= await page.$("#kw");     //獲取百度首頁的搜尋框。page.$()用來查詢元素
    await input_text.type("Hello Word!");      //type()輸入內容
    const btn_click=await page.$("#su");       //獲取百度首頁的搜尋按鈕。
    await btn_click.click();                  //點選搜尋按鈕。

    await page.waitFor('div#content_left > .result-op.c-container.xpath-log',{visible:true});//由於獲取元素是非同步操作,需要等待該元素加載出來

    let resultText= await page.$eval('div#content_left > .result-op.c-container.xpath-log',ele=>{return ele.innerHTML})//獲取元素並返回元素下的innerHTML。 .$eval表示獲取單個元素
    console.log("result is ",resultText);//在控制檯打印出值。

})();

演示:

√實戰04:puppeteer檔案上傳操作

程式碼:在百度首頁上傳圖片

const puppeteer=require('puppeteer');

(async ()=>{
    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page = await browser.newPage();
    await page.goto('https://www.baidu.com');

    await page.waitForSelector('span.soutu-btn');  //等待選擇元素出現
    const soutuBtn=await page.$('span.soutu-btn'); //獲取圖片按鈕
    await soutuBtn.click();                        //點選展開圖片按鈕

    await page.waitForSelector('input.upload-pic');   //等待上傳按鈕出現
    const uploadPic=await page.$('input.upload-pic'); //獲取上傳按鈕
    await uploadPic.uploadFile('C:\\Capture.PNG');    //上傳圖片。注意:路徑要用雙斜槓。
})();

演示:

√實戰05:puppeteer處理多個元素

程式碼:列印京東上的手機資訊

const puppeteer = require('puppeteer');

(async()=>{
    const browser=await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page =await browser.newPage();
    await page.goto('https://www.jd.com');

    await page.waitFor("#key");
    const inputText=await page.$("#key");
    await inputText.type("手機");
    await page.keyboard.press('Enter');

    await page.waitForSelector("ul.gl-warp > li");
    const items=await page.$$eval("ul.gl-warp > li",eles=>eles.map(ele=>ele.innerText));
    console.log("手機列表=",items);
})();

演示:

√實戰06:puppeteer切換iframe進行操作

iframe跟page的用法類似。使用前需要切換到iframe裡面並用src定位要操作的iframe。操作可參考:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-frame

程式碼:自動在阿里雲的註冊iframe裡面填寫註冊資訊

const puppeteer = require('puppeteer');

(async()=>{
    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800}});
    const page = await browser.newPage();
    await page.goto("https://account.aliyun.com/register/register.html");

    //切換iframe
    const frame= await page.frames().find(frame=>frame.url().includes("https://passport.aliyun.com"));//切換iframe,並找到對應src的iframe
    await frame.waitFor("input#nick"); //等待輸入框載入完成
    const nick =await frame.$("input#nick");//獲取輸入框
    await nick.type("測試使用者");//輸入資料
    
})();

演示:

√實戰07:puppeteer拖拽操作阿里雲驗證碼

程式碼:自動操作阿里雲驗證滑動模組

const puppeteer = require('puppeteer');

(async()=>{
    const browser = await puppeteer.launch({headless:false,defaultViewport:{width:1000,height:800},ignoreDefaultArgs:["--enable-automation"]});//在有些頁面可能需要將automation提示去掉。使用ignoreDefaultArgs:["--enable-automation"]引數
    const page = await browser.newPage();
    await page.goto("https://account.aliyun.com/register/register.html");

    //切換iframe
    const frame= await page.frames().find(frame=>frame.url().includes("https://passport.aliyun.com"));//切換iframe,並找到對應src的iframe
    await frame.waitForSelector("span#nc_1_n1z");//等待滑動塊載入完成
    const span =await frame.$("span#nc_1_n1z");  //獲取滑動元素
    const spanInfo=await span.boundingBox();     //獲取滑動塊的資訊,包括位置(x,y)以及寬和高
    //console.log(spanInfo);

    await frame.waitForSelector("div#nc_1_n1t"); //等待包裹滑塊的div載入完成
    const outDiv=await frame.$("div#nc_1_n1t");  //獲取包裹滑塊的div
    const outDivInfo=await outDiv.boundingBox(); //獲取包裹滑塊的div的資訊,包括位置(x,y)以及寬和高
    //console.log(outDivInfo);

    await page.mouse.move(spanInfo.x,spanInfo.y);//將滑鼠移動到滑塊位置
    await page.mouse.down();                     //將滑鼠按下(預設是左鍵)
    for(var i=0;i<outDivInfo.width;i++){
        page.mouse.move(spanInfo.x+i,spanInfo.y);//讓滑鼠向左移動outDivInfo的寬度距離
    }
    await page.mouse.up();                       //將滑鼠鬆開
 
})();

演示:

√實戰08:puppeteer自動抓取百度新聞上的語句並自動登入微博賬戶發一條微博

程式碼:這個實戰似乎翻車了,被微博檢測到自動化而被要求輸入驗證碼

const puppeteer = require('puppeteer');
const config=require('./config'); //為了保護我的個人隱私,所以我把賬號和密碼儲存在了config檔案中

(async ()=>{
    // console.log(config.username);
    // console.log(config.password);
    const browser = await puppeteer.launch({
        headless:false,
        defaultViewport:{width:1280,height:800},
        ignoreDefaultArgs:["--enable-automation"],//移除自動化,防止頁面生成驗證碼
        slowMo:200,//輸入延遲時間
        args:['--window-size:1280,800'],//調整視窗大小
        
    });
    const page= await browser.newPage();
    await page.goto('https://news.baidu.com/',{waitUntil:"networkidle2"});//第一個引數是要前往的地址url,第二個引數是保證頁面全部載入

    await page.waitForSelector('#imgTitle>a>strong');
    const newsText=await page.$eval("#imgTitle>a>strong",ele=>ele.innerText);//匹配第一個元素
    // console.log(newsText);

    await page.goto('https://weibo.com',{waitUntil:"networkidle2"});
    await page.waitFor(5*1000);//防止被檢測
    await page.reload();//防止被檢測

    await page.waitForSelector('#loginname');                   //等待賬號輸入框載入完成
    const inputText=await page.$('#loginname');                 //獲取賬號輸入框元素
    await inputText.click();                                    //防止被檢測,具體情況具體分析
    await inputText.type(config.username);

    await page.waitForSelector('input[name="password"]');       //等待密碼輸入框載入完成
    const inputPwd=await page.$('input[name="password"]');      //獲取密碼輸入框
    await inputPwd.click();//防止被檢測,具體情況具體分析
    await inputPwd.type(config.password);


    await page.waitForSelector('a[action-type="btn_submit"]');  //等待確認按鈕載入
    const submit=await page.$('a[action-type="btn_submit"]');   //獲取確認按鈕
    await submit.click();                                       //點選

})();

演示:翻車啦!!!

√實戰09:puppeteer模擬快捷鍵

程式碼演示:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-keyboard

√實戰10:puppeteer切換瀏覽器tab頁

主要用到api:browser.target();

文件:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-browser

√實戰11:puppeteer處理彈出的對話方塊

主要用到的api:Dialog處理彈出的內容

文件:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-dialog

√實戰12:puppeteer執行JavaScript方法

主要用到的方法:page.evaluate(()=>{ 在這裡面可以寫任意JS程式碼 })

文件:https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/docs/api.md#class-page

√實戰13:配置typescript環境,並在其中使用puppeteer

待完善

√實戰14:獲取全屏截圖

原始碼:

const puppeteer =require('puppeteer');//引入puppeteer
(async()=>{//使用自執行函式
    const browser = await puppeteer.launch();//生成browser例項

    const page = await browser.newPage();//生成一個頁面
    await page.goto('https://cn.aliyun.com/');//前往頁面

    console.log(await page.content());//列印頁面資訊(原始碼)
    await page.screenshot({//截圖
    path: 'ali.png',
    fullPage: true
    });

    await browser.close();//關閉browser例項
})();

效果圖: