現在、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で質問しましょう!
似たような質問が見つかりました
- PHP PHPの構文で間違えが分からない 5 2022/07/11 16:38
- PHP SQLとPHPの連結方法がわからないのでアドバイスお願い致します 1 2022/07/12 12:16
- JavaScript フォームが空欄の時にフォームの外をクリックすると、エラーが出るコードを調べています。 1 2023/06/25 11:51
- JavaScript jqueryを使ったスムーススクロールのコードを書いたのですが、HTMLコード内にある、a butt 2 2022/04/14 10:59
- Excel(エクセル) PHPプログラムをエクセルに張り付けると検索ボックスがでてくる! 3 2022/05/08 07:10
- その他(データベース) Accessのクエリで1フィールドの抽出条件設定をNullでなく全角半角含む空白のみの文字列でない文 1 2023/04/24 15:20
- その他(プログラミング・Web制作) Chromeのデベロッパーツール プログラム 2 2022/06/21 20:27
- HTML・CSS テキストを画面の真ん中に配置したいです。 2 2022/11/25 16:11
- HTML・CSS サルワカさんの吹き出しのスタイルシートについて。 https://saruwakakun.com/h 2 2022/10/28 22:55
- PHP アップロード画像数でCSSを分けることに成功したのですが、画像の横に文字を並べることが出来ません。 3 2023/07/28 17:16
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
DIVの入れ子で、FireFoxだとレ...
-
VBAでの素数の求め方
-
CSSで、何故か、「float」が上...
-
CSSセクレター 子孫のみに適用...
-
htmlの文字が縦書きになる
-
【ヒトの神秘】美男美女から何...
-
マージソートの計算量について-...
-
複数のボタンを等間隔に、かつ...
-
双方向リスト
-
htmlのolやulなどlistにtitleや...
-
HTML5のfooterに見出しを付けて...
-
「諸要素」とはどういう意味で...
-
ヘッダーとフッターだけ背景を...
-
<h1>、<h2>と<p><div>の行間を...
-
リストマーカーをボックス内に...
-
十進BASICでのプログラム
-
<table>の高さ固定。情報増加時...
-
「目次」と「サブ目次」のスタ...
-
記事の最後の次のページ、前の...
-
【html&css】太さの違う2本線の...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ASP.NETでプレーンなページに文...
-
pythonでのカーソル移動がずれる
-
cnt <= (others => '0'); の意...
-
Excel VBAでのIE操作でクリック...
-
検索結果がツリー状に表示され...
-
VBAでの素数の求め方
-
秀丸で複数行コメントアウトを...
-
質問です。 新規登録ボタンが全...
-
safariで見るとページ上部に余...
-
chromedriverのver-upで動作不...
-
Firefoxで別ページのアンカーリ...
-
プログラマーの方に質問です。 ...
-
行頭から全角で3文字位さげた...
-
css リンクの色が全部変わって...
-
背景が動くWEBページの作り方に...
-
同じIE8なのに機種によってmarg...
-
VBA : QueryTableでのスクレイ...
-
Ajaxで文字列を表示したいです。
-
携帯サイトの背景色
-
htmlの文字が縦書きになる
おすすめ情報