Skip to content

PHPでHTMLを解析する方法(ネイティブDOM、Simple HTML DOM Parser、SymfonyのDomCrawlerを使用)—それぞれの強みとユースケースの比較。

Notifications You must be signed in to change notification settings

bright-jp/php-html-parsing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 

Repository files navigation

PHPでのHTMLパース

Promo

本ガイドでは、PHPでHTMLをパースする3つのテクニックを検証し、それぞれの強みと違いを比較します。

なぜPHPでHTMLをパースするのか?

PHPでのHTMLパースとは、HTMLコンテンツをDOM(Document Object Model)構造に変換することです。DOM形式になれば、HTMLコンテンツのナビゲーションや操作を簡単に行えます。

特に、PHPでHTMLをパースする主な理由は次のとおりです。

  • データ抽出:HTML要素のテキストや属性など、Webページから特定のコンテンツを取得します。
  • 自動化:コンテンツのスクレイピング、レポート作成、HTMLからのデータ集約といったタスクを効率化します。
  • サーバーサイドでのHTML処理:アプリケーションでレンダリングする前に、HTMLをパースしてクリーンアップ、整形、変更します。

前提条件

コーディングを始める前に、マシンにPHP 8.4+がインストールされていることを確認してください。以下のコマンドを実行すると確認できます。

php -v

出力は次のようになります。

PHP 8.4.3 (cli) (built: Jan 19 2025 14:20:58) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.3, Copyright (c), by Zend Technologies

次に、依存関係の管理を簡単にするためにComposerプロジェクトを初期化します。Composerがシステムにインストールされていない場合は、ダウンロードしてインストール手順に従ってください。

まず、PHPのHTMLプロジェクト用に新しいフォルダを作成します。

mkdir php-html-parser

ターミナルでそのフォルダに移動し、composer initコマンドで中にComposerプロジェクトを初期化します。

composer init

このプロセスではいくつか質問されます。デフォルトの回答で問題ありませんが、必要に応じて、PHPのHTMLパースプロジェクトに合わせてより具体的な内容を指定することもできます。

次に、お好みのIDEでプロジェクトフォルダを開きます。PHP開発には、PHP拡張機能付きVisual Studio CodeまたはIntelliJ WebStormが良い選択肢です。

続いて、プロジェクトフォルダに空のindex.phpファイルを追加します。プロジェクト構成は次のようになります。

php-html-parser/
  ├── vendor/
  ├── composer.json
  └── index.php

index.phpを開き、プロジェクトを初期化するために次のコードを追加してください。

<?php

require_once __DIR__ . "/vendor/autoload.php";

// scraping logic...

次のコマンドでスクリプトを実行します。

php index.php

PHPでのHTML取得

PHPでHTMLをパースする前に、パースするHTMLが必要です。本セクションでは、PHPでHTMLコンテンツにアクセスする2つの異なるアプローチを見ていきます。あわせて、PHPでのWebスクレイピングガイドも読むことをおすすめします。

CURLを使用する

PHPは、HTTPリクエストを実行するために一般的に使われるHTTPクライアントであるcURLをネイティブでサポートしています。cURL拡張を有効化するか、Ubuntu Linuxでは次のコマンドでインストールしてください。

sudo apt-get install php8.4-curl

cURLを使用すると、オンラインサーバーへHTTP GETリクエストを送信し、サーバーから返されるHTMLドキュメントを取得できます。次の例のスクリプトは、シンプルなGETリクエストを行い、HTMLコンテンツを取得します。

// initialize cURL session
$ch = curl_init();

// set the URL you want to make a GET request to
curl_setopt($ch, CURLOPT_URL, "https://www.scrapethissite.com/pages/forms/?per_page=100");

// return the response instead of outputting it
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// execute the cURL request and store the result in $response
$html = curl_exec($ch);

// close the cURL session
curl_close($ch);

// output the HTML response
echo $html;

上記のコードスニペットをindex.phpに追加して実行してください。次のHTMLコードが生成されます。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hockey Teams: Forms, Searching and Pagination | Scrape This Site | A public sandbox for learning web scraping</title>
    <link rel="icon" type="image/png" href="/static/images/scraper-icon.png" />
    <!-- Omitted for brevity... -->
</html>

ファイルから取得する

cURLを使って事前に取得した、Scrape This Siteの「Hockey Teams」ページのHTMLを含むindex.htmlというファイルがあると仮定します。

The index.html file in the project folder

PHPでのHTMLパース:3つのアプローチ

本セクションでは、PHPでHTMLをパースするために3つの異なるライブラリを使用する方法を説明します。

  1. 素のPHP向けにDom\HTMLDocumentを使用
  2. Simple HTML DOM Parserライブラリを使用
  3. SymfonyのDomCrawlerコンポーネントを使用

いずれのケースでも、ローカルのindex.htmlファイルからHTMLをパースし、ページ上のすべてのホッケーチームのエントリを選択して、そこからデータを抽出します。

The table on the target page

