CakePHP 4 で HTML の解析(パース)を行う方法

はじめに
Web 開発を行っていると、クローラのスクレイピングや既存データの再利用などで HTML 解析を行うことがあります。
今日は CakePHP 4 で URL や ファイル から HTML を解析する方法をご紹介します。
今回使用したのは CakePHP 4.0.7 です。
1. 下準備
今回は解析対象として下記 HTML ファイルを作成しました。
サーバ内にあるファイルの読取りを想定し /data フォルダに置いています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<table>
<tr class="item fruit">
<th>オレンジ</th>
<td class="price" data-value="200">200円</td>
</tr>
<tr class="item fruit">
<th>ぶどう</th>
<td class="price" data-value="400">400円</td>
</tr>
<tr class="item drink">
<th>牛乳</th>
<td class="price" data-value="150">150円</td>
</tr>
</table>
</body>
</html>
インターネット上に公開されているデータを解析する場合でも、相手サーバ負荷軽減のため、開発時にはその HTML データを保存して解析した方がいいと考えています。
例えば、解析対象のパスや URL を app_local.php に追加することで、開発時と運用時とで簡単に切り替えることができます。
...
return [
'sampleUrl' => ROOT . DS . 'data' . DS . 'target.html',
...
2. HTML 解析
HTML の解析は以下のように実装することができます。
先に紹介した HTML ファイルから、フルーツの名前と価格を取得しています。
<?php
declare(strict_types=1);
namespace App\Controller;
// ▼ これらを追加
use Cake\Core\Configure;
use Cake\Utility\Xml;
use DOMXPath;
class SamplesController extends AppController
{
public function index()
{
// HTML 文字列読込
$url = Configure::read('sampleUrl');
$htmlString = file_get_contents($url);
// DOMXPath オブジェクトを生成
$html = Xml::loadHtml($htmlString, ['return' => 'domdocument']);
$xpath = new DOMXPath($html);
// 商品情報を取得
$fruitNodes = $xpath->query('//*[contains(@class, "fruit")]');
$fruits = [];
foreach ($fruitNodes as $fruitNode) {
$fruits[] = [
'name' => $xpath->query('./th', $fruitNode)
->item(0)
->nodeValue,
'price' => $xpath->query('./td[@class="price"]', $fruitNode)
->item(0)
->getAttribute('data-value')
];
}
debug($fruits);
exit;
}
}
[
(int) 0 => [
'name' => 'オレンジ',
'price' => '200'
],
(int) 1 => [
'name' => 'ぶどう',
'price' => '400'
]
]
app_local.php の sampleUrl を書き換えることで、解析対象を簡単に切り替えることができます。sampleUrl はサーバ内のファイルパスでも、外部 URL でも可能です。
CakePHP 特有の機能というのはほとんどなく、ほぼ PHP 標準の DOMXPath を使って行っています。Xml::loadHtml では DOMDocument を取得しています。
下記公式ドキュメントを参考にするときには DOMElement は DOMNode を継承していることを頭に置いておくと理解しやすいと思います。
- Loading HTML documents (CakePHP 4.x Cookbook)
- https://book.cakephp.org/4/en/core-libraries/xml.html#loading-html-documents
- PHP: DOMDocument - Manual
- https://www.php.net/manual/ja/class.domdocument.php
- PHP: DOMXPath - Manua
- https://www.php.net/manual/ja/class.domxpath.php
- PHP: DOMElement - Manual
- https://www.php.net/manual/ja/class.domelement.php
また DOMXPath の query() では XPath という構文をつかいます。検索サイトを使えば様々な参考文献が見つかると思います。
僕は下記の記事を参考にしました。とても分かりやすいと思います。
- クローラ作成に必須!XPATHの記法まとめ - Qiita
- https://qiita.com/rllllho/items/cb1187cec0fb17fc650a
3. おわりに
PHP 標準の機能が十分ということなのか、CakePHP でも HTML 解析には DOMDocument を使っています。
サンプルにある Xml::loadHtml() の箇所も、今のところ使うメリットが分からず、PHP 標準の DOMDocument クラスを使って書いても良いのかなとも思っています。