Wikipediaからアニメのサブタイトル一覧をXPath一発で抜き出す

# はじめに

知恵袋で[質問](http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14145873884)があったので<del>明日のTOEICの勉強サボって</del>書いてみました。Simple HTML DOM Parser は使うメリット皆無なので使ってません。

# コード

```php
function scrape_wikipedia_subtitles($title) {
    static $query = '
        //table[tr/th[contains(.,"サブタイトル")]]
        /tr
        /td[position()=count(../../tr/th[contains(.,"サブタイトル")]/preceding-sibling::*)+1]
        |
        //table[tbody/tr/th[contains(.,"サブタイトル")]]
        /tbody
        /tr
        /td[position()=count(../../../tr/th[contains(.,"サブタイトル")]/preceding-sibling::*)+1]
    ';
    static $ch;
    static $stringify;
    if (!$ch) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $stringify = function (\DOMNode $node) { return $node->nodeValue; };
        libxml_use_internal_errors(true);
    }
    $dom = new \DOMDocument;
    curl_setopt($ch, CURLOPT_URL, 'http://ja.wikipedia.org/wiki/' . urlencode($title));
    $dom->loadHTML(curl_exec($ch));
    libxml_clear_errors();
    $xpath = new \DOMXPath($dom);
    return array_map($stringify, iterator_to_array($xpath->query($query)));
}
```

- cURLでリクエストを行うようにし、また`@`演算子を使わないことで、パフォーマンス面への考慮をしました。
- 「サブタイトル」は2列目にあることが多いようですが、相対的に列を決定するので、多少表の構成が変わっていても正常にスクレイピング出来ます。
- 「サブタイトル」を完全一致ではなく部分一致で取るようにし、但し書きなどがあった場合にも対応出来るようにしました。
- 現状`tbody`は使用されていないようですが、今後仕様変更があった場合にも対応出来るようにしました。

# 使用例

```php
var_dump(scrape_wikipedia_subtitles('にゃんこい!'));
/*
array(12) {
  [0]=>
  string(42) "ブサイクな猫と呪われし高校生"
  [1]=>
  string(17) "その男 下僕?"
  [2]=>
  string(15) "キミの名は"
  [3]=>
  string(12) "美しい人"
  [4]=>
  string(54) "四角関係の刻（タイムズ・スクウェア）"
  [5]=>
  string(45) "ミルク&ビター&シュガー&スパイス"
  [6]=>
  string(30) "暗くなるまで待って♡"
  [7]=>
  string(30) "炎の個人教授ランナー"
  [8]=>
  string(45) "ガールズ・イン・ザ・ウォーター"
  [9]=>
  string(21) "ある夜の出来事"
  [10]=>
  string(12) "フレンズ"
  [11]=>
  string(28) "天国は待ってくれる?"
}
*/
```

```php
var_dump(scrape_wikipedia_subtitles('新妹魔王の契約者'));
/*
array(12) {
  [0]=>
  string(18) "妹ができた日"
  [1]=>
  string(24) "初めての主従契約"
  [2]=>
  string(24) "再会と信頼の狭間"
  [3]=>
  string(33) "悲しみがゼロになるまで"
  [4]=>
  string(24) "朝の妹の魔王無双"
  [5]=>
  string(24) "募る思いを抱えて"
  [6]=>
  string(18) "恩讐の果てに"
  [7]=>
  string(30) "暴走のエロサキュバス"
  [8]=>
  string(15) "主従の功罪"
  [9]=>
  string(18) "哀切なる背信"
  [10]=>
  string(66) "諜報者（エスビオナージ）……その先にあるもの"
  [11]=>
  string(36) "この夜、このときのために"
}
*/
```