最終結果は、次の詳細を含むスクレイピング済みホッケーチームのエントリ一覧になります。

  • Team Name
  • Year
  • Wins
  • Losses
  • Win %
  • Goals For (GF)
  • Goals Against (GA)
  • Goal Difference

これらは、次の構造を持つHTMLテーブルから抽出できます。

The HTML DOM structure of the table's rows

テーブル行の各カラムには固有のclassが付いており、classをCSSセレクタとして要素を選択し、テキストとして内容を取得することでデータを抽出できます。

アプローチ #1:Dom\HTMLDocumentを使用

PHP 8.4+ には組み込みのDom\HTMLDocumentクラスがあります。これはHTMLドキュメントを表し、HTMLコンテンツのパースやDOMツリーのナビゲーションを可能にします。

ステップ #1:インストールとセットアップ

Dom\HTMLDocumentStandard PHP Libraryの一部です。ただし、使用するにはDOM拡張を有効化するか、次のLinuxコマンドでインストールする必要があります。

sudo apt-get install php-dom

ステップ #2:HTMLパース

HTML文字列は次のようにパースできます。

$dom = \DOM\HTMLDocument::createFromString($html);

index.htmlファイルは次のようにパースできます。

$dom = \DOM\HTMLDocument::createFromFile("./index.html");

$domDom\HTMLDocumentオブジェクトで、データパースに必要なメソッドが提供されます。

ステップ #3:データパース

次のアプローチで、\DOM\HTMLDocumentを使ってすべてのホッケーチームのエントリを選択できます。

// select each row on the page
$table = $dom->getElementsByTagName("table")->item(0);
$rows = $table->getElementsByTagName("tr");

// iterate through each row and extract data
foreach ($rows as $row) {
  $cells = $row->getElementsByTagName("td");

  // extracting the data from each column
  $team = trim($cells->item(0)->textContent);
  $year = trim($cells->item(1)->textContent);
  $wins = trim($cells->item(2)->textContent);
  $losses = trim($cells->item(3)->textContent);
  $win_pct = trim($cells->item(5)->textContent);
  $goals_for = trim($cells->item(6)->textContent);
  $goals_against = trim($cells->item(7)->textContent);
  $goal_diff = trim($cells->item(8)->textContent);

  // create an array for the scraped team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print ("\n");
}

\DOM\HTMLDocumentは高度なクエリメソッドを提供しません。そのため、getElementsByTagName()のようなメソッドと手動の反復処理に依存する必要があります。

使用したメソッドの内訳は次のとおりです。

  • getElementsByTagName():ドキュメント内の指定タグ(<table><tr><td>など)の全要素を取得します。
  • item()getElementsByTagName()が返す要素リストから個別要素を返します。
  • textContent:要素の生のテキスト内容を返すプロパティで、表示されるデータ(チーム名、年など)を抽出できます。

また、テキスト内容の前後にある余分な空白を取り除いてデータを整えるために、trim()も使用しました。

上記スニペットをindex.phpに追加すると、次の結果が得られます。

Array
(
    [team] => Boston Bruins
    [year] => 1990
    [wins] => 44
    [losses] => 24
    [win_pct] => 0.55
    [goals_for] => 299
    [goals_against] => 264
    [goal_diff] => 35
)

// omitted for brevity...

Array
(
    [team] => Detroit Red Wings
    [year] => 1994
    [wins] => 33
    [losses] => 11
    [win_pct] => 0.688
    [goals_for] => 180
    [goals_against] => 117
    [goal_diff] => 63
) 

アプローチ #2:Simple HTML DOM Parserを使用

Simple HTML DOM Parserは軽量なPHPライブラリで、HTMLコンテンツのパースと操作を簡単にします。

ステップ #1:インストールとセットアップ

次のコマンドで、Composer経由でSimple HTML Dom Parserをインストールできます。

composer require voku/simple_html_dom

または、simple_html_dom.phpファイルを手動でダウンロードしてプロジェクトに含めることもできます。

次に、このコード行でindex.phpにインポートします。

use voku\helper\HtmlDomParser;

ステップ #2:HTMLパース

HTML文字列をパースするには、file_get_html()メソッドを使用します。

$dom = HtmlDomParser::str_get_html($html);

index.htmlをパースする場合は、代わりにfile_get_html()を書きます。

$dom = HtmlDomParser::file_get_html($str);

これによりHTMLコンテンツが$domオブジェクトに読み込まれ、DOMを簡単に辿れるようになります。

ステップ #3:データパース

Simple HTML DOM Parserを使用して、HTMLからホッケーチームのデータを抽出します。

// find all rows in the table
$rows = $dom->findMulti("table tr.team");

