Puppeteerを利用したWebスクレイピングについて記載します。
Puppeteerとは
Puppeteerは、Googleが開発した、DevTools Protocolを通じてヘッドレスChromeを制御できるNode.jsライブラリです。
Selenium WebdriverやPhantomJSのようなブラウザ拡張を必要とせず、ヘッドレスChromeやChromebitデバイスを使って、アプリケーションのテストを自動化するためのツールとして知られています。
Puppeteerは、Webアプリケーションのテストを自動化することができます。
スクレイピングができるツールとして広く使用されています。
Puppeteerのgithubページ
Puppeteerで出来る事
Puppeteerで出来る事を簡単にまとめておきます
- 最新の自動テスト環境を構築可能。最新バージョンのChromeで、最新のJavaScriptとブラウザの機能を使って、テストを直接実行可能。
- ページのスクリーンショットとPDFを生成。
- SPA(シングルページアプリケーション)をクロールし、プリレンダリングコンテンツを生成(SSR(サーバーサイドレンダリング))。
- フォーム送信、UIテスト、キーボード入力などのアクションを自動化。
Puppeteerを使えば、普段、Chromeを使ってインターネットで情報収集をしていたり、データをまとめていたりする作業を自動化することが出来るようになります。
Puppeteerインストール
PuppeteerはNode.jsのライブラリのため、node.jsが必要です。
まだインストールできていない場合は、Node.jsをインストールしてください。
それでは、puppeteerのインストールを始めましょう。
ターミナルソフトでnpm install puppeteerと入力し,Enterを押してください。
これでインストールは完了です。非常に簡単ですね。
Puppeteerの使い方
次にPuppeteerの使い方について記載します。
Visual Studio Codeなどのエディタでtest.jsファイルを作成しましょう。
最低限必要な設定
デフォルトの設定は下記のような形になります。
コードの内容
- Puppeteerライブラリをインストールし、launch関数にて各種設定
- ブラウザを開く
- Chromeで対象のページを開く(下記例ではgoogle.comを指定)
- ブラウザを閉じる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, // Chromeのビューポート設定 // defaultViewport: { // width: 1320,height: 1080 // }, // 自分のChromeプロファイルを利用したい場合は設定。設定しない場合デフォルトのChromeが起動 // executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', // userDataDir: 'C:\\Users\\user\\AppData\\Local\\Google\\Chrome\\User Data', } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページに移動。 await page.goto('https://google.com'); //ブラウザを閉じる。 await browser.close(); }); |
Puppeteer基本的なコマンド
次にPuppeteerの代表的なコマンド(コード)を紹介します。
新しいブラウザを開く
表示しているタイトルはtitleファンクションで取得できます。
1 |
let browser = await puppeteer.launch({product : "chrome" }) |
クリック
clickファンクションでクリックしたい要素を指定してクリック出来ます。
1 |
await page.click(".LC20lb"); |
表示しているページタイトルの取得
表示しているタイトルはtitleファンクションで取得できます。
1 |
await page.title() |
表示しているページのURLを取得
表示しているページのURLはurlファンクションで取得できます。
1 |
await page.url() |
表示しているページのソースコードを取得
表示しているページのソースコードはcontentファンクションで取得できます。
1 |
await page.content() |
要素のテキストを取得
要素のテキストは、取得したい要素をまず取得し、その後getPropertyファンクションにtextContentを指定し、jsonValueファンクションで取得可能です。
1 2 |
const t = await page.$("#txt") const text = await (await n.getProperty('textContent')).jsonValue() |
要素の属性取得方法
取得したい要素の属性はgetAttributeで取得可能です。
1 |
let v = await page.$eval("input", element=> element.getAttribute("class")) |
要素の取得方法 1つのクエリセレクターの1つの要素を取る
1つのクエリから一つの要素のみ取りたい場合は、下記で可能です。
複数要素がある場合は、一番初めに表示されている要素のみ取得されます。
1 2 |
const imageSrcs = await page.$('img'); picSrc = await (await imageSrc.getProperty('src')).jsonValue() |
要素の取得方法 1つのクエリセレクターの複数の要素を取る
複数の要素を取得したい場合は、$$ファンクションで取得可能です
1 2 |
const imageSrcs = await page.$$('img'); picSrc = await (await imageSrc.getProperty('src')).jsonValue() |
同期処理
puppeteerはasync関数内で非同期で処理が進みます。
ページを遅らせたい、時間をある程度待ちたいなどの場合は、同期処理が必要になります。
waitForファンクションで時間を指定することによって指定時間処理を待つことが出来ます。
時間の単位はミリセックです。
1 |
await page.waitFor(4000) |
キーボードを動かす
キーボードも利用する事が出来ます。下記例ではEnterコマンドを押す例を示しています。
1 |
keyboard.press('Enter') |
よく使うコード集(実用例)
実際に私がスクレイピングする際によく使うコードをまとめてみました。
Google検索結果の1番目の結果を取得
Googleで検索したい内容を検索し、ページ一番目に取得した内容を取得するコードは下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //clipboardyのライブラリをインストール import clipboardy from 'clipboardy'; //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページへ移動 await page.goto('https://0xbrokers.com'); //検索したいワードをnameにしてい const name = "0xbrokers" await page.type('input[name=q]', name, { delay: 100 }) await page.keyboard.press('Enter'); //検索結果URL取得 検索結果のタイトルクラスはLC20lb await page.waitForSelector(".LC20lb", {visible: true}); const resultURL = page.url(); console.log("currentURL:" + resultURL); //ブラウザを閉じる。 await browser.close(); }); |
Google検索結果のタイトルとURLを取得
Googleで検索したい内容を検索し、検索結果に表示されたすべてのタイトルとURLをリスト化して表示するコードは下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //clipboardyのライブラリをインストール import clipboardy from 'clipboardy'; //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページへ移動 await page.goto('https://0xbrokers.com'); //検索したいワードをnameにしてい const name = "0xbrokers" await page.type('input[name=q]', name, { delay: 100 }) await page.keyboard.press('Enter'); const searchResults = await page.evaluate(() => [...document.querySelectorAll(".LC20lb")].map(e => ({ title: e.innerText, link: e.parentNode.href }), ) ); console.log(searchResults); //ブラウザを閉じる。 await browser.close(); }); |
Google画像検索結果を一つづつクリックして保存
Googleで画像検索し、一つづつ画像をPCへ保存したい場合のコードは下記のとおりです。
下記例では、Google画像検索で表示された画像を1番目から15番目の画像を保存するコードとなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //画像保存するためfsライブラリをインストール const fs = require('fs'); //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページへ移動 await page.goto('https://google.com'); //検索したいワードをnameにしてい const name = "0xbrokers" await page.type('input[name=q]', name, { delay: 100 }) await page.keyboard.press('Enter'); await page.waitForSelector(".hdtb-mitem", {visible: true}); const googleTabLists = await page.$$('.hdtb-mitem'); let tabContent; for (const googleTabList of googleTabLists) { tabContent = await (await googleTabList.getProperty('textContent')).jsonValue() if(tabContent == "Images"){ console.log("Found Images Tab" + googleTabList); const aTag = await googleTabList.$('a') const href = await aTag.getProperty('href') const picURL = await href.jsonValue() await page.goto(picURL, {"waitUntil":"domcontentloaded"}); break; } } let totalPic = await page.$$('a.wXeWr.islib.nfEiy'); let picRelatedCount=0; for(let i=0;i<16;i++){ await totalPic[i].click(); await page.waitFor(4000) let picAfterClicks = await page.$$('.n3VNCb.KAlRDb'); console.log(picAfterClicks.length); for(const picAfterClick of picAfterClicks){ picRelatedCount++; let picContent = await (await picAfterClick.getProperty('src')).jsonValue() let picRelatedFileName = name + picRelatedCount + ".jpg" let viewSource = await page.goto(picContent,DCL); await fs.writeFile(`ここは自分の環境に合わせてフォルダを指定`, await viewSource.buffer(), (error) => { if (error) { return console.log(error); } console.log(`ダウンロード完了: ${picRelatedFileName}`); i++; }); } }; //ブラウザを閉じる。 await browser.close(); }); |
Google翻訳に文字を入力して、翻訳結果を格納
Google翻訳に文字を入力して、翻訳結果を取得するコードは下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページへ移動 await page.goto("https://translate.google.com/?sl=en&tl=ja&op=translate", {"waitUntil":"domcontentloaded"}); await page.waitFor(4000) // Google Translate 左側のテキストエリアにデータ入力 //Google翻訳 日本語バージョンの場合は、textarea[aria-label=原文]を指定する await page.type('textarea[aria-label=原文]', datas, { delay: 0.001 }) await sleep(5000); //ブラウザを閉じる。 await browser.close(); }); |
ページのコピペ
表示されているページをコピペするコードは下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //clipboardyのライブラリをインストール import clipboardy from 'clipboardy'; //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //0xbrokers.comのホームページに移動。(例) await page.goto('https://0xbrokers.com'); //コントロールA、Cを行いコピペ await page.keyboard.down('ControlLeft'); await page.keyboard.press('KeyA'); await page.keyboard.press('KeyC'); await page.keyboard.up('ControlLeft') //コピペした内容をクリップボードへ貼り付け const textCopypaste = clipboardy.readSync(); //ブラウザを閉じる。 await browser.close(); }); |
ワードプレスへAPI接続
ワードプレスへAPI接続し、自動で投稿するコードは下記のとおりです。
ワードプレスサーバー側でAPIの設定および独自のワードプレスライブラリを作成する必要があります(パスワード設定やhtaccessの設定など)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
//puppeteerのライブラリをインストール import puppeteer from 'puppeteer' //wordpressのライブラリを独自に作成し、インストール import {wpClient} from './domain.js'; //puppeteerのlaunch関数を使って、puppeteerを起動します。 puppeteer.launch( //puppeteerの設定を記載します。 { // headlessモードの設定 trueの場合,ヘッドレスで起動 // 動作確認のため、falseに設定 headless: false, // 動作確認のため、puppeteerの操作を遅延させる設定 slowMo: 100, } //上記設定後にブラウザを起動 ).then(async browser => { const page = await browser.newPage(); //Googleのホームページへ移動 await page.goto('https://0xbrokers.com'); //検索したいワードをnameにしてい const name = "0xbrokers" await page.type('input[name=q]', name, { delay: 100 }) await page.keyboard.press('Enter'); await page.waitForSelector(".hdtb-mitem", {visible: true}); const googleTabLists = await page.$$('.hdtb-mitem'); await wpClient.newPost({ title: wpTitle, status: 'draft', // thumbnail: arguments[1].id, content: wpContent, author: 1 //customFields: [{ key: 'hoge', value: 'fuga' }] }, function (error, posts) { console.log(posts); console.log("*****WPへ記事アップロード終了*****") }); //ブラウザを閉じる。 await browser.close(); }); |
Puppeteerを利用してスクレイピングする上で注意する事
最後にPuppeteerを利用するうえで注意するべき2点を記載しておきます
1.スクレイピングをしても問題ないかどうか利用規約を確認する
スクレイピングは動作によっては、対象のページのサーバーに負荷を与えるものです。
スクレイピングしたい対象のサイトでスクレイピングをしても問題ないか確認してから行うようにしましょう。
また利用できたとしても、極力負荷がかからないようにコードを作成してスクレイピングするようにしましょう。
2.Chrome historyが肥大化する可能性があるため、定期的に消す
PuppeteerでスクレイピングをしているとChromeのhistoryが肥大化してChromeの動作が非常に重くなることがあります。実際私はよくこの状況になります。
スクレイピングをしてChrome処理を自動化、長期間使用していると、historyのサイズが10GB以上になることなんてざらにあります。
定期的にChrome履歴を消すようにしましょう。
実際に手で消すことも可能ですが、バッチファイルで自動化して消すことも可能です。
以下にchromeヒストリーをバッチファイルで消すバッチファイルのソースコード例も記載しておきます。
1 2 3 4 5 6 |
rem delete history and favicons on chrome rem 下記は私の環境の場合です。Chromeのprofileのフォルダを指定して移動するコマンド cd C:\Users\HP\AppData\Local\Google\Chrome\"User Data"\"Profile 1" taskkill /F /IM chrome.exe /T del History del Favicons |