HTMLウェブスクレイピングチュートリアル

HTMLウェブスクレイピングをマスターして、ウェブサイトから効率的にデータを収集する方法を学び、プロジェクトや戦略の強化に役立てましょう。
5 分読
HTML web scraping main blog image

ウェブスクレイピングに興味があるなら、すべてのウェブサイトがHTMLで構築されているため、HTMLを理解することが重要です。ウェブスクレイピングはあらゆる場面で活用でき、APIのないウェブサイトからデータを収集したり、商品価格を監視したり、リードリストを作成したり、学術研究を行ったりするのに役立ちます。

この記事では、HTMLの基本と、Pythonを使ったデータの抽出・パース・処理方法を学びます。

Pythonによるウェブスクレイピングの詳細ガイドに興味がありますか?こちらをクリックしてください。

ウェブサイトをスクレイピングしてHTMLを抽出する方法

このチュートリアルを始める前に、HTMLの重要なコンポーネントを確認しておきましょう。

HTMLの基本

HTMLはブラウザにウェブサイトの構造と要素を伝える タグ の集合体です。例えば、<h1> Text </h1> はそのテキストが見出しであることをブラウザに伝え、<a href=""> link </a> はハイパーリンクを示します。

HTML属性はタグに関する追加情報を提供します。例えば、<a> </a>タグのhref属性は、その属性が指し示すページのURLに関する情報を提供します。

クラスとIDは、ページ上の要素を正確に識別するための重要な属性です。クラスは類似した要素をグループ化し、CSSで一貫したスタイルを適用したり、JavaScriptで統一的に操作したりするために使用されます。クラスは.class-nameを使ってターゲット指定します。

W3Schoolsのウェブサイトでは、クラスグループは次のようになります:

<div class="city">
  <h2>London</h2>
  <p>London is the capital of England.</p>
</div>

<div class="city">
  <h2>Paris</h2>
  <p>Paris is the capital of France.</p>
</div>

<div class="city">
  <h2>Tokyo</h2>
  <p>Tokyo is the capital of Japan.</p>
</div>

各タイトルと都市のブロックが同じcityクラスを持つdivで囲まれているのがわかります。

一方、IDは各要素に固有のものです(つまり、2つの要素が同じIDを持つことはできません)。例えば、次のH1はそれぞれ固有のIDを持ち、個別にスタイル設定・操作できます:

<h1 id="header1">Hello World!</h1>

<h1 id="header2">Lorem Ipsum Dolor</h1>

IDで要素をターゲット指定する構文は#id-nameです。

HTMLの基本を理解したところで、ウェブスクレイピングを始めましょう。

スクレイピング環境のセットアップ

このチュートリアルでは、多くのHTMLスクレイピングライブラリが利用でき、習得しやすいPythonを使用します。コンピュータにPythonがインストールされているか確認するには、PowerShell(Windows)またはターミナル(macOS)で次のコマンドを実行します:

python3

Pythonがインストールされていればバージョン番号が表示され、インストールされていなければエラーが表示されます。インストールされていない場合はPythonをインストールしてください。

次に、WebScraperというフォルダを作成し、その中にscraper.pyというファイルを作成します。そして、お好みの統合開発環境(IDE)で開きます。ここではVisual Studio Codeを使用します:

VSCが使用されている場所を示す画像

IDEはプログラマーがコードを書いたり、デバッグしたり、プログラムのテスト、ビルド自動化などを行える多目的アプリケーションです。ここではHTMLスクレイパーのコーディングに使用します。

次に、仮想環境を作成してグローバルなPythonインストールとスクレイピングプロジェクトを分離する必要があります。これにより依存関係の競合を避け、アプリケーション全体を整理された状態に保てます。

そのために、次のコマンドを使用してvirtualenvライブラリをインストールします:

pip3 install virtualenv

プロジェクトフォルダに移動します:

cd WebScraper

次に仮想環境を作成します:

 python<version> -m venv <virtual-environment-name>

このコマンドはプロジェクトフォルダ内にすべてのパッケージとスクリプト用のフォルダを作成します:

