80469


**環境**

ruby 2.7.2  
rails 6.1  
macOS

**前提**

ポートフォリオ作成のため、現在自作アプリを実装しております。  
その中で、DBに必要なデータを保存するため、Rubyのnokogiriでスクレイピングを行なっています。

**解決したいこと**

下記のhref属性値から緯度、経度の値を取得したいです（34.907757が緯度で134.859368が経度です）

[code]

    <a target="_blank" href="http://maps.google.com/?q=34.907757,134.859368">GoogleMapsで見る</a>
    
[/code]

**試したこと**

nokogiriに関する記事を探したのですが、実現させたいことが記述している記事を見つけることができませんでした、、

詳しい方がいましたら是非アドバイスいただけると幸いです。  
何卒よろしくお願いします。

**該当するソースコード**

`db/schema.rb`

[code]

      create_table "spots", force: :cascade do |t|
        t.string "name", null: false
        t.string "time"
        t.string "location"
        t.text "feature"
        t.string "image"
        t.string "url"
        t.float "latitude"
        t.float "longitude"
        t.integer "area_id"
        t.datetime "created_at", precision: 6, null: false
        t.datetime "updated_at", precision: 6, null: false
        t.index ["area_id"], name: "index_spots_on_area_id"
      end
    
[/code]

`app/models/scraping.rb`

[code]

    class Scraping < ApplicationRecord
      require 'open-uri'
      require 'nokogiri'
    
      def self.spot_scrape
        base_url = 'https://loconavi.jp/'
        features_url = 'features/hananomeisho'
        url = "#{base_url}#{features_url}"
    
        20.times do |i|
          html = URI.open(url).read
          doc = Nokogiri::HTML.parse(html)
    
          flower_href = doc.css('.flower')[i]["href"]
          next_url = "#{base_url}#{flower_href}"
    
          3.times do |i|
            html = URI.open(next_url).read
            doc = Nokogiri::HTML.parse(html)
            # href属性値の取得(相対URL)。また、_[i]_はtimesメソッドの変数で、繰り返すことに1づつ数値が増えていく
            href = doc.css('.flower-spot')[i]["href"]
            # 相対URLから絶対URLを作成
            link_url = "#{base_url}#{href}"
    
            html = URI.open(link_url).read
            doc = Nokogiri::HTML.parse(html)
    
            doc.css('.main-left-layout').each do | node |
              sleep 1
              # DBに取得したデータを保存
              Spot.create!(
                name: node.at_css('.block').inner_text,
                time: node.css('.spot-info > .t-cell')[1].inner_text,
                location: node.css('.spot-info > .t-row > .t-cell')[1].inner_text,
                feature: node.at_css('.text').inner_text,
                image: node.at_css('img').attribute('src'),
                url: node.at_css('#information p a')["href"],
                latitude: node.at_css('#information p > a')[1]["href"], # コード未完成
                longitude: node.at_css('#information p > a')[1]["href"] # コード未完成
              )
            end
          end
        end
      end
    
[/code]


CC BY-SA 4.0
2021-07-29T12:28:06.780
80467
2021-07-30T11:39:35.633
46968
question
ruby-on-rails ruby web-scraping
スクレイピングでhref属性値の一部の値を取得したい


正規表現を利用した方法を紹介します。

記述してある `node.at_css('#information p > a')[1]["href"]`
ではうまくnodeがとれなかったので範囲内のaタグのリンク先を見て探すようにしました

[code]

    base_url = "https://loconavi.jp/"
    features_url = "features/hananomeisho"
    url = "#{base_url}#{features_url}"
    # google mapsの正規表現、緯度経度をキャプチャする
    maps_regexp = /\Ahttp:\/\/maps\.google\.com\/\?q=(.+),(.+)\z/
    
    # -- 省略 ---
        doc.css(".main-left-layout").each do |node|
          latitude, longitude = nil, nil
          node.css("a").each do |node|
            # aタグを絞り込み正規表現にマッチするリンクを探す
            result = maps_regexp.match(node[:href])
    
            # アンマッチの場合はnilなので次の要素へ
            next if result.nil?
    
            # match:マッチ全体 latitude:緯度 longitude:経度
            match, latitude, longitude = result.to_a
          end
          puts latitude
          puts longitude
          # DBに取得したデータを保存
    # -- 省略 ---
        end
    
