Puppeteerを使ったウェブスクレイピング

このステップガイドでは、Puppeteerを使って静的および動的なウェブサイトをスクレイピングする方法を紹介します。
3 min read
web scraping with puppeteer

Puppeteerはブラウザテストと自動化のライブラリで、ウェブスクレイピングにも適しています。Axiosやcheerioのようなシンプルなツールに比べ、Puppeteerは、開発者が動的コンテンツ、(つまりユーザーのアクションに基づいて変化するコンテンツ)をスクレイピングすることを可能にします。

つまり、JavaScriptを使ってコンテンツを読み込むウェブアプリケーション(シングルページアプリケーション)のスクレイピングに使えるのです。ここで行おうとしているのは、まさにこのことです。

Puppeteerを使ったウェブスクレイピング

このチュートリアルでは、Puppeteerを使って静的、動的データ(つまり、Bright Dataのブログの投稿タイトルやリンク)をスクレイピングする方法を説明します。

セットアップ

チュートリアルを始める前に、お使いのコンピュータにNode.jsがインストールされていることを確認する必要があります。公式のダウンロードページからダウンロードできます。

次に、プロジェクト用に新しいディレクトリを作成して、以下のコマンドでそのディレクトリに移動します。

mkdir puppeteer_tutorial 
cd puppeteer_tutorial 
npm init -y 

次に、以下のコマンドでPuppeteerをインストールします。

npm i puppeteer --save

このコマンドで、ライブラリが使用する専用ブラウザもダウンロードされます。

静的サイトのスクレイピング

他のウェブスクレイピングツールと同様、PuppeteerでもウェブページのHTMLコードをスクレイピングできます。

以下に、Puppeteerを使ってBright Dataのブログから投稿の最初のページをスクレイピングする手順を示します。

index.jsファイルを作成して、Puppeteerをインポートします。

const puppeteer = require('puppeteer');

次に、Puppeteerの実行に必要な定型文を挿入します。

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  // all the web scraping will happen here  

  await browser.close();

})();

この関数はブラウザを開き、スクレイピングするページに移動して、ブラウザを閉じます。

あとはページからデータをスクレイピングするだけです。

Puppeteerで、HTMLデータにアクセスする最も簡単な方法は、page.evaluateメソッドを使うことです。Puppeteerには、要素を取得するのに便利な$メソッドと$$メソッドがありますが、page.evaluateからすべてのデータを取得する方が簡単です。

Bright Dataのブログでは、すべてのブログ投稿データは、brd_post_entryというクラスの<a>タグで囲まれています。投稿のタイトルは、brd_post_titleクラスの<h3>要素内にあります。投稿へのリンクは、brd_post_entryhref値です。

これらの値を抽出するpage.evaluate関数は、以下のようになります。

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

最後に、データをコンソールに出力できます。

  console.log(data);