// loop through each row to extract the data
foreach ($rows as $row) {
  // extract data using CSS selectors
  $team_element = $row->findOne(".name");
  $team = trim($team_element->plaintext);

  $year_element = $row->findOne(".year");
  $year = trim($year_element->plaintext);

  $wins_element = $row->findOne(".wins");
  $wins = trim($wins_element->plaintext);

  $losses_element = $row->findOne(".losses");
  $losses = trim($losses_element->plaintext);

  $win_pct_element = $row->findOne(".pct");
  $win_pct = trim($win_pct_element->plaintext);

  $goals_for_element = $row->findOne(".gf");
  $goals_for = trim($goals_for_element->plaintext);

  $goals_against_element = $row->findOne(".ga");
  $goals_against = trim(string: $goals_against_element->plaintext);

  $goal_diff_element = $row->findOne(".diff");
  $goal_diff = trim(string: $goal_diff_element->plaintext);

  // create an array with the extracted team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print("\n");
}

上記で使用したSimple HTML DOM Parserの機能は次のとおりです。

  • findMulti():指定したCSSセレクタで識別されるすべての要素を選択します。
  • findOne():指定したCSSセレクタに一致する最初の要素を特定します。
  • plaintext:HTML要素内部の生のテキスト内容を取得するための属性です。

今回は、より包括的で堅牢なロジックでCSSセレクタを適用しましたが、結果は最初のPHPでのHTMLパースアプローチと同じです。

アプローチ #3:SymfonyのDomCrawlerコンポーネントを使用

SymfonyのDomCrawlerコンポーネントは、HTMLドキュメントを簡単にパースし、そこからデータを抽出できるようにします。

Note: このコンポーネントはSymfony frameworkの一部ですが、本セクションで行うようにスタンドアロンでも使用できます。

ステップ #1:インストールとセットアップ

次のComposerコマンドでSymfonyのDomCrawlerコンポーネントをインストールします。

composer require symfony/dom-crawler

次に、index.phpファイルにインポートします。

use Symfony\Component\DomCrawler\Crawler;

ステップ #2:HTMLパース

HTML文字列をパースするには、html()メソッドでCrawlerインスタンスを作成します。

$crawler = new Crawler($html);

ファイルをパースする場合は、file_get_contents()を使用してCrawlerインスタンスを作成します。

$crawler = new Crawler(file_get_contents("./index.html"));

上記の行によりHTMLコンテンツが$crawlerオブジェクトに読み込まれ、データの走査と抽出を容易にするメソッドが提供されます。

ステップ #3:データパース

DomCrawlerコンポーネントを使用してホッケーチームのデータを抽出します。

// select all rows within the table
$rows = $crawler->filter("table tr.team");

// loop through each row to extract the data
$rows->each(function ($row, $i) {
  // extract data using CSS selectors
  $team_element = $row->filter(".name");
  $team = trim($team_element->text());

  $year_element = $row->filter(".year");
  $year = trim($year_element->text());

  $wins_element = $row->filter(".wins");
  $wins = trim($wins_element->text());

  $losses_element = $row->filter(".losses");
  $losses = trim($losses_element->text());

  $win_pct_element = $row->filter(".pct");
  $win_pct = trim($win_pct_element->text());

  $goals_for_element = $row->filter(".gf");
  $goals_for = trim($goals_for_element->text());

  $goals_against_element = $row->filter(".ga");
  $goals_against = trim($goals_against_element->text());

  $goal_diff_element = $row->filter(".diff");
  $goal_diff = trim($goal_diff_element->text());

  // create an array with the extracted team data
  $team_data = [
    "team" => $team,
    "year" => $year,
    "wins" => $wins,
    "losses" => $losses,
    "win_pct" => $win_pct,
    "goals_for" => $goals_for,
    "goals_against" => $goals_against,
    "goal_diff" => $goal_diff
  ];

  // print the scraped team data
  print_r($team_data);
  print ("\n");
});

使用したDomCrawlerのメソッドは次のとおりです。

  • each():選択した要素のリストを反復処理します。
  • filter():CSSセレクタに基づいて要素を選択します。
  • text():選択した要素のテキスト内容を抽出します。

PHPでのHTMLパース:比較表

ここで取り上げたPHPでHTMLをパースする3つのアプローチを、以下のサマリ表で比較できます。

\DOM\HTMLDocument Simple HTML DOM Parser Symfony’s DomCrawler
Type PHPネイティブコンポーネント 外部ライブラリ Symfonyコンポーネント
GitHub Stars 880+ 4,000+
XPath Support ✔️ ✔️
CSS Selector Support ✔️ ✔️
Learning Curve 低〜中
Simplicity of Use
API 基本 充実 充実

結論

これらのソリューションは動作しますが、ターゲットのWebページがレンダリングにJavaScriptを依存している場合は効果的ではありません。そのようなケースでは、上記のようなシンプルなHTMLパース手法だけでは不十分です。代わりに、Scraping Browserのような、高度なHTMLパース機能を備えたフル機能のスクレイピングブラウザが必要になります。

HTMLパースを省略して構造化データへ即時アクセスしたい場合は、数百のWebサイトを網羅するすぐに使えるデータセットをご覧ください。

今すぐBright Dataアカウントを作成し、無料トライアルで当社のデータおよびスクレイピングソリューションのテストを開始しましょう!

About

PHPでHTMLを解析する方法(ネイティブDOM、Simple HTML DOM Parser、SymfonyのDomCrawlerを使用)—それぞれの強みとユースケースの比較。

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published