[/code]


CC BY-SA 4.0
2021-07-29T15:37:39.257
80469
2021-07-29T15:37:39.257
298
80467
answer


以下は [String#scan](https://docs.ruby-
lang.org/ja/latest/method/String/i/scan.html) を使う方法です。

[code]

    doc.css('.main-left-layout').each do |node|
      coord = node.css('a').each {|m|
        coord = m['href'].scan(/http:\/\/maps\.google\.com\/\?q=([\d.]+),([\d.]+)/)
        if coord.any?
          break {:lat => coord[0][0], :lng => coord[0][1]}
        end
      }
      p coord
    
      # DBに取得したデータを保存
                   :
    end
    
    ## p coord の出力結果
    {:lat=>"34.907757", :lng=>"134.859368"}
    
[/code]


CC BY-SA 4.0
2021-07-29T17:58:12.950
80472
2021-07-29T18:03:51.027
2021-07-29T18:03:51.027
47127
47127
80467
answer


回答いただき、ありがとうございます。  
回答をもとにコードを修正した所、緯度、経度の値を取得し、無事DBに保存することができました。

`app/models/scraping.rb`

[code]

    class Scraping < ApplicationRecord
      require 'open-uri'
      require 'nokogiri'
    
      def self.spot_scrape
        base_url = 'https://loconavi.jp/'
        features_url = 'features/hananomeisho'
        url = "#{base_url}#{features_url}"
        # google mapsの正規表現、緯度経度をキャプチャする
        maps_regexp = /\Ahttp:\/\/maps\.google\.com\/\?q=(.+),(.+)\z/
    
        20.times do |i|
          html = URI.open(url).read
          doc = Nokogiri::HTML.parse(html)
    
          if doc.css('.flower')[i] == nil
            break
          end
    
          flower_href = doc.css('.flower')[i]["href"]
          next_url = "#{base_url}#{flower_href}"
    
          3.times do |i|
            html = URI.open(next_url).read
            doc = Nokogiri::HTML.parse(html)
    
            # 取得したいスポットがない場合、次のループに移行
            if doc.css('.flower-spot')[i] == nil
              next
            end
            # href属性値の取得(相対URL)。また、_[i]_はtimesメソッドの変数で、繰り返すことに1づつ数値が増えていく
            href = doc.css('.flower-spot')[i]["href"]
            # 相対URLから絶対URLを作成
            link_url = "#{base_url}#{href}"
    
            html = URI.open(link_url).read
            doc = Nokogiri::HTML.parse(html)
    
            doc.css('.main-left-layout').each do | node |
              sleep 0.5
    
              latitude, longitude = nil, nil
              node.css("a").each do |node|
                # aタグを絞り込み正規表現にマッチするリンクを探す
                result = maps_regexp.match(node[:href])
                # アンマッチの場合はnilなので次の要素へ
                next if result.nil?
                # match:マッチ全体 latitude:緯度 longitude:経度
                match, latitude, longitude = result.to_a
              end
    
              # データが取得できなかった場合、次のループに移行
              if node.at_css('.block') == nil \
                || node.css('.spot-info > .t-cell')[1] == nil \
                || node.css('.spot-info > .t-row > .t-cell')[1] == nil \
                || node.at_css('.text') == nil \
                || node.at_css('img') == nil \
                || node.at_css('#information p a') == nil
                next
              end
    
              # DBに取得したデータを保存
              Spot.create!(
                name: node.at_css('.block').inner_text,
                time: node.css('.spot-info > .t-cell')[1].inner_text,
                location: node.css('.spot-info > .t-row > .t-cell')[1].inner_text,
                feature: node.at_css('.text').inner_text,
                image: node.at_css('img').attribute('src'),
                url: node.at_css('#information p a')["href"],
                latitude: latitude,
                longitude: longitude
              )
            end
          end
        end
      end
    end
    
[/code]


CC BY-SA 4.0
2021-07-30T11:39:35.633
80489
2021-07-30T11:39:35.633
46968
80467
answer