スクリプト全体は以下の通りです。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  const data = await page.evaluate(() => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

ターミナルでnode index.jsを呼び出して実行します。このスクリプトで、投稿のタイトルとリンクのリストが返されるはずです。

[
  {
    title: 'APIs for Dummies: Learning About APIs',
    link: 'https://brightdata.com/blog/web-data/apis-for-dummies'
  },
  {
    title: 'Guide to Using cURL with Python',
    link: 'https://brightdata.com/blog/how-tos/curl-with-python'
  },
  {
    title: 'Guide to Scraping Walmart',
    link: 'https://brightdata.com/blog/how-tos/guide-to-scraping-walmart'
  },
…

動的コンテンツのスクレイピング

静的コンテンツのスクレイピングは、単純なツールで簡単に行えるシンプルな作業です。ありがたいことに、Puppeteerは、クリック、入力、スクロールなど、さまざまなアクションを行うことができます。これらすべてを使って、動的なページと対話し、ユーザーのアクションをシミュレートできます。

このようなライブラリを使った一般的なウェブスクレイピングのタスクは、ページ上の特定のデータセットを検索することです。例えば、Puppeteerを使用して、Puppeteerに関するすべてのBright Dataの投稿を検索したいと思うかもしれません。

以下に、その方法を示します。

ステップ1:クッキーを受け入れる

Bright Dataのブログにアクセスすると、クッキーのバナーが表示されることがあります。

それに対処するためには、以下のコードを使って、すべて受け入れるボタンをクリックする必要があります。

  await page.waitForSelector('#brd_cookies_bar_accept', {timeout: 5000})
    .then(element => element.click())
    .catch(error => console.log(error));

コードの先頭行で、#brd_cookies_bar_acceptを持つ項目が表示されるまで5秒間待機します。2行目で、その要素をクリックします。3行目で、クッキーバーが表示されなくてもスクリプトがクラッシュしないようにします。

Puppeteerでの待機は、前のアクションが実行されるまでの一定の待機時間を設定するのではなく、待機する条件を指定することにより行われることに留意してください。前者を暗黙的待機、後者を明示的待機と呼びます。

Puppeteerでの明示的待機は、実行上の問題につながるため、使用しないことが強く推奨されています。待機時間を明示的に指定すると、長すぎる(効率的でない)か短すぎる(スクリプトが正しく実行されない)のどちらかになります。

ステップ2:投稿を検索する

その後、スクリプトは検索アイコンをクリックする必要があります。「Puppeteer」と入力し、検索アイコンを再度押して検索を行います。

これは以下のコードで実行できます。


await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

 await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

この例は、クッキーバナーの例と同様に動作します。ボタンをクリックした後、検索コンテナが表示されるまで待つ必要があります。そのため、このコードでは.search_container.activeのCSSセレクタに一致する要素を待機します。

さらに最終的には、項目を読み込むために2秒の停止を加える必要があります。Puppeteerでは明示的な待機は推奨されませんが、今は他に良い選択肢がありません。

大半のウェブサイトでは、URLの変更が起こった場合、waitForNavigationメソッドを使用できます。新しい要素が現れたら、waitForSelectorメソッドを使えばよいのです。一部の要素が更新されるかどうかを把握するのは少し難しく、この記事の範囲外です。

自分で試してみたい場合は、このStack Overflowの回答が役に立つことでしょう。

ステップ3:投稿を収集する

投稿を検索した後、静的ページのスクレイピングにすでに使用したコードを使用して、ブログ投稿のタイトルを取得できます。

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

スクリプトの全コードは以下の通りです。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null
  });

  const page = await browser.newPage();
  await page.goto('https://brightdata.com/blog');

  const cookie_bar_accept = await page.waitForSelector('#brd_cookies_bar_accept');
  await cookie_bar_accept.click();
  await new Promise(r => setTimeout(r, 500));

  await page.click('.search_icon');

  await page.waitForSelector('.search_container.active');
  const search_form = await page.waitForSelector('#blog_search');
  await search_form.type('puppeteer');

  await page.click('.search_icon');

  await new Promise(r => setTimeout(r, 2000));

  const data = await page.evaluate( () => {

    let data = [];
    const titles = document.querySelectorAll('.brd_post_entry');

    for (const title of titles) {
      const titleText = title.querySelector('.brd_post_title').textContent;
      const titleLink = title.href;

      const article = { title: titleText, link: titleLink };
      data.push(article);
    }

    return data;

  })

  console.log(data);

  await browser.close();

})();

もっとうまくやれるでしょうか?

Puppeteerでウェブスクレイピングスクリプトを作成することは可能ですが、理想的ではありません。Puppeteerはテスト自動化のために作られているため、ウェブスクレイピングを行うには少し不便です。

例えば、お使いのスクリプトで規模拡大と効率化を実現したい場合、ブロックされずにスクレイピングできることが重要です。そのために、ユーザーとスクレイピングするウェブサイトの間にプロキシ(ゲートウェイ)を使うことができます。Puppeteer はプロキシの使用をサポートしていますが、プロキシネットワークを自分で探して契約する必要があります(詳しくは、Bright DataとのPuppeteerプロキシ統合をご覧ください)。

さらに、Puppeteerを並列使用に最適化するのは容易ではありません。大量のデータをスクレイピングしたいのであれば、パフォーマンスを最適化するための努力が必要になります。

こうした欠点があるため、Puppeteerは趣味で使う小規模なスクリプトには適しているものの、これを使用する場合、運用の規模を拡大するにはかなりの時間がかかります。

