九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、このところNode.js
(Express)での開発を続けていて色々と新しい技術や開発思想に触れる機会を多く持つことができました。
もちろんまだまだ学ぶことは多いのですが、Node.js
を使った開発はある程度理解が深まってきたので、この辺で、「これは今後も使いそう。でも、ちょっと複雑ですぐ忘れちゃいそう・・・😅」という内容を備忘録的にまとめておくことにしました。
ということで、今回は「実際のブラウザを使ってウェブサイトにアクセスできる」puppeteer
を使った「こんなとき」をご紹介していきたいと思います。
※puppeteer
のインストール方法や基本的な使い方は、以前「JavaScriptでレンダリングしてるページをスクレイピングする方法」という記事を公開していますので、ぜひそちらをご覧ください。
ぜひ皆さんのお役に立てると嬉しいです😊✨
開発環境:Node.js 8
目次
スタイルシートやJavaScript、画像などの外部ファイルを読み込まないようにする
大多数のウェブページでは、ひとつのページだけでなくスタイルシートやJavaScript、画像などを使って装飾や動きを加えています。さらにそれらは、.css
や.js
など外部ファイル化している場合が多いですよね。
ただ、スクレイピングをする場合にはこういった「外部ファイル」が不要だったりする場合があったりします。
なによりpuppeteer
はchrome
を使っているとはいえ、全ての外部ファイルを読み込んでしまうとサーバーに負荷がかかりすぎて動かなくなってしまう場合もあります。
そんな場合は、次のようにして不要なURLは事前に読み込みをキャンセルすることができます。
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { const requestType = request.resourceType(); if(requestType === 'document') { // 文書ファイルのみOK request.continue(); // 続けて読み込む } else { request.abort(); // 外部ファイルは読み込まない } }); await page.goto('https://www.yahoo.co.jp/'); const headingText = await page.evaluate(() => { return document.querySelector('h1').innerText; // h1のテキストを取得 }); await browser.close(); console.log(headingText); // Yahoo! JAPAN })();
流れとしては、Chrome
が外部ファイルにリクエストをする直前にabort()
を使ってキャンセルをしています。
なお、より細かくアクセスするURLを分けたい場合は以下がresourceType()
で取得できるリソースタイプになりますので、if
文などで制御するといいでしょう。
- document
- stylesheet
- image
- media
- font
- script
- texttrack
- xhr
- fetch
- eventsource
- websocket
- manifest
- other
iFrameを読み込まないようにする
基本的に外部ファイルを遮断するには、前項目の「スタイルシートやJavaScript、画像などの外部ファイルを読み込まないようにする」で実現できますが、実はiFrame
だけはリソースタイプが同じくdocument
になってしまうので、それだけでは遮断することができません。
そのため、iFrame
の遮断をするには以下のようにframeattached
を使って「次読み込むURLはiFrameのものなのかどうか」で判別します。
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); let isIframe = false; // iFrameかどうかを判別する変数 page.on('request', request => { const requestType = request.resourceType(); if(requestType === 'document' && !isIframe) { // 文書ファイルのみOK request.continue(); // 続けて読み込む } else { request.abort(); // iFrame、外部ファイルは読み込まない } isIframe = false; // iFrameの結果を初期化 }); page.on('frameattached', frame => { isIframe = true; // iFrameの読み込みの場合は "true" になる }); await page.goto('https://www.w3schools.com/html/html_iframe.asp'); const headingText = await page.evaluate(() => { return document.querySelector('h1').innerText; // h1のテキストを取得 }); await browser.close(); console.log(headingText); // HTML Iframes })();
HTTPステータスコードを取得する
例えば、アクセスがうまくいったときの200
やページが存在しないことを意味する404
、エラーの500
などのステータスコードを取得するには、response
を読み取ることで実現できます。
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { const requestType = request.resourceType(); if(requestType === 'document') { request.continue(); } else { request.abort(); } }); let statusCode = 0; page.on('response', response => { statusCode = response.status(); // HTTPステータスコードを取得する }); await page.goto('https://www.yahoo.co.jp/'); const headingText = await page.evaluate(() => { return document.querySelector('h1').innerText; // h1のテキストを取得 }); await browser.close(); console.log(statusCode); // 200 })();
なお、レスポンスヘッダー全てを取得するには、response.headers()
を使ってください。
以下は、取得できるデータのサンプルです。
{ status: '200', 'content-encoding': 'gzip', 'accept-ranges': 'bytes', 'cache-control': 'max-age=604800', 'content-type': 'text/html; charset=UTF-8', date: 'Thu, 13 Feb 2020 10:19:08 GMT', etag: '"3147526947+gzip"', expires: 'Thu, 20 Feb 2020 10:19:08 GMT', 'last-modified': 'Thu, 17 Oct 2019 07:18:26 GMT', server: 'EOS (vny/0452)', vary: 'Accept-Encoding' }
jQueryを挿入する
例えば、Yahoo! JAPANにはjQuery
は使われていませんが、強制的にcdn
のjQuery
を読み込ませて$()
を使ってみます。
const puppeteer = require('puppeteer'); const JQUERY_URL = 'https://code.jquery.com/jquery-3.4.1.min.js'; (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { const requestType = request.resourceType(); const requestUrl = request.url(); if(requestType === 'document' || requestUrl === JQUERY_URL) { // 通常ページとjQueryだけは読み込む request.continue(); } else { // それ以外は読み込みをキャンセル request.abort(); } }); await page.goto('https://www.yahoo.co.jp/'); await page.addScriptTag({ url: JQUERY_URL }); // jQueryを強制的に読み込ませる const headingText = await page.evaluate(() => { return $('title').text(); // jQueryを使ってページタイトルを取得 }); await browser.close(); console.log(headingText); // Yahoo! JAPAN })();
POST送信する
POSTメソッドでアクセスするには、以下のようにcontinue()
でメソッドを指定します。
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { const requestType = request.resourceType(); if(requestType === 'document') { request.continue({ method: 'POST' }); // POSTメソッドでアクセス } else { request.abort(); } }); await page.goto('https://example.com/'); const headingText = await page.evaluate(() => { return document.querySelector('h1').innerText; }); await browser.close(); console.log(headingText); // Example Domain })();
ユーザーエージェントをセットする
ユーザーエージェントとは、ブラウザやアクセス環境を識別するためなどにつかわれている文字列のことで、簡単にいうと「私はGoogle Chromeです!」などと名乗ることができる文字列です。(つまり偽装はとても簡単ということです)
【ユーザーエージェントの例】
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36
ユーザーエージェントをセットするには以下のようにします。
const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36'); // 省略
※ちなみにcnetの記事によると、Google Chrome
ではこのユーザーエージェントの機能を今後縮小していく方針のようです。
Cookieを追加する
特定のCookie
をセットしてウェブサイトにアクセスするには、setCookie()
を使います。
const cookies = [ { 'name': 'YOUR-COOKIE-NAME', 'value': 'YOUR-COOKIE-VALUE', 'domain': 'www.example.com', // ドメイン(省略可) 'path': '/' // パス(省略可) } ]; const browser = await puppeteer.launch(); const page = await browser.newPage(); page.setCookie(...cookies);
※Cookie
の内容は配列でいくつでも追加できます。
スクリーンショットをとる
ウェブサイトのスクリーンショットをとるには以下のようにします。
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('http://example.com/'); await page.screenshot({path: './screenshot.png'}); })();
おわりに
ということで、今回はNode.js
の強力な武器といってもいいpuppeteer
の使い方をケースごとに紹介しました。
ただ、puppeteer
は今回紹介した以外にも色々なことができますので、今後何か「おっ!」というものがありましたら、その都度このページに追加していきたいと思います。
ぜひご期待下さいね。
ではでは〜!