iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0
Modern Web

Have fun! 新手也能打造的Javascript微型專案系列 第 26

Have fun! 新手也能打造的Javascript微型專案! Day26: 基礎網頁自動化,利用Puppeteer替你做一些麻煩的操作!

  • 分享至 

  • xImage
  •  
tags: ItIron2022 Javascript

前言

我們昨天示範了如何利用Puppeteer做基本的頁面資訊擷取,今天我們則要進一步的配合Puppeteer的其他功能做一些綜合應用,有了這些技巧後之後你就有能力做基本的網頁自動化操作囉!

Getting started

Step1: 專案結構

你同樣可以接續昨天的專案結構,我們需要的套件一樣只有Puppeteer一個,且需要一個app.js檔案去執行我們的腳本,最終你應該要有這樣的專案結構準備好。

目前為止你的專案結構應該會是這樣。

Step2: 在app.js檔案中引入puppeteer

首先先釐清一下我們今天的目標,今天的操作目標是Quotes to Scrape這個網站,是一個用於爬蟲練習的簡單頁面,收錄了許多知名的格言並附註其出處,同時也有基本的分類與會員登入功能。

我們今天希望能透過Puppeteer達成以下的自動化操作

  1. 點擊右上角的登入按鈕
  2. 輸入帳號與密碼
  3. 點選love分類

是的,我們今天需要用到點擊&輸入的功能,透過Puppeteer提供的API會讓這個過程相當的容易,馬上來看看吧!

就像之前文章強調的,你要操作頁面上的資訊,首先你要先知道該元素要如何選取,為此你需要透過devtool去知道那些元素的id或是class,以上方的流程來說你需要知道以下的元素

  1. 登入按鈕
  2. 帳號輸入元素
  3. 密碼輸入元素
  4. 提交按鈕
  5. Love分類按鈕

我們透過打開devtool找出所有指定的元素吧! 下圖以登入按鈕為例,配合devtool找出元素的資訊並不是難事!

同時別忘了在登入頁面去找輸入框的元素!

知道幾個指定的元素後我們就可以開始了,我們會照著與昨天相當類似的流程進行操作

  1. 建立瀏覽器實體
  2. 開啟分頁
  3. 指定分頁前往https://quotes.toscrape.com/
  4. 點擊登入按鈕
  5. 輸入帳號密碼
  6. 點擊love分類

請你在js檔案寫下以下的內容

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  page.setViewport({
    width: 1200,
    height: 800,
  });
  await page.goto("https://quotes.toscrape.com/");
  // 點擊登入按鈕
  await page.click('a[href="/login"]');

  // 輸入帳號
  await page.type("#username", "windate3411");

  // 輸入密碼
  await page.type("#password", "1234");

  // 點擊提交按鈕
  await page.click('input[type="submit"]');

  // 點擊love分類按鈕
  await page.click('a[href="/tag/love/"]');

  browser.close();
})();

最後一樣請你在終端機輸入以下指令執行我們的腳本

node app.js

這次我們用了click & type兩個新語法,使用的方法都相當簡單,只要選擇到元素剩下的就好說。但實際操作的話你會發現上方的程式碼有些小問題,畫面最終會停在首頁而非love tag的分頁。

聰明的你應該想到了,原因在於我們點擊love tag的時機點,我們並沒有等到整個登入流程完成後就去點擊元素,造成這樣意外的結果。
這種情況最簡單的方法就是稍等一下下,比方說等個三秒五秒之類的,給瀏覽器一些處理的時間後再做最後的點擊操作。

請你將上方的程式碼略作修正

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage({});
  page.setViewport({
    width: 1200,
    height: 800,
  });
  await page.goto("https://quotes.toscrape.com/");
  // 點擊登入按鈕
  await page.click('a[href="/login"]');

  // 輸入帳號
  await page.type("#username", "windate3411");

  // 輸入密碼
  await page.type("#password", "1234");

  // 點擊提交按鈕
  await page.click('input[type="submit"]');

  // 稍等一下再進行下一步
  await page.waitForTimeout(3000); // 加入這行

  await page.click('a[href="/tag/love/"]');

  browser.close();
})();

再次運行你會看到這樣的結果,很好! 一切都如同我們預期!

但這樣的做法有著極大的缺點,原因在於你並不能保證你指定的時間後頁面就已經渲染完畢或是可以點擊了,就連官方文件都有著這樣的說法。

It's generally recommended to not wait for a number of seconds, but instead use Frame.waitForSelector(), Frame.waitForXPath() or Frame.waitForFunction() to wait for exactly the conditions you want.

這個部分就當作回家作業,歡迎有興趣的人去做嘗試囉!

總結

我們今天利用了Puppeteer完成了簡單的網頁自動化操作,這類的操作配合我們昨天談到的資料擷取就會是很有用的工具,Puppeteer能做到的事情遠遠不止於此,這幾天的文章連教學都稱不上,只能稱作是入門磚讓你知道有這樣的玩意存在,之後若是需要網頁爬蟲或自動化測試都可以試著鑽研一下這個套件! 配合chrome擴充插件的使用會更加如虎添翼!

文章中的範例程式碼可以在這邊取得,歡迎自行取用