もっと使いやすいものが欲しいのであれば、Bright Dataのようなウェブデータプラットフォームを選ぶとよいでしょう。これにより、企業はスクレイピング専用に作られたScraping Browser(Puppeteer/Playwright互換)のような使いやすいツールを使って、ウェブから大量の構造化データを収集できます。

まとめ

この記事では、Puppeteerを使用して静的および動的なウェブページをスクレイピングする方法を説明しました。

Puppeteerは、項目をクリックしたり、テキストを入力したり、JavaScriptを実行したりと、ブラウザで実行可能なほとんどのアクションを実行できます。また、暗黙的待機を使用するため、Puppeteerで書かれたスクリプトは高速で簡単に記述できます。

ただし、いくつかの問題が発生する可能性もあります。Puppeteerはウェブスクレイピング用の最も効率的なツールではなく、そのドキュメントは初心者には適していません。また、Puppeteerの使い方に精通していない場合、Puppeteerを使ってスクレイピングの規模を拡大するのは困難です。

自分でデータをスクレイピングするのにうんざりしていませんか?事前に収集された、またはカスタマイズ済みのデータセットを入手してください。

あなたは下記にもご興味がおありかもしれません

各種ご利用方法

Node.js でプロキシサーバーを使用する方法

データ収集の手法である Web スクレイピングは、IP の禁止、ジオブロッキング、プライバシーの問題など、さまざまな障害によって妨げられることがよくあります。幸い、これらの課題を乗り越えるのにプロキシサーバーが有効です。プロキシサーバーはユーザーのコンピューターとインターネットの間の仲介役となり、独自の IP アドレスで要求を処理します。この機能は、IP 関連の制限や禁止を回避するだけでなく、地理的に制限されたコンテンツへのアクセスも容易になります。さらに、プロキシサーバーは Web スクレイピング中に匿名性を維持し、プライバシーの保護にも役立ちます。 また、プロキシサーバーを利用することで、Web スクレイピングのパフォーマンスと信頼性を向上させることもできます。要求を複数のサーバーに分散させることで、1 台のサーバーに過剰な負荷がかからないようにし、プロセスを最適化します。 このチュートリアルでは、Web スクレイピングプロジェクト向けに Node.js でプロキシサーバーを使用する方法を解説します。 前提条件 このチュートリアルを始める前に、JavaScript と Node.js についてある程度理解しておくことをお勧めします。Node.js がコンピューターにまだインストールされていない場合は、 今すぐインストールする必要があります。 適切なテキストエディターも必要です。Sublime Text など、いくつかの選択肢があります。このチュートリアルでは、Visual Studio Code (VS Code) を使用します。このエディターは使いやすく、コーディングを容易にする機能が満載です。 はじめに、web-scraping-proxy という名前の新しいディレクトリを作成し、Node.js プロジェクトを初期化します。ターミナルまたはシェルを開き、次のコマンドを使用して新しいディレクトリに移動します。 次に、HTTP 要求を処理して HTML を解析するために、Node.js パッケージをいくつかインストールする必要があります。 プロジェクトディレクトリにいることを確認し、次のコマンドを実行します。 HTTP 要求で Web コンテンツを取得するには、axios を使用します。Playwright と Puppeteer は、動的な Web サイトのスクレイピングに不可欠なブラウザの操作を自動化します。Playwright はさまざまなブラウザをサポートしており、Puppeteer は Chrome または Chromium […]
4 min read
How to Set Proxy in AIOHTTP
各種ご利用方法

AIOHTTPのプロキシ設定方法

このガイドではAIOHTTPのプロキシ設定方法を段階的に説明
3 min read
Invoke-Webrequest With a Proxy
各種ご利用方法

プロキシで PowerShell Invoke-WebRequest を使用する方法

PowerShell の Invoke-WebRequest コマンドレットは、ウェブサイトに HTTP 要求を行うのに便利なツールです。Bright Data のプロキシサービスを使用している場合は、-Proxy パラメーターの後にプロキシの詳細を指定することで、このコマンドレットをプロキシで使用できます。
3 min read