続・HTMLScrapingで苦戦

またまたHTMLScrapingのサンプルスクリプトHTMLToFeed.class.phpです。


1.XMLの操作がうまくできない
→配列の添え字が0から始まるのを忘れてた


2.現在の添え字が取得できない
XML操作の際、as $key => $valでは添え字取得できない



ここまできて、もう全て把握した、後は仕上げるだけ、4日前の夜にはそう思ってました。


しかし、
1.別に変数を用意して、添え字を指示する。
→なぜか最初の1回目だけうまく値が入らない


2.itemとしてRSSに表示されるのは100以上あるはずなのに10個しか表示されない
 しかもよくわからない順番(foreachの実行順は配列の定義順らしいけど、その順番でもなさそう)


とか、さらによくわからない方向につっこんでしまいました。
いくらスクリプトを読んでも原因になる箇所がみつかりません。

前回、同じような状況になった時、HTMLToFeed.class.phpの「サンプルスクリプトと同一の条件をつくって、ひとつひとつ変えてどの段階で動かなくなるかを確認することにしました」が、今回も同じ方法を採ることにしました。


HTMLToFeed.class.phpのサンプルの最もシンプルな状態からだんだん複雑にしていけば、原因となる箇所もみつかるだろ、と。
力技だけど、十分なスキルのない状態でスクリプトの動きを正確に把握するにはこうやって切り分けていくしかないよね。くすん。


ULを2つにしてみる

<TITLE>BOGUS HTML EXAMPLE</TITLE>
<B><FONT COLOR=#FF0000>This is a bogus HTML example for test_feed.php.</b></font>
<UL>
<LI><A HREF=test_feed.html#11>This is item #11 (2007/1/1)
<LI><A HREF=test_feed.html#12>This is item #12 (2007/1/2)
<LI><A HREF=test_feed.html#13>This is item #13 (2007/1/5)
<LI><A HREF=test_feed.html#14>This is item #14 (2007/1/7)
<!-- ↓ここから追加↓ -->
</UL>
<UL>
<LI><A HREF=test_feed.html#21>This is item #21 (2007/2/1)
<LI><A HREF=test_feed.html#22>This is item #22 (2007/2/2)
<LI><A HREF=test_feed.html#23>This is item #23 (2007/2/5)
<LI><A HREF=test_feed.html#24>This is item #24 (2007/2/7)
</UL>
<!-- ↑ここまで追加↑ -->
<HTML>


htmlとしてもおかしい(タグが閉じられていない)のですが、HTMLScraping(の付属機能)によって、xmlに変換できることを示すサンプルでもあるので、いいんです、これで。


サンプルスクリプトのままだと1組目のulの中のみフィード化して2組目のulの中まで辿り着けないから、以下のように修正します。foreachの出番ですね。

<?php
// 以上略
/* Retrieve and parse LI elements */
if ($ul_elements = $xml->body->ul) {
    foreach ($ul_elements as $ul) {

        if ($li_elements = $ul->li) {
            $this->convertPath($li_elements, array('a' => 'href'));
            foreach ($li_elements as $li) {
                $item = new HTMLToFeed_Item;
                $item->title = (string) $li->a;
                $item->link = $item->guid = (string) $li->a['href'];
                if (preg_match('|(\d{4})/(\d{1,2})/(\d{1,2})|s', $item->title, $matches)) {
                    $item->pubDate = strtotime("$matches[1]-$matches[2]-$matches[3]");
                }
                $this->channel->items[] = $item;
            }
        }

    }
}
// 以下略 
?>

日付をh4で囲ってulの外に出す