すべてのパッケージとスクリプト用フォルダを作成するコマンド

次に、使用するプラットフォームに応じて以下のいずれかのコマンドで仮想環境をアクティブ化します:

source <virtual-environment-name>/bin/activate #In MacOS and Linux

<virtual-environment-name>/Scripts/activate.bat #In CMD

<virtual-environment-name>/Scripts/Activate.ps1 #In Powershell

アクティブ化に成功すると、画面左側に仮想環境の名前が表示されます:

仮想環境の名前

仮想環境がアクティブ化されたら、ウェブスクレイピングライブラリをインストールする必要があります。PlaywrightSeleniumBeautiful SoupScrapyなど多数の選択肢があります。ここでは使いやすく、複数のブラウザに対応し、動的コンテンツを処理でき、ヘッドレスモード(グラフィカルユーザーインターフェース(GUI)なしでのスクレイピング)も提供するPlaywrightを使用します。

pip install pytest-playwrightを実行してPlaywrightをインストールし、playwright installで必要なブラウザをインストールします。

Playwrightのインストールが完了したら、ウェブスクレイピングを始める準備が整いました。

ウェブサイトからHTMLを抽出する

ウェブスクレイピングプロジェクトの最初のステップは、スクレイピングしたいウェブサイトを特定することです。ここではこのテストサイトを使用します。

次に、ページからスクレイピングしたい情報を特定します。この場合はページの完全なHTMLコンテンツです。

スクレイピングしたい情報を特定したら、スクレイパーのコーディングを始めます。Pythonでの最初のステップは、Playwrightに必要なライブラリをインポートすることです。Playwrightではsyncとasyncの2種類のAPIをインポートできます。asyncライブラリは非同期コードを書く場合のみ使用するため、次のコマンドでsyncライブラリをインポートします:

from playwright.sync_api import sync_playwright

syncライブラリをインポートした後、次のコードスニペットを使用してPython関数を宣言します:

def main():
    #Rest of the code will be inside this function

上記の注釈からわかるように、ウェブスクレイピングのコードはこの関数の中に記述します。

通常、ウェブサイトから情報を取得するには、ウェブブラウザを開き、新しいタブを作成してそのウェブサイトにアクセスします。サイトをスクレイピングするには、これらの操作をコードに変換する必要があり、それがPlaywrightの役割です。ドキュメントによると、先ほどインポートしたsync_apiを呼び出し、次のスニペットでブラウザを開くことができます:

with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)

括弧内にheadless=Falseを追加することで、ウェブサイトのコンテンツを確認できます。

ブラウザを開いた後、新しいタブを開いてターゲットURLにアクセスします:

page = browser.new_page()
try:
  page.goto("https://webscraper.io/test-sites/e-commerce/static")
except:
  print("Error")

注意: 上記のコードはブラウザを起動した行の下に追加する必要があります。このコードはすべてmain関数内の同一ファイルに記述します。

このコードスニペットは、より良いエラー処理のためにgoto()関数をtry-exceptブロックで囲んでいます。

サイトのURLを検索バーに入力すると、ページが読み込まれるまで待つ必要があります。コードでそれを再現するには、次のコードを使用します:

page.wait_for_timeout(7000) #millisecond value in brackets

注意: 上記のコードは前の行の下に追加する必要があります。

最後に、次のコードでページの HTMLコンテンツをすべて抽出 します:

print(page.content())

ページのHTMLを抽出するための完全なコードは次のようになります:


from playwright.sync_api import sync_playwright

def main():
  with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)   
        page = browser.new_page()
        try:
            page.goto("https://webscraper.io/test-sites/e-commerce/static")
        except:
            print("Error")

        page.wait_for_timeout(7000)
        print(page.content())

main()

Visual Studio Codeでは、抽出されたHTMLは次のように表示されます:

VSCで抽出されたHTML

特定の属性を使ってHTMLを抽出する

