2023.08.11
【PHP】スクレイピングの基礎

Larisa KoshkinaによるPixabayからの画像

はじめに

ちょっと気が向いてPHPでスクレイピングのやり方を調べたので、メモとしてここにまとめます。

この記事では基礎のみをシンプルにまとめているので、すぐに応用できるかと思います。

スクレイピングについて以下のサイトを参考にさせていただきました。
https://qiita.com/rllllho/items/cb1187cec0fb17fc650a

動作確認した環境

OS:Windows11 / WSL(Ubuntu20.04)
php:v7.4.33

前提

スクレイピングをやってみる

お試し:ページをまるまる取得して表示してみる

以下の1行で対象のページのHTMLをまるまる取得できます。

file_get_contents("[対象ページのURL]");

試しにBootstrapのページのHTMLを取得して、表示させてみます。

<?php
  $html = file_get_contents("https://getbootstrap.jp/");
  echo $html;

こんな感じで表示されました。

レイアウトは崩れていても、HTML要素を取得できています。

しかし、これだけでは必要な要素のみ取得することはできません。

スクレイピング実践

では実際にスクレイピングをやってみます。

取得する要素の指定方法は若干難しいので、この後にご説明します。

表示については、下のコードにあるように、ループとインデックス番号で指定して表示する方法だけ知っていればどうにかなるんじゃないかと思っています。

<?php

    //↓↓ ------スクレイピングの準備------ ↓↓
  $dom = new DOMDocument('1.0', 'TUF-8');
  $html = file_get_contents('https://getbootstrap.jp/');  //⇐取得するページのURLに変える
  $html = mb_convert_encoding($html, "HTML-ENTITIES", auto);
  @$dom->loadHTML($html);
  $xpath = new DOMXpath($dom);
    //↑↑ ------スクレイピングの準備------ ↑↑


//対象ページのh2要素を取得 ※取得した値は配列になっています。
$elements = $xpath->query("//h2");  //⇐ここについては後程説明します。

//ループで取得した全要素を表示してみる。
foreach($elements as $node){
    echo "<p>", $node->nodeValue, '</p>';
}

echo '<br><br>'; //⇐これは表示を見やすくしているだけなので気にしないでください。。

//インデックス番号で指定して表示してみる。
echo $elements[0]->nodeValue;

自在に欲しい要素を取得する($xpath->query(“[この部分の書き方]”))

下の赤字の部分のことをロケーションパスを言うそうなのですが、この部分の書き方について以下でご説明します。

$xpath->query(“[この部分]“)

ロケーションパスのシンプルな書き方

このシンプルな書き方だけで、だいぶ自由に要素を取得できます。

■直下(子要素のみ):/

例)$xpath->query("//footer/ul/li");

■より下(子孫要素全て)://

例)$xpath->query("//footer//li");

■属性で指定して要素を取得:@属性='[値]’

$xpath->query("//footer//div[@class='row']");

■任意文字列で要素を取得:contains

例)classに'wrapper'を持つdiv要素を取得
 $xpath->query("//footer/div[contains(@class, 'wrapper')]");

例)「<div>品番</div>」のように'品番'を含むdivを取得する
 $xpath->query("//footer/div[contains(text(), '品番']");

例)任意文字を含むJavaScriptを取得する
 $xpath->query("//script[contains(test(), 'stoch']");

■n番目の要素を取得:position

例)3番目のli要素を取得する ※省略形:li[position()=3 ⇒ li[3]
 $xpath->query("//footer/li[position()=3]");

例)2番目以降のli要素を取得する。
 $xpath->query("//footer/li[position()>1]");

■任意要素の任意の属性を取得:/@[属性]

例)a要素のhref属性の値を取得する
 $xpath->query("//footer//a/@href");

■要素内のテキストを取得:text()

例)p要素のテキストを取得する
 $xpath->query("//footer//p/text()");

Not, Or, And

■式の否定:not

例)$xpath->query("//footer/img[not(contains(@src, 'main'))]/@src");

■または:or

例)$xpath->query("//footer//a[contains(@href, 'https') or contains(@href, '/home')]");

■かつ:and

例)$xpath->query("//footer//a[contains(@href, 'https') and contains(@class, 'btn')]");

ロケーションパスの詳細な書き方

上で記載した「ロケーションパスのシンプルな書き方」は以下で記載する「詳細な書き方」の省略形です。

ロケーションパスを詳細に書くと、以下のように構成されます。

::ノードテスト[式]
記述例)$xpath->query("/descendant-or-self::footer/child::div[contains(@class,'wrapper')]");

赤字で書いてある「軸」が増えただけですね。

軸の主なものを以下の表にまとめます。

内容
selfノード自身
childノードの子ノード
parentノードの親ノード
ancestorノードの祖先ノード(親も含む)
descendantノードの子孫ノード
ancestor-or-selfノード自身とその祖先ノードの集合
descendant-or-selfノード自身とその子孫ノードの集合
precending-sibling同じ階層にあり、かつ前に出てくる兄弟ノード
following-sibling同じ階層にあり、かつ後に出てくる兄弟ノード