hacca8

PHPで利用するXPathメモ

PHPでクローラー作成やスクレイピングする際に、XPathの記述をよく忘れるのでメモします。
XPathについては、他の言語でも大体似たような記述です。

XPath

  • HTMLソースをXPathに変換
function toXPath($source)
{
    $result = new DOMDocument;
    @$result->loadHTML($source);
    return new DOMXPath($result);
}

適当なクラスに組み込んで利用します。

特定の文字列を持っているノードを取得

  • targetという文字列が含まれるdiv
//div[contains(text(), 'target')]
  • class属性がtarget target2のdiv
//div[@class='target target2']
  • name属性にtargetという文字列が含まれるノード
//*[contains(@name, 'target')]

and、or、not

  • href属性に https:// を含み、targe属性が_blankのa
//a[contains(@href, 'https://') and @target='_blank']
  • href属性か、src属性に http:// を含むノード
//*[contains(@href, 'http://') or contains(@src, 'http://')]
  • href属性に https:// を含まないa
//a[not(contains(@href, 'http://'))]

N番目のノードを取得

  • tableタグ内の2番目のtd
//table//td[position()=2]
  • tableタグ内の2番目以降のtd
//table//td[position()>2]
  • tableタグ内の最後のtd
//table//td[position()=last()]

ノードの階層から指定して取得

  • h3の後ろにある1つ目のtable
//h3/following-sibling::table[1]
  • tableの前にある1つ目のh3
//table/preceding-sibling::h3[1]
  • targetという文字列が含まれるtdの親tr
# 該当のtrのみ
//td[contains(text(), 'target')]/parent::tr

# 該当のtrの兄弟も取得
//td[contains(text(), 'target')]/../../tr

存在するか

  • class属性にpriceを含むノードが存在するか
function hasClassNamePrice($xpath)
{
    return $xpath->query("//*[contains(@class, 'price')]")['length'] ? true : false;
}

該当タグ内のテキストや、属性を出力

  • targetという文字列が含まれる1番目のdivのテキストを出力
echo $xpath->query("//div[contains(text(), 'target')]")->item(0)->textContent;
  • targetという文字列が含まれる2番目のdivのclass属性を出力
echo $xpath->query("//div[contains(text(), 'target')]")->item(1)->getAttribute('class');
// もしくは
echo $xpath->query("//div[contains(text(), 'target')]/@class")->item(1)->textContent;

HTMLに変換する

function XPathToHTML($node)
{
    $result = '';
    $children = $node->childNodes;
    foreach ($children as $value) {
        $result .= $node->ownerDocument->saveHTML($value);
    }
    return $result;
}