先ほどはウェブスクレイパーのウェブページの全要素を抽出しましたが、必要な情報だけをスクレイピングするように絞り込まない限り、ウェブスクレイピングは役に立ちません。このセクションでは、ウェブサイトの最初のページにあるすべてのノートパソコンのタイトルのみを抽出します:

ターゲットウェブサイトから抽出するタイトルを表示

特定の要素を抽出するには、ターゲットウェブサイトの構造を理解する必要があります。右クリックして検証オプションを選択することで確認できます:

ターゲットウェブサイトで検証をクリック

または、次のキーボードショートカットを使用することもできます:

  • macOSの場合は Cmd + Option + I
  • Windowsの場合は Control + Shift + C

ターゲットページの構造は次のようになっています:

ターゲットウェブサイトのHTML構造

検証ウィンドウの左上にある選択ツールを使って、ページ上の特定のアイテムのコードを確認できます:

ソースコードで特定のアイテムを検証する方法

検証ウィンドウでノートパソコンのタイトルのひとつを選択します:

スクレイピングしたいタイトルのひとつを検証

タイトルが<a> </a>タグの中にあり、それがh4タグで囲まれていて、リンクにtitleクラスがあることがわかります。つまり、titleクラスを持つ<h4>タグ内の<a href>タグ(URL)を探しています。

これらの要素を正確にターゲット指定するスクレイピングプログラムを作成するには、ライブラリをインポートしてPython関数を作成し、ブラウザを起動してターゲットウェブサイトにアクセスする必要があります:

from playwright.sync_api import sync_playwright

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        try:
            page.goto("https://webscraper.io/test-sites/e-commerce/static/computers/laptops")

        except:
            print("Error")

        page.wait_for_timeout(7000)

page.goto()関数内のターゲットURLがノートパソコンのリストを含む最初のページを指すように更新されていることに注意してください。

スクレイピングプログラムを作成したら、ウェブサイト構造の分析に基づいてターゲット要素を特定する必要があります。Playwrightにはロケーターというツールがあり、次のようなさまざまな属性に基づいてページ上の要素を特定できます:

  • get_by_label() は要素に関連付けられたラベルを使ってターゲット要素を特定します。
  • get_by_text() は要素が含むテキストを使ってターゲット要素を特定します。
  • get_by_alt_text() はaltテキストを使って画像のターゲット要素を特定し操作します。
  • get_by_test_id() は要素のテストIDを使ってターゲット要素を特定します。

要素を特定するその他のメソッドについては、公式ドキュメントを参照してください。

すべてのノートパソコンのタイトルをスクレイピングするには、タイトルをすべて囲む<h4>タグを特定する必要があります。get_by_role()ロケーターを使用して、ボタン、チェックボックス、見出しなどの機能に基づいて要素を検索できます。つまり、ページ上のすべての見出しを検索するには、次のように記述します:

titles = page.get_by_role("heading").all()

その後、次のコードでコンソールに出力できます:

print(titles)

出力すると、要素の配列が返されることがわかります:

タイトルを出力した後の要素の配列

この出力にはタイトルは含まれていませんが、セレクター条件に一致する要素への参照が含まれています。これらの要素をループして、titleクラスを持つ<a>タグとその中のテキストを見つける必要があります。

パスとクラスに基づいて要素を検索するにはCSSロケーターを使用することが推奨されており、all_inner_texts()関数を使って要素の内部テキストを抽出できます:

for title in titles:
  laptop = title.locator("a.title").all_inner_texts()

このコードを実行すると、出力は次のようになります:

出力がどのように見えるかを示す画像

値のない配列を除外するには、次のコードを記述します:

if len(laptop) == 1:
  print(laptop[0])

値のない配列を除外すると、特定の要素のみを抽出するスクレイピングプログラムの構築が完了です。

このスクレイパーの完全なコードは次のとおりです:

from playwright.sync_api import sync_playwright

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        try:
            page.goto("https://webscraper.io/test-sites/e-commerce/static/computers/laptops")

        except:
            print("Error")

        page.wait_for_timeout(7000)

        titles = page.get_by_role("heading").all()

        for title in titles:
            laptop = title.locator("a.title").all_inner_texts()
            if len(laptop) == 1:
                print(laptop[0])