```php
var_dump(scrape_wikipedia_subtitles('やはり俺の青春ラブコメはまちがっている。'));
/*
array(22) {
  [0]=>
  string(57) "こうして彼らのまちがった青春が始まる。"
  [1]=>
  string(63) "きっと、誰しも等し並みに悩みを抱えている。"
  [2]=>
  string(57) "たまにラブコメの神様はいいことをする。"
  [3]=>
  string(39) "つまり、彼は友達が少ない。"
  [4]=>
  string(54) "またしても、彼は元来た道へ引き返す。"
  [5]=>
  string(51) "ようやく彼と彼女の始まりが終わる。"
  [6]=>
  string(72) "ともあれ、夏休みなのに休めないのは何かおかしい。"
  [7]=>
  string(45) "いずれ彼ら彼女らは真実を知る。"
  [8]=>
  string(45) "三度、彼は元来た道へ引き返す。"
  [9]=>
  string(91) "依然として彼らの距離は変わらずに、
祭りはもうすぐカーニバる。"
  [10]=>
  string(106) "そして、それぞれの舞台の幕が上がり、
祭りは最高にフェスティバっている。"
  [11]=>
  string(69) "それでも彼と彼女と彼女の青春はまちがい続ける。"
  [12]=>
  string(48) "だから、彼らの祭りは終わらない。"
  [13]=>
  string(103) "こちらとしても彼ら彼女らの行く末に
幸多からんことを願わざるを得ない。"
  [14]=>
  string(63) "何故、彼らが奉仕部に来たのか誰も知らない。"
  [15]=>
  string(48) "彼と彼女の告白は誰にも届かない。"
  [16]=>
  string(45) "静かに、雪ノ下雪乃は決意する。"
  [17]=>
  string(48) "そして、由比ヶ浜結衣は宣言する。"
  [18]=>
  string(57) "その部屋には、紅茶の香りはもうしない。"
  [19]=>
  string(57) "つつがなく、会議は踊り、されど進まず。"
  [20]=>
  string(66) "されど、その部屋は終わらぬ日常を演じ続ける。"
  [21]=>
  string(36) "それでも、比企谷八幡は。"
}
*/
```

2クールまとまってるやつも一緒に抜き出しちゃうので、クール分けまでやろうと思ったらもうちょっとしっかり書く必要がありますね…

```php
var_dump(scrape_wikipedia_subtitles('女王の教室'));
/*
array(13) {
  [0]=>
  string(79) "悪魔のような鬼教師に戦いを挑んだ六年生の一年間の記録!"
  [1]=>
  string(82) "鬼教師の目に涙!? 子役の秘められた過去と哀しい友情の告白!"
  [2]=>
  string(94) "親友・裏切り・涙。小学校最後の思い出…先生お願い、私に踊らせて!"
  [3]=>
  string(98) "みんなにドロボウと言われてクラス崩壊・犯人探し…先生友達を返して!!"
  [4]=>
  string(95) "友達も消えた…もう学校なんて行かない! 先生どうして私をイジメるの"
  [5]=>
  string(94) "夏休みはありません! 追いつめられた子供が引き起こした悲劇と奇跡!!"
  [6]=>
  string(94) "学校が燃える夜、鬼教師が流した血の涙…先生お願い、友達を助けて!"
  [7]=>
  string(94) "卒業行事はやりません! 悪魔か天使か? 児童が知る鬼教師の哀しい過去"
  [8]=>
  string(21) "鬼教師への刺客"
  [9]=>
  string(24) "真矢、最後の授業"
  [10]=>
  string(27) "真矢のいない卒業式"
  [11]=>
  string(31) "エピソード1〜堕天使〜"
  [12]=>
  string(34) "エピソード2〜悪魔降臨〜"
}
*/
```

若干表構成の違うドラマのサブタイトルもいけますね。

# 関連

- [Qiita - PHPネイティブのDOMによるスクレイピング入門](http://qiita.com/mpyw/items/c0312271819baee09132)
- [Stack Overflow - Find position of a node using xpath](http://stackoverflow.com/questions/226405/find-position-of-a-node-using-xpath)