
現在、Rubyを用いて
あるWebページから特定のタグの要素を抽出して
テキストデータに出力したいと思い、プログラムを書いているのですが、
途中で行き詰ってしまいました。
ちなみにHpricotやNokogiriといったHTMLパーサーを試してみました。
パーサーを使うのは初めてです。
例えば以下のようにHTML文書に抽出したい部分がDIVタグとPタグに囲まれていた場合、
=====================================
・・・
<div class="content">
<div class="header">不要部分</div>
<div class="title">抽出したい文字列1</div>
<div class="subtitle">抽出したい文字列2</div>
<div class="subtitle">抽出したい文字列3</div>
<p class="paragraph">抽出したい文字列4</p>
<p class="paragraph">抽出したい文字列5</p>
<div class="comment">抽出したい文字列6</div>
<div class="footer">不要部分</div>
</div>
・・・
=====================================
このHTMLからまずは
抽出したい文字列1
抽出したい文字列2
抽出したい文字列3
抽出したい文字列4
抽出したい文字列5
抽出したい文字列6
といった出力が得たいのです。
プログラムも書いてみました。
=====================================
#ドキュメント全体を取得(dataにはHTMLの文字列が入っている)
html = Hpricot(data)
#内容部分(contentクラスのdiv)を取得(CSSセレクタで記述)
content = html/"div.content"
=====================================
ここまでは良いのですが、
そのあと、div要素を取り出すだけであれば
headerクラスと、footerクラスを抽出しないように
=====================================
(content/"div:not(.header):not(.footer)").each{ |line|
puts line.inner_html
}
=====================================
とすることで、
抽出したい文字列1
抽出したい文字列2
抽出したい文字列3
抽出したい文字列6
が得られますが、pタグをカンマでdivの前に追加して
=====================================
(content/"p,div:not(.header):not(.footer)").each{ |line|
puts line.inner_html
}
=====================================
のようにすると、
抽出したい文字列4
抽出したい文字列5
抽出したい文字列1
抽出したい文字列2
抽出したい文字列3
抽出したい文字列6
のように出力されます。
同様にdivの後にpを追加し
=====================================
(content/"div:not(.header):not(.footer),p").each{ |line|
puts line.inner_text
}
=====================================
のようにすると
抽出したい文字列1
抽出したい文字列2
抽出したい文字列3
抽出したい文字列6
抽出したい文字列4
抽出したい文字列5
のようになってしまいます。
つまり、複数のタグを指定すると、指定した順序で抽出されるようです。
代わりに子供すべてを列挙するchildのようなものがあるかと、調べてみたのですが、
どうやらそのような書き方はないようです。
複数のタグを含む場合にはHTMLパーサーでは解析できないのでしょうか。
パーサーは抽出時に順番を保証はしてくれないのでしょうか。
あきらめて、正規表現で抽出しようと思いましたが、
=====================================
<div class="comment">
<div class="comment_header">ごちゃごちゃ</div>
<div class="comment_body">抽出したい要素6</div>
</div>
=====================================
などDIVが入れ子となっている場合に、
非常にややこしく感じたので
お手上げ状態です。
どのように、解決できるでしょうか。
よろしくお願いします。
No.1ベストアンサー
- 回答日時:
CSSであまり複雑な指定をしたこと無いのですが、おそらくXPathのほうが細かい指定が出来ます。
Nokogiriでしか確認していませんが、おそらくHpricotでもXPath指定が出来ると思います。
content / "./*[@class!='header' and @class!='footer']"
./* が直下の任意のノードで、[ ] 内がその選択条件です。
あるいはまとめて、htmlから直接、
html / "//div[@class='content']/*[@class!='header' and @class!='footer']"
任意のノードでなく、div と p だけに限るなら、
content / "./*[((name()='p')or(name()='div')) and @class!='header' and @class!='footer']"
どうもありがとうございます。
結論から言いますとHpricotからはできませんでしたが
Nokogiriで成功しました!!
Hpricotで一生懸命あれやこれややってみましたが、
結局できませんでした。
なぜか、ワイルドカード指定*がパス直下ではなく
すべての子孫の要素に適応されるようです。
バグなのか、仕様なのかわかりませんが。
何がおかしいんでしょう。
検証に使用したのは以下のコードです↓
==========================================
#coding: utf-8
require 'rubygems'
require 'hpricot'
data = <<"END"
<html>
<body>
<div class="content">
<div class="header">不要部分</div>
<div class="title"><b>抽出</b>したい文字列1</div>
<div class="subtitle">抽出したい文字列2</div>
<div class="subtitle">抽出したい文字列3</div>
<p class="paragraph">抽出したい文字列4</p>
<p class="paragraph">抽出したい文字列5</p>
<div class="comment">抽出したい文字列6</div>
<div class="footer">不要部分</div>
</div>
</body>
<html>
END
html = Hpricot(data)
content = html/"div.content"
(content/"./*[((name()='p')or(name()='div')) and @class!='header' and @class!='footer']").each_with_index{ |line,i|
print i
print ":"
puts line.inner_html
}
==========================================
結果は
==========================================
1:
2:不要部分
3:
4:
5:<b>抽出</b>したい文字列1
6:抽出
7:
8:
9:
10:抽出したい文字列2
11:
12:
13:抽出したい文字列3
14:
15:
16:抽出したい文字列4
17:
18:
19:抽出したい文字列5
20:
21:
22:抽出したい文字列6
23:
24:
25:不要部分
26:
27:
==========================================
というおかしなものになってしまいました。
さらに最後のputs line.inner_htmlをpp lineにすると
いろいろとんでもないことになっているようです。
しかしNokogiriを使うと結果は
==========================================
0:<b>抽出</b>したい文字列1
1:抽出したい文字列2
2:抽出したい文字列3
3:抽出したい文字列4
4:抽出したい文字列5
5:抽出したい文字列6
==========================================
として正しく抽出できました。
とりあえず、Nokogiriでうまくできたので満足しています。
質問して良かったです。
ありがとうございました。
また機会がありましたらよろしくお願いします。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
cnt <= (others => '0'); の意...
-
htmlの文字が縦書きになる
-
ホームページの下にあるcopy ri...
-
ボタンをセル内一杯に表示させ...
-
訪問済のリンク色を変えない方法
-
既婚男女の方、結婚前と結婚後...
-
html の divとtable の役割
-
aの中にspan
-
テンソル解析(絶対微分学)は...
-
htmlのolやulなどlistにtitleや...
-
【CSS】ヘッダーの高さが不明の...
-
<h1>、<h2>と<p><div>の行間を...
-
Flexslider2のカーセルスライダ...
-
円形の配置にするコーディング...
-
widthやheightの数値に単位(px...
-
borderをページの下まで伸ばしたい
-
含む含まないという概念自体の...
-
ワードにコピペ、画像が表示さ...
-
リストマーカーをボックス内に...
-
1から100までの自然数のうち、2...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
cnt <= (others => '0'); の意...
-
pythonでのカーソル移動がずれる
-
CSSで背景色を指定しても、読み...
-
回り込みfloatの連続?
-
csvファイルについて
-
コーディングで「float」のclea...
-
Excel VBAでのIE操作でクリック...
-
ASP.NETでプレーンなページに文...
-
CSSで高さを自動にするとレイア...
-
htmlを組んでいるのですが、DIV...
-
.htaccessについて
-
検索結果がツリー状に表示され...
-
現在作成しているページで、一...
-
秀丸で複数行コメントアウトを...
-
忍者ツールズのカウンタを右寄...
-
CSSメニューボタンのセンター配置
-
safariで見るとページ上部に余...
-
css リンクの色が全部変わって...
-
RubyのHTMLパーサーで複数のタ...
-
CSSのdivタグの名称には決まり...
おすすめ情報