main()

要素と対話する

次のレベルに進んで、ノートパソコンを含む最初のページからタイトルを抽出し、2ページ目に移動してそこのタイトルも抽出するプログラムを作成しましょう。

ページからタイトルを抽出する方法はすでに知っているので、ノートパソコンの次のページに移動する方法を考えるだけです。

現在のページにはページネーションボタンがあることに気づいているかもしれません。

スクレイピングプログラムで2を特定してクリックする必要があります。ページを検証すると、必要な要素はリスト要素(<li>タグ)で、内部テキストが2であることがわかります:

必要な要素の内部テキストは2

つまり、get_by_role()セレクターでリストアイテムを検索し、get_by_text()セレクターで2というテキストを持つ要素を検索できます。

ファイルへのコーディング方法は次のとおりです:

page.get_by_role("listitem").get_by_text("2", exact=True)

これは2つの条件に一致する要素を検索します:まずリストアイテムであること、次に2というテキストを持つことです。

exact=Trueは指定されたテキストを持つ要素を検索するための関数引数です。

ボタンをクリックするには、前のコードを次のように変更します:

        page.get_by_role("listitem").get_by_text("2", exact=True).click()

このコードでは、click()関数が指定された要素をクリックします。

ページが読み込まれるのを待ち、再びすべてのタイトルを抽出します:

        page.wait_for_timeout(5000)

        titles = page.get_by_role("heading").all()

        for title in titles:
            laptop = title.locator("a.title").all_inner_texts()
            if len(laptop) == 1:
                print(laptop[0])

完全なコードブロックは次のようになります:

from playwright.sync_api import sync_playwright

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        try:
            page.goto("https://webscraper.io/test-sites/e-commerce/static/computers/laptops")

        except:
            print("Error")

        page.wait_for_timeout(7000)

        titles = page.get_by_role("heading").all()

        for title in titles:
            laptop = title.locator("a.title").all_inner_texts()
            if len(laptop) == 1:
                print(laptop[0])
        
        page.get_by_role("listitem").get_by_text("2", exact=True).click()

        page.wait_for_timeout(5000)

        titles = page.get_by_role("heading").all()

        for title in titles:
            laptop = title.locator("a.title").all_inner_texts()
            if len(laptop) == 1:
                print(laptop[0])

main()

HTMLを抽出してCSVに書き込む

スクレイピングしたデータを保存・分析しなければ意味がありません。このセクションでは、スクレイピングするノートパソコンのページ数をユーザーが入力し、タイトルを抽出してプロジェクトフォルダのCSVファイルに保存する高度なプログラムを作成します。

このプログラムには、次のコマンドでインポートできるプリインストールのCSVライブラリが必要です:

import csv

CSVライブラリをインストールしたら、ユーザーの入力に応じて可変数のページにアクセスする方法を考える必要があります。

ウェブサイトのURL構造を見ると、ノートパソコンの各ページがURLパラメーターで示されていることがわかります。例えば、ノートパソコンディレクトリの2ページ目のURLはhttps://webscraper.io/test-sites/e-commerce/static/computers/laptops?page=2です。

URLパラメーター?page=2を異なる数値に変更することで、異なるページにアクセスできます。つまり、次のコマンドでスクレイピングするページ数をユーザーに入力させる必要があります:

pages = int(input("enter the number of pages to scrape: "))

ユーザーが入力したページ数の1から最後まで各ページにアクセスするには、次のようにforループを使用します:

for i in range(1, pages+1):

このrange関数では、ループの開始値と終了値を表す関数引数として1pages+1を使用します。2番目の関数引数はループから除外されます。例えば、range関数がrange(1,5)の場合、プログラムは1から4までしかループしません。

次に、イテレーションでiの値をURLパラメーターとして入力して各ページにアクセスする必要があります。Python f-stringsを使って変数を文字列に追加できます。

文字列を出力する際、引用符の前にfを付けてf-stringであることを示します。引用符の中では、波括弧を使って変数を示すことができます。

