Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8837

JavaScript Puppeteerで絶対ハマらないブラウザ操作2021

$
0
0

はじめまして。マーティンです。Twitterフォロワー20人記念記事、爆誕です。
スクレイピングライブラリはいろいろありますがブラウザ操作用ライブラリはPuppeteer一択じゃぁ!
個人的にはPythonよりJSでブラウザ操作する方が好みですが、このPuppeteerというライブラリは少々くせ者ですのでハマりやすいところを解説します。
JavaScriptの文法を理解した初心者の方向けです。

目次

1.そもそもPuppeteerとは
2.始め方
3.おすすめ起動オプションと注意点
4.ページ遷移
5.よく使うpageのメソッドと注意点

1. そもそもPuppeteerとは

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Nodeで動くChromium操作用のライブラリ。「ヘッドレス」なので、デスクトップにChromiumを表示させずに動かすこともできるということですね。
Chromiumというやつがオープンソースで、それをPuppeteerで操作するという流れ。自分のパソコンに入ってるChromeを操作するわけではない。Chromiumはpuppeteerをインストールすると一緒についてくる。

Async AwaitのおかげでPuppeteerを使うのはめちゃくちゃ簡単になりました。
Puppeteerは操り人形という意味らしい。

2. 始め方

$npm install puppeteer

installするものはこれだけです。
Puppeteerで何かするときは以下の雛形を用います。

