質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.25%

  • Ruby on Rails

    5359questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Rubyのクレイピングでn番目の要素(HTMLタグ)を取得したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 59

vol_ay

score 13

HTMLからスクレイピングをするとき、構造が複雑で簡単にタグを指定できない場面に遭遇しました。

まず、スクレイピングしたHTMLの構造は以下のとおりです。

<div class="products-mainImg "><img src="/products/products_images/l/PRD2009-09-0046_100012_00_1487742558_58abd4de0e038.png" height="350" width="350" alt=""></div>

<div class="products-detailBox">
<div class="products-detailBox__inner">
<dl class="products-detailBox__list">
<dt class="products-detailBox__term">発売日</dt>
<dd class="products-detailBox__desc">
<table class="products-saleDate" summary="販売地域について:北海道~東海">
      <tr>
      <th>北海道</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>東北</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>北関東・信越</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>首都圏</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>東海(静岡含む)</th>
      <td>2017年3月<br /></td>
      </tr></table>
<table class="products-saleDate" summary="販売地域について:北陸~沖縄">
      <tr>
      <th>北陸</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>近畿</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>中国</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>四国</th>
      <td>2017年3月<br /></td>
      </tr>      <tr>
      <th>九州・沖縄</th>
      <td>2017年3月<br /></td>
      </tr></table>

<p class="products-detailBox__areaLink"><a href="#areaBox" class="linkTxt00 js-lightBox">販売地域について</a></p>
</dd>
<dt class="products-detailBox__term">内容量</dt>
<dd class="products-detailBox__desc">135ml</dd>
<dt class="products-detailBox__term">原材料</dt>
<dd class="products-detailBox__desc">グレープフルーツ果汁、異性化液糖、食塩/香料、酸味料、甘味料(アスパルテーム・L−フェニルアラニン化合物、スクラロース)、ビタミンC、ポリリン酸Na、カロテン色素</dd>
<dt class="products-detailBox__term">栄養成分</dt>
<dd class="products-detailBox__desc">[ 1カップ 当り ]<br />熱量:13kcal<br />たんぱく質:0g<br />脂質:0g<br />炭水化物:3.2g<br />食塩相当量:0.17g<br />ビタミンC:40mg</dd>

<dt class="products-detailBox__term">アレルギー<br>関連情報</dt>
<dd class="products-detailBox__desc">
<p class="products-detailBox__note01"><i class="products-detailBox__icon"><img src="img/detail/icon_material/icon_thumb.png" height="17" width="35" alt=""></i>色のアイコンは原材料に使用されております</p>
<p class="products-detailBox__stitle">特定原材料</p>
<ul class="products-detailBox__material">
    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0101_off.png" width="60" height="77" alt="卵:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0102_off.png" width="60" height="77" alt="乳:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0103_off.png" width="60" height="77" alt="小麦:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0104_off.png" width="60" height="77" alt="えび:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0105_off.png" width="60" height="77" alt="かに:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0106_off.png" width="60" height="77" alt="そば:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0107_off.png" width="60" height="77" alt="落花生:不使用"></li></ul>
<p class="products-detailBox__note02">卵・乳を含む製品と共通の設備で製造しています。</p>

<p class="products-detailBox__stitle">特定原材料に準ずるもの</p>
<ul class="products-detailBox__material">
    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0201_off.png" width="60" height="77" alt="あわび:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0202_off.png" width="60" height="77" alt="いか:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0203_off.png" width="60" height="77" alt="いくら:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0204_off.png" width="60" height="77" alt="オレンジ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0205_off.png" width="60" height="77" alt="キウイフルーツ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0206_off.png" width="60" height="77" alt="牛肉:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0207_off.png" width="60" height="77" alt="くるみ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0208_off.png" width="60" height="77" alt="さけ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0209_off.png" width="60" height="77" alt="さば:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0210_off.png" width="60" height="77" alt="大豆:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0211_off.png" width="60" height="77" alt="鶏肉:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0212_off.png" width="60" height="77" alt="バナナ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0213_off.png" width="60" height="77" alt="豚肉:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0214_off.png" width="60" height="77" alt="まつたけ:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0215_off.png" width="60" height="77" alt="もも:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0216_off.png" width="60" height="77" alt="やまいも:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0217_off.png" width="60" height="77" alt="りんご:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0218_off.png" width="60" height="77" alt="ゼラチン:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0219_off.png" width="60" height="77" alt="ごま:不使用"></li>    <li class="products-detailBox__material__item"><img src="img/detail/icon_material/icon_material_0220_off.png" width="60" height="77" alt="カシューナッツ:不使用"></li></ul>


