これは、週に1〜2回ポップアップする同様の(ただし、具体的な質問が多すぎてターゲット候補にはならない)すべてに正規のQ&Aを提供することを目的としています。
テーブルを含むWebサイトを解析する必要があるアプリケーションを開発しています。 WebページをスクレイピングするためのXPath式を導出するのは退屈でエラーが発生しやすい作業なので、これにはFirebugのXPath抽出機能(または他のブラウザーの同様のツール)を使用したいと思います。
入力例は次のようになります。
<!-- snip -->
<table id="example">
<tr>
<th>Example Cell</th>
<th>Another one</th>
</tr>
<tr>
<td>foobar</td>
<td>42</td>
</tr>
</table>
<!-- snip -->
最初のデータセル(「foobar」)を抽出したい。 FirebugはXPath式を提案します
//table[@id="example"]/tbody/tr[2]/td[1]
which XPathテスタープラグインでは正常に機能しますが、自分のアプリケーションでは機能しません(結果が見つかりません)。クエリを//table[@id]
に切り詰めると、再び機能します。
何が問題なのですか?
<tbody/>
_タグが必要ですFirebug、Chromeの開発者ツール、JavaScriptのXPath関数などは、基本的なHTMLソースコードではなく、[〜#〜] dom [〜#〜]で動作します。
HTMLのDOMでは、フッターのテーブルヘッダー(_<thead/>
_、_<tfoot/>
_)に含まれていないすべてのテーブル行がテーブル本体タグ_<tbody/>
_に含まれている必要があります。したがって、(X)HTMLの解析中にタグが欠落している場合、ブラウザーはこのタグを追加します。たとえば、 MicrosoftのDOMドキュメント は
テーブルで
tbody
要素が明示的に定義されていない場合でも、tbody
要素はすべてのテーブルで公開されます。
stackoverflowに関する別の回答の詳細な説明 があります。
一方、 HTMLでは必ずしもそのタグを使用する必要はありません :
TBODY
startタグは、テーブルにテーブル本体が1つだけ含まれ、テーブルの頭または足のセクションがない場合を除いて、常に必要です。
JavaScriptを除いて、ほとんどのXPathプロセッサはDOMではなく生のXMLで動作するため、_<tbody/>
_タグを追加しません。また、 tag-soup や htmltidy などのHTMLパーサーライブラリは、「DOM-HTML」ではなくXHTMLのみを出力します。
これは、PHP、Ruby、Python、Java、C#、Google Docs(Spreadsheets)などのStackoverflowに投稿された一般的な問題です。 Seleniumはブラウザ内で実行され、DOMで動作するため、影響を受けません!
Firebug(またはChromeの開発ツール)によって表示されるソースを、右クリックして[ページソースを表示](またはブラウザーで呼び出されるもの)を選択するか、コマンドで_curl http://your.example.org
_を使用して表示されるソースと比較します。ライン。後者にはおそらく_<tbody/>
_要素が含まれていません(それらはめったに使用されません)。Firebugは常にそれらを表示します。
/tbody
_軸ステップを削除する行き詰まっているテーブルに本当に_<tbody/>
_要素が含まれていないかどうかを確認します(最後の段落を参照)。もしそうなら、おそらく別の種類の問題があります。
次に、_/tbody
_軸ステップを削除して、クエリが次のようになるようにします。
_//table[@id="example"]/tr[2]/td[1]
_
<tbody/>
_タグをスキップするこれはかなり汚い解決策であり、ネストされたテーブルでは失敗する可能性があります(内部テーブルにジャンプする可能性があります)。非常にまれなケースでのみこれをお勧めします。
_/tbody
_軸ステップを子孫または自己ステップに置き換えます。
_//table[@id="example"]//tr[2]/td[1]
_
<tbody/>
_タグの有無にかかわらず両方の入力を許可するテーブルを事前に確認できない場合、または「HTMLソース」とDOMコンテキストの両方でクエリを使用する場合。ソリューション2のハックを使用したくない/使用できない場合は、代替クエリ(XPath 1.0の場合)を提供するか、「オプションの」軸ステップ(XPath 2.0以降)を使用します。
//table[@id="example"]/tr[2]/td[1] | //table[@id="example"]/tbody/tr[2]/td[1]
_//table[@id="example"]/(tbody, .)/tr[2]/td[1]
同じ問題に遭遇したばかりです。すべてのtbodyタグが存在するかどうかをチェックし、そのようにdomをトラバースする再帰関数をほとんど作成しました。その後、正規表現を知っていることを思い出しました。 :)
解析する前に、htmlを文字列として取得します。不足している<tbody>
タグと</tbody>
タグを正規表現で挿入し、それをDOMDocumentオブジェクトにロードし直します。
Jens Eratが良い説明をしていますが、ここにあります
<tbody>
タグがあることを確認しますJavaScript
var html = '<html><table><tr><td>foo</td><td>bar</td></tr></table></html>';
html.replace(/(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/g,"$1<tbody>").replace(/(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/g,"$1</tbody>$4");
PHP
$html = $dom->saveHTML();
$html = preg_replace(array('/(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/','/(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/'),array('$1<tbody>','$1</tbody>$4'),$html);
$dom->loadHTML($html);
正規表現だけ:
matches `<table>` tag with whatever else junk inside the tag and between this and the next tag if the next tag is NOT `<tbody>` also with stuff inside the tag
/(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/
replace with
$1<tbody>
the $1 referencing the captured `<table>` tag with contents.
Do the same for the closing tag like this:
/(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/
replace with
$1</tbody>$4
このようにして、domには常に必要に応じて<tbody>
タグが付けられます。