f-stringsを使って変数と文字列を一緒に出力する例を示します:

print(f"The value of the variable is {variable_name_goes_here}")

スクレイパーに戻り、ファイルに次のコードブロックを記述してf-stringsを活用できます:

     try:
             page.goto(f"https://webscraper.io/test-sites/e-commerce/static/computers/laptops?page={i}")

     except:
            print("Error")

タイムアウト関数でページの読み込みを待ち、次のコードでタイトルを抽出します:

            page.wait_for_timeout(7000)

            titles = page.get_by_role("heading").all()

すべてのタイトル要素を取得したら、CSVファイルを開き、各タイトルをループして必要なテキストを抽出し、ファイルに書き込む必要があります。

CSVファイルを開くには、次の構文を使用します:

with open("laptops.csv", "a") as csvfile:

ここでは、追加(a)モードでlaptops.csvファイルを開いています。ファイルを開くたびに古いデータを失わないよう追加モードを使用します。ファイルが存在しない場合、ライブラリはプロジェクトフォルダに新しいファイルを作成します。CSVにはいくつかのモードがあり、次のものが含まれます:

  • r は何も指定しない場合のデフォルトオプションで、読み取り専用でファイルを開きます。
  • w は書き込み専用でファイルを開きます。ファイルを開くたびに以前のデータが上書きされます。
  • a はデータを追加するためにファイルを開きます。以前のデータは上書きされません。
  • r+ は読み取りと書き込みの両方のためにファイルを開きます。
  • x は新しいファイルを作成します。

前のコードの下に、CSVファイルを操作できるwriterオブジェクトを宣言する必要があります:

writer = csv.writer(csvfile)

次に、各タイトル要素をループしてテキストを抽出します:

                for title in titles:
                    laptop = title.locator("a.title").all_inner_texts()

これにより、個々のノートパソコンのタイトルを含む複数の配列が得られます。空の配列を除外してCSVファイルに書き込むには、次の条件コードを記述します:

               if len(laptop) == 1:
                        writer.writerow([laptop[0]])

writerow関数を使ってCSVファイルに新しい行を書き込むことができます。

プログラムの完全なコードは次のとおりです:

from playwright.sync_api import sync_playwright
import csv

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        pages = int(input("enter the number of pages to scrape: "))

        for i in range(1, pages+1):
            try:
                page.goto(f"https://webscraper.io/test-sites/e-commerce/static/computers/laptops?page={i}")

            except:
                print("Error")

            page.wait_for_timeout(7000)

            titles = page.get_by_role("heading").all()

            with open("laptops.csv", "a") as csvfile:
                writer = csv.writer(csvfile)
                for title in titles:
                    laptop = title.locator("a.title").all_inner_texts()
                    if len(laptop) == 1:
                        writer.writerow([laptop[0]])

        browser.close()             


main()

このコードを実行すると、CSVファイルは次のようになります:

CSVファイルの例

まとめ

この記事では、Pythonを使ってHTMLを抽出・パース・保存する方法を学びました。

このチュートリアルは比較的シンプルでしたが、実際のシナリオでは、CAPTCHA、レート制限、ウェブサイトレイアウトの変更、規制要件など、さまざまな障壁に直面する可能性があります。幸い、Bright Dataがお役に立てます。Bright Dataはスクレイピングを改善する高度なレジデンシャルプロキシ、大規模なスクレイパー構築のためのWeb Scraper IDE、CAPTCHAの解決を含む公開ウェブサイトのブロック解除のためのWeb Unblockerなどのツールを提供しています。これらのツールは大規模な正確なデータ収集と障壁の克服に役立ちます。さらに、Bright Dataの倫理的スクレイピングへのコミットメントにより、ウェブサイトの利用規約と法的規制の範囲内で活動できます。

Bright Dataの機能豊富なスクレイピングインフラを活用すれば、ウェブスクレイピングの複雑さを気にせず、必要な価値あるデータの抽出に集中できます。今すぐ無料トライアルを始めましょう!