轉職Q & A

Danny,我之前聽了你的說明,決定要去挑戰外商公司,到底要英文多好才可以去外商工作啊?

大概多益990、托福120或是雅思9就可以去了吧!簡單得很! 玩笑話放一邊,實際在外商工作過後你就會發現,許多人把語言這玩意看得太重了,說穿了英文也只是溝通工具的一種,能做到溝通就足夠了!當然我不否認你需要一定的聽力跟閱讀能力,畢竟幾乎所有的會議與文件都會是以英文為主,但口說這方面...坦白說外國人對於非母語人士真的是非常的包容,講錯、講慢都不太會去糾正你,你只要勇敢表達自己的意思配合一些文字的輔助,要應付工作的內容真的不是太困難的事情,畢竟程式碼是共通的!

很多東西都只是熟能生巧,面試前做好一些常見問題的英文回答(比方說基本的自我介紹、你為什麼想來這個團隊面試、過往的經歷以及技術相關的面試問題等),即便你在回答的過程中有點卡,只要你能穩穩地表達你的意思,口說得流利度並不會決定你面試的成敗! 我非常推薦鼓起勇氣去嘗試一些外商的面試,英文絕對是開啟工程師超能力的好方法,太多太多的機會不存在於台灣,不要因為不敢英文口說這樣的事情耽誤了你的職涯發展,勇敢去做一些嘗試吧!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
Have fun! 新手也能打造的Javascript微型專案! Day25: 基礎爬蟲,利用Puppeteer替你擷取頁面中元素的資訊!
下一篇
Have fun! 新手也能打造的Javascript微型專案! Day27: 想要與朋友分享你的專案嗎? 一起來透過Railway部署你的作品吧!(Heroku alternative)
系列文
Have fun! 新手也能打造的Javascript微型專案30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
boycher2000
iT邦新手 5 級 ‧ 2023-04-13 11:38:50

<button id="details-button" class="secondary-button small-link" aria-expanded="false">進階</button>

想請問這樣的一個按鈕,無論是我使用
page.click('#details-button')

page.click('button[id="details-button"]')
都沒有反應是為什麼

看更多先前的回應...收起先前的回應...

boycher2000 可能的原因坦白說挺多的,沒有更多情報前我也只能通靈,以下我列出幾種可能的猜測

  1. 選擇的元素不存在(selector或url有typo, 目標網站更新等等)
  2. 觸發點擊時頁面尚未渲染出該元素,可以試著用puppteer提供的一些方法等到元素渲染後再觸發點擊事件。

我其實是想針對內部沒有有效憑證的Server,做一個自動化登入
想請教針對"進階"及"繼續前往"的按鈕我應該要怎麼寫他的元素才好https://ithelp.ithome.com.tw/upload/images/20230413/20114550bslnYgYgqH.png
https://ithelp.ithome.com.tw/upload/images/20230413/20114550ZxXjssQ2b9.png

    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();

    await page.goto('https://192.168.0.157/WebConsole/#!/account/settings');

    // Set screen size
    await page.setViewport({ width: 1080, height: 1024 });

    await page.click('#details-button');
    await page.click('#proceed-link');


    // Type into login box

    await page.waitForSelector('input[name="username"]');
    await page.type('[name=username]', pid);

    await page.waitForSelector('input[name="password"]');
    await page.click('input[name="password"]')
    await page.type('[name=password]', pidpw, { delay: 100 });

    await page.waitForSelector('[type=submit]');
    await page.click('[type=submit]')

用上面的程式碼也會得到錯誤ERR_CERT_AUTHORITY_INVALID的錯誤
https://ithelp.ithome.com.tw/upload/images/20230413/20114550xpCKLftqPS.png
難道說puppeteer沒辦法這樣用?

自問自答,加了ignoreHTTPSErrors: true就OK了
就不會有unsecure的頁面

有解決就好! 不好意思啊,我完全誤解你的問題了www

不好意思想再請教一下,已經卡我一個禮拜了
我想要按變更密碼這個tab,可是他的ID是會變動的
https://ithelp.ithome.com.tw/upload/images/20230421/20114550QylW1KYdLj.png

https://ithelp.ithome.com.tw/upload/images/20230421/201145504s0apFwSku.png

我嘗試用以下的寫法但都沒有成功

    await page.evaluate(() => {
        const elements = [...document.querySelectorAll('span')];
        const targetElement = elements.find(e => e.contains == '變更密碼');
        if (targetElement) targetElement.click();
    });

我應該怎麼寫才好呢? 然後對我來說的大魔王是舊的密碼新的密碼這邊
輸入文字的type欄位還都叫password,ID也是完全浮動,完全沒轍...
https://ithelp.ithome.com.tw/upload/images/20230421/20114550zv7HDJmulr.png

boycher2000 下午好~! 我大致上看了一下你提供的截圖,如果是像這種浮動id的情況,我會建議採用結構式的選取,總不可能連HTML結構都是動態的吧!
以tabs的部分為例,你可以看看他是固定在第幾個li元素,這麼一想後續的操作應該就簡單多了!輸入密碼的地方同理,選取所有的input元素後再依照順序操作就行囉!

我要留言

立即登入留言