</dd>
<dt class="products-detailBox__term">製造工場</dt>
<dd class="products-detailBox__desc">国内で製造しています。</dd>
<dt class="products-detailBox__term">JANコード</dt>
<dd class="products-detailBox__desc">4902888341731</dd>
</dl>
</div>
<p class="products-detailBox__txtDate">※2018年01月10日現在の情報です。</p>
<!-- /.products-detailBox --></div>

長くてすみません。

このうち、上から2番目のdd要素である内容量の<dd class="products-detailBox__desc">135ml</dd>の135mlという情報を取得したいです。

試しに、

serving_size = page.at('.products-detailBox__list dt:nth-child(2) dd').inner_text

追記:以下のコードもだめでした。

serving_size = page.at('dl dt:nth-child(2) dd').inner_text

としてみたのですが、上手く取得できません。

検索してみたのですが、有用な情報がなかったので、おわかりの方がいましたらご教授いただけますと幸いです。

追記:以下にRubyのコードを記載いたします。

require 'mechanize'

class Scraping
  def self.icecream_urls
    links = [] # 個別ページのリンクを保存する配列
    agent = Mechanize.new
    (1..4).each do |i|
        current_page = agent.get("http://www.morinaga.co.jp/products/list.php?id=030#{i}")
        elements = current_page.search('.products__list__item a')
        elements.each do |ele|
            links << ele.get_attribute('href')
        end
    end


    links.each do |link| #get_product に引数(個別ページのURL)を渡す
      get_product('http://www.morinaga.co.jp/products/' + link)
    end
  end

  def self.get_product(link) # 個別ページからアイスの情報を取得する
    agent = Mechanize.new
    page = agent.get(link)
    product_name = page.at('.headingType01__txt').inner_text # 商品名
    image_url = page.at('.products-mainImg img').get_attribute('src') # 商品画像

    serving_size = page.search('dd:nth-child(13)').inner_text # 内容量

    product = Product.where(name: product_name, image_url: image_url, serving_size: serving_size).first_or_initialize

    product.save
  end
end
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • jun68ykt

    2018/01/10 21:21

    可能ならばRubyのコードも全体を載せてもらったほうが、より確実に回答を望めると思います。

    キャンセル

  • vol_ay

    2018/01/10 21:50

    コメントありがとうございます。Rubyのコードを追記いたしました。

    キャンセル

回答 3

+1

xpathで指定してはいかがですか?

Nokogiriを使っていると想定してですが。

require 'nokogiri'
require 'open-uri'

urls = 'http://hogehoge.com'

page = Nokogiri::HTML(open(urls))

puts page.xpath('//dd[@class="products-detailBox__desc"][2]').inner_text

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

2番目に内容量が来ることが確定していてcssのほうが分かりやすいなら、こんなのでもいけます

page.at_css("dd:nth-of-type(2)").inner_text

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

"内容量"の次ということでしょうか。

serving_size = page.at('//dt[text()="内容量"]/following-sibling::dd[position()=1]').inner_text

実用的には、一発で目的ノードを選択する記述を考えるより、Arrayで結果を得て順番に探す方が良いかも知れません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/10 22:49 編集

    ありがとうございます!内容量の数値が取得できました。
    もし可能であれば、'//dt[text()="内容量"]/following-sibling::dd[position()=1]' という記述方法について簡単にご説明いただけますと幸いです。
    誠に勝手ながら、初心者なので次回以降に活かしたいと思いまして…。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.25%

関連した質問

同じタグがついた質問を見る

  • Ruby on Rails

    5359questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。