0

PHPを使用して、特定のURLとXPATHのコンテンツを取得しています。私はDOMDocument/DOMXPathを使用しています(クエリまたは評価を使用)。

小さいxpathの場合、正しい結果が得られますが、長いxpathの場合、機能しません。(そして、このxpathは良いようです(私はXpather(firefoxプラグイン)でそれらを取得し、YQLでそれらを再テストしました)。

この不思議なトラブルについて何かアドバイスはありますか?

コードの例:

$doc = new DOMDocument();
$myXMLString = file_get_contents('http://stackoverflow.com/questions/4097230/too-long-xpath-with-domxpath-query-evaluate-return-nothing');
@$doc->loadHTML($myXMLString); //@ to suppress warnings 
                               //(good for not ending markup)
$xpath = new DOMXPath($doc);

$fullPath ="/html/body/small/path"; //it works
//$fullPath = "/html/body/full/path/with/lot/of/markup";//does not works
$entries = $xpath->query($fullPath);
//or ->evalutate($fullPath) (same behaviour)
//$entries return DOMNodeList (empty for a long path query, 
//                             correct for a small path query)

属性制限を使用してテストしましたが、変更されていないようです(xpathが小さい場合は機能し、長い場合は機能しません)

例:この現在のページの場合:

$fullPath = "/html
              /body
               /div[4]
                /div[@id='content']
                 /div[@id='question-header']
                  /h1
                   /a";//works (retrieve the question title)
$fullPath = "/html
              /body
               /div[4]
                /div[@id='content']
                 /div[@id='mainbar']
                  /div[@id='question']
                   /table
                    /tbody
                     /tr[2]
                      /td[2]
                       /div[@id='comments-4097230']
                        /table
                         /tbody
                          /tr[@id='comment-4408626']
                           /td[2]
                            /div
                             /a"; //does'nt work 
                                  //(should retrieve 'gaby' from comment)

編集:

SimpleXML libでテストしましたが、まったく同じ動作をします(小さなクエリでは良い結果が得られ、長いクエリでは何も起こりません)。


編集2:

また、最初の要素を削除して最長のxpathを切り取り、機能します。ところで、私は完全に正しいxpathが機能しない理由を本当に理解していません。

4

1 に答える 1

3

このステップバイステップを見ていきましょう:

ステップ1:エラーを複製します。

XPathが実際に結果を返さないことを確認した後、XPathが破損する前にXPathがどの程度深くなるかを確認するための小さなスクリプトを作成しました。

foreach (explode('/', $fullPath) as $segment) {
    $xpath .= trim($segment);
    echo '-------------------------------------------', PHP_EOL,
         'Trying: ', $xpath, PHP_EOL,
         '-------------------------------------------', PHP_EOL;
    echo $xp->evaluate("string($xpath)"), PHP_EOL;
    $xpath .= '/';
}

結果を返す最後のことは

/html/body/div[4]/div[@id='content']/div[@id='mainbar']/div[@id='question']/table

ステップ2:マークアップを確認する

DOMDocument::saveHTML()だから私はそれがどのように見えるかを見るために返されたマークアップをチェックしました、そしてありませんでした<tbody> (読みやすさのために再フォーマットされました)

<div id="question">
    <div class="everyonelovesstackoverflow" id="adzerk1"></div>
        <table>
            <tr><td class="votecell">

次に、このページをチェックして、それがDOMによって破棄されたのか、それとも実際には存在しないのかを確認しました。そこにはありませんでした。どうやら、Firebugはそれを挿入します。これは、XPatherで結果が得られた理由を説明します(ただし、YQLで結果が得られた理由は説明しません)。

ページソースと明らかにバグのあるFirebugビューを示すスクリーンショット

ステップ3:プルーフチェックと結論

<tbody>XPathからを削除し、スクリプトを再実行しました。問題はありません。「Gaby」を返します。

最初にFirebugのバグを疑っていましたが、Alejandroは、これはIEのDeveloperToolsでも発生するとコメントしました。次に、これがJavaScriptによって追加されたのではないかと疑っていましたが、それを確認できませんでした。さらに調査した後、アレハンドロは私に、なぜファイアバグが追加<tbody>されるの<table>かを指摘しました。-実際にはFirebugでもJavaScriptでもありませんが、ブラウザ自体です。

だから私の結論を修正するには:

ブラウザまたは他のテクノロジーによって変更される可能性があるため、ブラウザに表示されるマークアップを信頼しないでください。DOMは、直接提供されるもののみをダウンロードします。同様の問題が再び発生した場合でも、対処方法がわかります。


いくつかの追加の補足

マークアップをDOMにフィードする前に変更する必要がない限りfile_get_contents()、コンテンツのロードにを使用する必要はありません。DOMを使用できますloadHTMLFile()

$dom->loadHTMLFile('http://www.example.com/foo.htm');

また、エラーを抑制する適切な方法は、libxmlに内部エラーハンドラを使用するように指示することです。ただし、エラーを処理する代わりに、単にエラーをクリアします。これは、(すべてのPHPエラーではなく)解析エラーなど、libxmlに関連するエラーにのみ影響します。

libxml_use_internal_errors(TRUE);
libxml_clear_errors();

最後に、xPathクエリはコンテキストノードに関連して実行できます。したがって、長いXPathはルックアップ時間の点で効率的ですが、単純に使用getElementById()して最も深い既知のノードを取得し、それに対してXPathを使用することができます。

言い換えると:

libxml_use_internal_errors(TRUE);
$dom = new DOMDocument;
$dom->loadHTMLFile('http://www.example.com/foo.htm');
libxml_clear_errors();
echo $xp->evaluate(
    'string(td[2]/div/a)', 
    $dom->getElementById('comment-4408626'));

「ギャビー」も返します。

于 2010-11-04T14:32:57.290 に答える