RubyのHTMLパーサーを使って複数のタグを抽出する方法

このQ&Aのポイント
  • Rubyを使用してあるWebページから特定のタグの要素を抽出する方法について説明します。
  • HpricotやNokogiriなどのHTMLパーサーを使用することで、特定のタグの要素を取得できます。
  • 抽出したい要素のタグを指定する際には、タグ名をカンマで区切ります。抽出される順序は指定した順序となります。
回答を見る
  • ベストアンサー

RubyのHTMLパーサーで複数のタグを抽出したい

現在、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が入れ子となっている場合に、 非常にややこしく感じたので お手上げ状態です。 どのように、解決できるでしょうか。 よろしくお願いします。

  • Ruby
  • 回答数1
  • ありがとう数1

質問者が選んだベストアンサー

  • ベストアンサー
  • notnot
  • ベストアンサー率47% (4846/10257)
回答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']"

Rupyjp
質問者

お礼

どうもありがとうございます。 結論から言いますと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

  • BCBで使えるHTMLパーサー、リンクの抽出

    Borland C++Builderで使えるHTMLパーサーのVCLを探しています。海外では、THTMLParserなどを検索語にして調べると幾つかありましたが日本語が文字化けしたりして、調子よくありません。 http://www.dallas.net/~richardp/delphi/components/home.html 日本語の通るフリーのHTMLパーサーのVCLというのはご存知でしょうか。 また、実際にやりたいのは、ページから、 ・<TITLE>タグで囲まれたタイトルの抽出 ・<A HREF = URL>リンク文字列</A>  の、URL部分とリンク文字列のリストを得る ということなのですが、私の拙い知識では何度やってもうまく取り出すことができません。それでVCLでできないかなあと調子の良いことを考えています。こういった文字列解析の定石のようなコーディングはあるのでしょうか。 質問ばかりですみません。ご存知の方よろしくお願いいたします。

  • 開始タグと終了タグについて

    開始タグと終了タグについて ものすごく初歩的な質問で失礼します。 <div id="container"> <div id="header"> </div> <div id="menu"> </div> <div class="main-top-img"> </div> <div class="main-middle-img"> <div id="main-contents"> <div class="text"> <h2 class="title"></h2> <p class="text"></p> <h3 class="subtitle"> <p class="text"></p> </div> </div> <div class="main-bottom-img"> </div> <div id="footer"> </div> </div> などいのように開始タグと終了タグの間に、他の多くの<div>要素がある場合、どこがその要素の終了タグなのか分からなくなります。 それぞれの開始タグが、それぞれの終了タグと対応するのは、どのようなルールのもとに決定されているのですか??

    • ベストアンサー
    • HTML
  • HTMLタグ内文字を抽出するには

    HTMLタグ内の特定<title>タグ内文字列のみをエクセルマクロで抽出する方法はありますか? またエクセルのURL一覧の横に結果を表示させたいです。 宜しくお願い致します。

  • 空要素タグ??

    ブログのSEO対策の為に以下のページに自分のページURLを入れ タグが上手く書けているかをチェックしました ​http://openlab.ring.gr.jp/k16/htmllint/htmllinte.html​ すると、その中の回答に 空要素タグ の要素には空白さえも含めることはできません。 という問題点を指摘されました これの意味は XMLの Content of Elements(J)では、空要素タグは、<tag></tag> のように開始タグと終了タグをくっつけて書かなければならないとされてます。つまり、空白も含めることはできません。 という事です そして対象となる部分は以下のタグのどこかにあるようなのですが まったく検討がつきません どこがエラーの対象なのでしょうか? ちなみにブログはMTですのでカスタマイズは問題ありません よろしくお願いします <h2 class="archive-header"><$MTBlogName encode_html="1"$>項目一覧</h2> <div class="entry" id="entry-999"> <MTCategories> <h3 class="entry-header"><a href="<$MTCategoryArchiveLink$>"> <$MTCategoryLabel cutfirstchar="4"$></a></h3> <$MTCategoryDescription$> <div class="entry-content"> <div class="entry-body"> <MTEntries lastn="999"> <a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a>&nbsp;|&nbsp; </MTEntries> </div> <p class="entry-footer"></p> </div> </MTCategories> </div> <p><br /></p> <div class="gotop"><a href="#TOP">このページのトップへ&uarr;</a></div> </div> </div>

  • 変数内でHTMLを使用したいです

    自分なりにいろいろ試してみたのですが出来なかったので、お教えいただきたいと思い、質問させていただきます。 現在、perlを使ってCGIを組んでいます。 (というよりは組まれていたCGIを編集しています) その中で、外部から入力された文字列データを変数に格納し それをprintで書き出しています。 以下のような形です。 -------------------------------------------------- open(IN,"$datfile"); while (<IN>) { my ($no,$date,$name,$comment,$ip) = split(/<>/); print "<div class=\"line_wrap\">"; print "<div class=\"line_left\">$date</div>"; print "<div class=\"line_right\">$comment</div>"; print "</div>"; } close(IN); -------------------------------------------------- なんとなくでしか理解できてないのに編集したのでおかしい部分もあるとは思いますが・・・。 今回お伺いしたいのは $comment という変数内に、HTMLタグが入っていた場合、そのままの状態で吐き出されてしまうことになり困っています。 変数内の文字列に記載されているHTMLを、HTMLとして認識して実行したいのですが(要はリンクを貼りたい)可能でしょうか? 外部から入力されているものですので、HTMLがあったりなかったりします。 何か方法があれば、教えていただきたいです。 よろしくお願い致します。

    • ベストアンサー
    • Perl
  • HTMLタグの中味抽出

    HTMLのタグの中味を抽出する正規表現として、以下の形を学びました。 [^<]+?(?=>) この正規表現で以下のHTML文を処理すると、 <tag_nakami_1><tag_nakami_2>title_text</tag_nakami_1></tag_nakami_2> 以下の結果が出ました。 tag_nakami_1 tag_nakami_2 /tag_nakami_1 /tag_nakami_2 ですが、なぜ、「title_text」の部分がうまく取り除かれたのかがわかりません。 <tag_nakami_1><tag_nakami_2> まで処理がすんで、次は title_text</tag_nakami_1> が処理対象の文字列となるのでは無いでしょうか?その場合、末尾の > より前の部分、title_text</tag_nakami_1 から < のみが除かれた文字列、title_text/tag_nakami_1 が抽出される、つまり以下のような結果が出そうに思えます。 tag_nakami_1 tag_nakami_2 title_text/tag_nakami_1 /tag_nakami_2 ですが実際はそうなりません。 これはなぜなのでしょうか……。

  • ブログのHTMLから本文を抽出したい

    いつもお世話になっています。 ブログのHTMLから本文を抽出したいと思っています。 ヤプログを例にとると、ヤプログのHTMLは以下のようになっています。 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> … <!-- entry --> …  <td class="entry_title">  <h3>タイトル<span class="date">2005年10月29日(土)</span></h3> … <td class="entry_text"> … <div class="entry_layer"><div style="text-align: center;"> <img src="/cafe_life/img/25/cafe67.jpg" border="0"> </div> <br> 今日はとてもいい天気だったので、<br> 渋谷に買い物に行きました。<br> … <br> 新しいiMacが欲しいなぁ~<br> <br> </div> … <!-- entry --> … </html> そこで、本文を抽出するのに、 <td class="entry_text"> から<!-- entry -->までをHTMLから抽出し、 さらにそれに対してタグを除去するようにしてブログの本文の 「今日はとてもいい天気だったので、  渋谷に買い物に行きました。  …  新しいiMacが欲しいなぁ~」 を抽出させたいと思っています。 このようにするにはプログラムをどのように作成すればよいでしょうか? よろしくお願いします。

    • ベストアンサー
    • Java
  • HTMLで○や■などをテキストの代用とした時

    HTMLで○や■などをテキストの代用とした時レイアウトが崩れてしまうのはなぜでしょうか? 教えてください。 よろしくお願い致します。 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Script-Type" content="text/javascript" /> <meta http-equiv="Content-Style-Type" content="text/css" /> <title>無題ドキュメント</title> <style type="text/css"> <!-- body { font: 100%/1.4 "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "メイリオ", Meiryo, Osaka, "MS Pゴシック", "MS P Gothic", sans-serif; background-color: #42413C; margin: 0; padding: 0; color: #000; } .container { width: 960px; text-align: center; background-color: #FFF; margin: 0 auto; } .header { background-color: #ADB96E; } .content { text-align: left; padding: 10px 0; } .footer { padding: 10px 0; background-color: #CCC49F; } --> </style></head> <body> <div class="container"> <div class="header"> </div> <div class="main"> <p>○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○○</p> </div> <div class="footer"> </div> </div> </body> </html>

  • HTML・CSSタグの書き方教えてください!

    画像では見づらいのですが、HOMEの左側にボーダーがピンク色で表示されています。 このHOMEの左側のボーダーを消したい(又は見えなくする)タグを教えてください。 最初のリンクのところだけグループ化して, CSSに{border-left-style: none;}と加えてみたり、 .menu ul li a .first {border-left: none;}等してみたのですが、 変わらず…。 どういうタグを加えれば、左側のボーダーが見えなくなりますでしょうか? よろしくお願いいたします。 <HTML> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <meta http-equiv= "content-style-type" content= "text/css"> <meta http-equiv="Content-Script-Type" content="text/javascript" /> <link href="css/style.css"rel="stylesheet" type="text/css" /> </head> <body> <div id="wrapper"> <div id="header"> <h1> 作りかけページ </h1> <p>テストページ</p> </div><!-- header終わり --> <div class="menu"> <ul> <li><a href="#">HOME</a></li> <li><a href="#">サービス概要</a></li> <li><a href="#">会社概要</a></li> <li><a href="#">お問い合わせ</a></li> <li><a href="#">サイトマップ</a></li> </ul> </div><!-- menu終わり --> <div id="main"> <div id="contents"> ***コンテンツ*** </div><!-- contents終わり --> <div id="leftsidebar"> ***左サイドバー*** </div><!-- sidebar終わり --> </div><!-- main終わり --> <div id="rightsidebar"> ***右サイドバー*** </div><!-- sidebar終わり --> <div id="footer"> ***フッター*** </div><!-- footer終わり --> </div><!-- wrapper終わり --> </body> </html> <CSS> <style type="text/css"> * { margin: 0; padding: 0; } #wrapper{ width: 760px; /*全体の幅(#wrapper)を760pxの固定幅とする。 */ position: absolute;/*絶対位置*/ left: 50%;/*左端から右方向の位置を指定する */ margin-left: -380px;/*はみ出た半分を戻す */ } /*ヘッダー*/ #header{ width: 758px;/*ヘッダー部分の幅(#content)を758pxとする。*/ height: 200px; background-color:#ffd700; background-repeat: no-repeat; background-position: -50px -70px; text-align: right; border: solid 1px gray; } #header h1{ margin: 0; font-size: 30px; padding: 20px 10px 0px 0px; font-family:arial unicode ms; } #header p{ margin: 0; font-size: 1em; padding-right: 10px; margin-top: 8px; } /*ヘッダー終わり*/ /*body*/ /*menu部分*/ .menu {font-size:20px; height:30px; background-color:#0c907c; text-align:center; } .menu ul{ margin: 0; padding: 0; 760px; } .menu ul li{ float: left; list-style: none; margin: 0; padding: 0; } .menu ul li a{ display:block; width: 151.2px; line-height: 30px; border-left: solid 1px #FF3366; color:#ffffff; text-align: center; text-decoration: none; } .menu ul li a:hover{background-color: #99cc00} /*menu部分終わり*/ #contents{ width: 388px;/*コンテンツ部分の幅(#content)を388pxとする。*/ background-color:#0000FF; float: right;/* 右に配置 */ } #leftsidebar{ width: 166px; /*左サイドバー部分の幅を166pxとする。*/ background-color:#0000FF; float: left;/* 左に配置 */ } #main{ width: 574px; float: left;/* 左に配置 */ } #rightsidebar{ width: 166px;/*右サイドバー部分の幅を166pxとする。*/ background-color:#0000FF; float: right;/* 右に配置 */ } #footer{ width:760px; background-color:#FF3366; clear: both;/* float解除 */ height: 50px; }

    • ベストアンサー
    • HTML
  • HTML トップページから各ページへのタグの作り方

    ホームヘージを作っていますが、 ド素人で分かりません。 教えてください。 http://webz.jugem.jp/?tid=12 上記のようなホームページを作るのですが、 「menu1」のボタンから新しいページを作りたいのですが、 どのようなタグを使えばよいのでしょうか? 下記はトップページの一部分です。 次のページに移動するにはどこへそのタグを書き込めば良いでしょうか? <div class="header"><p class="description">{blog_description}</p></div> <div class="navigation"> <a href="./" id="active">Home</a> <a href="./#top">menu1</a> <a href="./#top">menu2</a> <a href="./#link">Link</a> <a href="./?pid=1">Profile</a> <div class="clearer"></div> </div>

専門家に質問してみよう