<TITLE>BOGUS HTML EXAMPLE</TITLE>
<B><FONT COLOR=#FF0000>This is a bogus HTML example for test_feed.php.</b></font>
<H4>2009/1/1</H4>
<UL>
<LI><A HREF=test_feed.html#11>This is item #11
<LI><A HREF=test_feed.html#12>This is item #12
<LI><A HREF=test_feed.html#13>This is item #13
<LI><A HREF=test_feed.html#14>This is item #14
<!-- ↓ここから追加↓ -->
</UL>
<H4>2009/2/1</H4>
<UL>
<LI><A HREF=test_feed.html#21>This is item #21
<LI><A HREF=test_feed.html#22>This is item #22
<LI><A HREF=test_feed.html#23>This is item #23
<LI><A HREF=test_feed.html#24>This is item #24
</UL>
<!-- ↑ここまで追加↑ -->
<HTML>


次。日付がulの外にあるから、サンプルスクリプトの単なる改造では期待どおりの動作はしない。
liの処理の外に、日付を取得する箇所を書く。

<?php
// 以上略
        /* Retrieve and parse LI elements */
if ($ul_elements = $xml->body->ul) {

    $count = '0';		 //カウンタ XMLの添え字を取得できない為
    foreach ($ul_elements as $ul) {

        $pubdate_mday = $xml->body->h4[$count];
        if (preg_match('|(\d{4})/(\d{1,2})/(\d{1,2})|s', $pubdate_mday, $matches)) {
           $pubDate = strtotime("$matches[1]-$matches[2]-$matches[3]");
        }


        if ($li_elements = $ul->li) {
            $this->convertPath($li_elements, array('a' => 'href'));
            foreach ($li_elements as $li) {
                $item = new HTMLToFeed_Item;
                $item->title = (string) $li->a;
                $item->link = $item->guid = (string) $li->a['href'];
//              if (preg_match('|(\d{4})/(\d{1,2})/(\d{1,2})|s', $item->title, $matches)) {
//                  $item->pubDate = strtotime("$matches[1]-$matches[2]-$matches[3]");
//              }

                  $item->pubDate = $pubDate;



                $this->channel->items[] = $item;
            }
        }
      $count++;		//カウンタに+1

    }
}
        $this->sortMultiArray($this->channel->items, 'pubDate');
    }
}
// 以下略 
?>


そうすると、
countが0 (2009/1/1) の時は、pubDateが出力されず、
countが1 (2009/2/1) の時は、pubDateが出力されて、Sun, 01 Feb 09 00:00:00 +0900
となる。


なんでYearが2桁表示なんだろう(^_^;
まぁそれはさほど問題じゃないので、とりあえずペンディング


肝心のpubDateが出力されない問題は、自分が書いたスクリプトでも生じたので、文字型かなんかの問題だと思う。


と思ってふと読み直すと

$count = '0'; //カウンタ XMLの添え字を取得できない為


これだ( ̄□ ̄;

文字列を指定する最も簡単な方法は、引用符 (文字 ') で括ることです。

PHP: 文字列 - Manual
 http://php.benscom.com/manual/ja/language.types.string.php


いくら「PHPでは特に変数のデータ型を宣言して使う必要がありません。こうした「曖昧さ」、「いい加減さ」が万人受けする言語でもある」とはいっても、間違って文字列変数を明示的に指定してたらおかしくなりますね。ゴメンナサイ


だから1回目は文字列として処理されて表示されない。
2回目からは文字列に足し算をする過程で文字列を数字の0と評価して+1したから結果として2回目以降は期待通りの結果となったと。


なるほどなるほど。
これで冒頭1の問題、解決です。


この点を元のスクリプトに反映したところ、無事全てのitemのpubDateが表示されました。万歳!


一部しか表示されていない件

その上で改めてはき出したRSSをみると、各日の最後のitemだけ出力されています。
変数が重なってるとかだな……。


そこで、元のスクリプトを読み直すと


> $this->channel->items[] = $item;


の行が本来、ある}の内側にあるべきにもかかわらず、外側にありました。
修正したところあっさり解決。


ようやく期待通りのところにやってきました。


悩んでる時間よりもサンプルスクリプトから出発した時間の法が圧倒的に短かったですね。
明らかに詰まった時は原点に帰って問題の切り分けをする。これを徹底したいと思います。