constpuppeteer=require('puppeteer');constURL="https://example.com";(async()=>{constbrowser=awaitpuppeteer.launch();//あとでオプション設定するよconstpage=awaitbrowser.newPage();awaitpage.goto(URL);//あとでオプション設定するよ//ここでなんかするよawaitbrowser.close();//開発時はコメントアウトしておくと良いよ})();//即実行する関数よ

3. おすすめ起動オプションと注意点

オプションの指定が可能です。

Puppeteerはデフォルトでヘッドレスが有効になっていますが、無効に設定できます。
slowMoを設定しないと処理が超高速で行われてしまいます。非同期処理に自身がない方は必ずslowMoしましょう。非同期処理に自身がある方も基本的にslowMoしたほうが良いかと。slowMo:50くらいにするといい感じの速度でブラウザが操作されるので見ていて面白い。

またwindowサイズを決定することも重要です。レスポンシブデザインのサイトでは閲覧するブラウザのwindowサイズによってHTMLの要素が変化する場合があるため、windowサイズを明示的に固定することでこれを防ぎます。
defaultViewportを設定しましょう。defaultViewportを設定することによりHMTLページのサイズを指定します。下にある --window-size はHTMLページのサイズではなくブラウザのサイズのみ指定します。defaultViewportなしではレスポンシブデザインによるHMTL要素の変化を防止できません。

goto(URL)するときはwaitUntilも一緒につけちゃいましょう。
https://pptr.dev/#?product=Puppeteer&version=v5.5.0&show=api-pagegotourl-options
ページ遷移を待ってくれます。

//開発時はheadless:falseが良いawaitpuppeteer.launch({headless:false,slowMo:50,args:['--window-size=1900,1080'],defaultViewport:{width:1900,height:1080}});awaitpage.goto(URL,{"waitUntil":'networkidle0'})

学生向けコーナー(需要あるのか?

Googleにログインした状態でブラウザ操作したいときがありますがPuppeteerでGoogleにログインすることは基本的にできません。詳しくは以下の記事がよくまとまっていますのでご覧あれ。
https://qiita.com/vicugna-pacos/items/a52e22d08856d1041316
自動でブラウザを操作していることがバレて弾かれちゃいます。
しかしなんと、教育用Googleアカウント: xxxxxx@ed.jp等を持っている学生ならそのアカウントを使ってGoogleにログインできます。謎ですね!ところが一つ問題点があります。
Headless trueでGoogleにログインしたい場合は必ずUserAgentを設定しなければならないということです。(個人的にハマったところ)
Puppeteerではフォームに値を入力するとき、フォームのHTML要素を指定します。よってheadless falseでブラウザを起動させてからデベロッパーツールを使ってHTML要素を調べるわけです。
headless falseではこちらが指定した通りGoogleにログインできるのですが、trueにした途端できなくります。原因はGoogleログインフォームとUserAgentの関係。
下を試してみると...

//headless trueのときconsole.log(awaitbrowser.userAgent())// => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4298.0 Safari/537.36//headless falseのときconsole.log(awaitbrowser.userAgent())// => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4298.0 Safari/537.36

headlessがfalseの場合はuserAgentに「Chrome」とあり,headless trueにするとuserAgentに「HeadlessChrome」とあることからheadlessか否かによってUserAgentが異なることがわかります。
UserAgentが異なると何が問題なのでしょうか?
Googleのログイン画面を見てみましょう。

[headlessがtrue UserAgentがHeadlessChromeの場合]
headless.png
[headlessがfalse UserAgentがChromeの場合]
chrome.png

ご覧の通りHeadlessChromeの場合とChromeの場合でGoogleのログインフォームのHTML構造が異なるため、メールアドレスを打ち込む際の(後に述べますが)inputフィールドを指定するセレクターの違いが生まれることがエラー発生の原因でした。

で解決策は?

Headless trueの状況下でUserAgentをChromeに無理やり設定することで解決!UserAgentはご自分のChromiumで「UserAgent 確認」と検索するといろいろなUserAgent確認用サイトがヒットしますのでそこで調べてください。

awaitpage.setUserAgent("あなたのUserAgent")

4. ページ遷移

非同期処理って何?って方はこちらの記事が非常に分かりやすいので参考までに。
https://qiita.com/soarflat/items/1a9613e023200bbebcb3
https://qiita.com/klme_u6/items/ea155f82cbe44d6f5d88

ページ遷移はPuppeteerの肝。
「async await つけるだけでしょ」と思っていると少し危険です。
Puppeteerでページ遷移を待つときには page.waitForNavigation()が使われますが、これがよく誤用されがち。

waitForNavigationはページ遷移の前に宣言

awaitPromise.all([page.waitForNavigation(),page.click("button")]);

buttonタグをクリックするpage.click()よりもあとに宣言された場合、たまにエラーが発生します。clickによるページ遷移がwaitForNavigationが呼ばれる前に完了した場合、waitForNavigationはページ遷移を認識できない。

waitForNavigationよりwaitForSelector

waitForNavigationにはいくつかオプション指定ができますが、どのオプションを持ってしても確実にページ遷移の非同期処理が行われるという保証はありません。そこで登場するのがwaitForSelector。

awaitPromise.all([page.waitForSelector("input[name='email']"),page.click("button")])

読んで字の如く、指定したセレクターが出現するのを待ちます。セレクターの指定方法はquerySelectorと同じです(後述)。
waitForSelectorを使えば非同期処理エラーはまずないだろうと思いますが、それでも不安な方はwaitForTimeoutを使いましょう。

awaitPromise.all([<省略>])awaitpage.waitForTimeout(500)///500ミリセカンド待つ

余談ですが少し前まで使われていたwaitForは非推奨となりましたので代わりにwaitForTimeoutを使いましょう。

waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.

5. よく使うpageのメソッドと注意点

page.click(selector[, options])

ページ内のHMTL要素をCSSセレクターを用いて指定します。
CSSセレクターはどうやって指定するのか?
デベロッパーツールを開いてお目当てのHTMLタグを右クリック、「Copy」=>「copy selector」で一発です。オプションはあまり使わないので言及しません。

page.type(selector,text[,options])

指定したCSSセレクターにテキストを入力します。

page.evaluate(pageFunc[,...args])

具体例を見てみましょう。

lettext=awaitpage.evaluate((selector)=>{returndocument.querySelector(selector).innerHTML;},"div > h1")

page.evaluateの第一引数にpageFuncとして渡すアロー関数を、第二引数にセレクター"div > h1"を渡していることがわかります。第二引数に渡したセレクターがpageFuncの引数selectorに代入され処理が実行されています。
注意したいことが3つ。
1つ目がpageFuncはブラウザ内で実行される関数であるため、スクリプトファイル内で定義された変数を直接関数内で使うことはできないこと。しっかりpage.evaluateの第二引数として変数をわたしてあげましょう。
2つ目がreturnをすること。returnしないとtextに値がを代入されません。
3つ目はmapとforeachについて。evaluate内でquerySelectorAllするときはmapを使いましょう。
return document.querySelectorAll("div").map(() => (do stuff here))
mapは新しい配列を返すがforeachは配列を書き換えるだけ。またmap内でもreturnを忘れないこと。
初歩的なことですが、よく忘れてしまうので自戒も込めて。

 その他

const URL = "https://------"のところで行末にセミコロンをつけないとエラーになる
・Puppeteerを使ってGoogleForm自動回答マシーンを作りたい方はhttps://stackoverflow.com/questions/59328036/how-to-select-an-opion-from-a-google-forms-popup-dropdown-puppeteer-nodejs 
が参考になります。
・Puppeteerでブラウザ操作を定期的に行いたいwindowsユーザーの方にはタスクスケジューラの使用をおすすめします。.batファイルでnode実行コマンド記述->タスクスケジューラで定期的に.batファイルを実行。
・間違っているところがあれば指摘していただけると幸いです。

最後まで読んでいただきありがとうございました。


Viewing all articles
Browse latest Browse all 8837

Trending Articles