80892


**環境**

ruby 2.7.2  
rails 6.1  
macOS

**前提**

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

以前、スクレイピングについて質問させていただき、取得したデータをDBに保存することができたのですが、スクレイピング先のサイトの変更があったため、CSVに出力してデータを保持した方が良いと感じ、現在取り組んでいます。

**解決したいこと**

取得したデータをCSVファイルに出力したいです。

**試したこと**

CSV出力に関する参考記事を元にコードを修正し、コンソールで実行した所、繰り返し処理がうまく行われておらず、最初の1件しかデータを取得することができていませんでした（下記）

`db/csv_data/spot_list.csv`

[code]

    "name","time","location","feature","image","url","latitude","longitude"
    "兵庫県立フラワーセンターのサルビア","6月下旬〜8月下旬","兵庫県加西市豊倉町飯森1282−1 兵庫県立フラワーセンター","園内中央には豊かな水に水鳥が遊ぶ亀の倉池を配し、背に野鳥のつどう緑濃い飯盛山を取り込んだ、約46haにおよぶ松の自然林をそのままに生す全国でも有数の花の公園。池を巡る遊歩道を歩けば、季節の花が咲き競う四季の花壇、大温室、花と彫刻の道、かおりの森など、約4,500種類の花々が咲く。園内の中央にある亀の倉池の近くには大小さまざまな花壇が展開。中央花壇、四季の花壇、池辺の花壇等合わせて12,000㎡の花壇は、春にはチューリップ、夏から秋にはサルビア、マリーゴールド、ベゴニア・センパフローレンス、菊、冬はパンジー、ハボタンなどで埋めつくされる。4月の春からは460品種22万本のチューリップが次々と開花します。","https://image-loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/40430/ac27a2ba-3576-4b9b-bf3c-2bac4089cbe6.jpg","http://www.hyogo-park.or.jp/flower-center/","34.907757","134.859368"
    
    
[/code]

byebugでモデル内のデバックしながらファイルの書き込み部分のコードを変更させるなど色々試してみたのですが、解決には至っておりません。

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

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

`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'
      require 'csv'
    
      def self.spot_list_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/
    
        # テーブルヘッダーの作成
        header = ['name', 'time', 'location', 'feature', 'image', 'url', 'latitude', 'longitude']
        rows = []
        rows << header
    
        # timesメソッドを使用して20回ループさせる
        20.times do |i|
          html = URI.open(url).read
          doc = Nokogiri::HTML.parse(html)
    
    　　     # # 該当クラスが存在しない場合、ループを終了させる
          if doc.css('.flower')[i] == nil
            break
          end
    
          # _[i]_はtimesメソッドの変数で、繰り返すことに1づつ数値が増えていく
          flower_href = doc.css('.flower')[i]["href"]
    
          # 相対URL(flower_href)と結合させて絶対URLを作成
          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 = doc.css('.flower-spot')[i]["href"]
            link_url = "#{base_url}#{href}"
    
            html = URI.open(link_url).read
            doc = Nokogiri::HTML.parse(html)
    
            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])
    
                # アンマッチの場合、次の要素に移行
                if result.nil?
                  next
                end
    
                # 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
    
              # 各カラムのデータを取得
              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').value
              url = node.at_css('#information p a')["href"]
              latitude = latitude
              longitude = longitude
    
              rows << [name, time, location, feature, image, url, latitude, longitude]
            end
          end
        end
        # CSVに書き込み
        CSV.open('db/csv_data/spot_list.csv', 'w', :force_quotes=>true) do |csv|
          rows.each do |row|
            csv << row
          end
        end
      end
    
[/code]


CC BY-SA 4.0
2021-08-17T05:59:03.503
80864
2021-08-18T23:05:43.393
2021-08-17T13:32:26.287
46968
46968
question
ruby-on-rails ruby csv web-scraping nokogiri
画面遷移を伴うスクレイピングで取得したデータをCSVファイルに出力したい


コメントに書いた通り、`url` 変数の上書きが原因かと思います。

以下は本題からは離れた話になります。

> データが取得できずnilになる場合があるため、nextを使用して次のループに移行するよう記述した次第です。

この部分の判定処理に関しては Ruby 2.3.0 で導入された [Safe navigation operator](https://bugs.ruby-
lang.org/issues/11537) を使うと簡潔に記述できます。

例えば以下の処理では `NoMethodError`(exception)が発生しますが、

[code]

    nil[0]
    
    NoMethodError (undefined method `[]' for nil:NilClass)
    
[/code]

Safe navigation operator を使うと exception は発生しません。

[code]

    nil&.[](0)
    => nil
    
[/code]

以下は `Scraping.spot_list_scrape` メソッドを取り出して書き変えたコードです。参考にしてみてください。

[code]

    require 'open-uri'
    require 'nokogiri'
    require 'csv'
    
    BASE_URL = 'https://loconavi.jp'
    FEATURES_URL = '/features/hananomeisho'
    
    # google mapsの正規表現、緯度・経度をキャプチャする
    MAPS_REGEXP = /\Ahttp:\/\/maps\.google\.com\/\?q=(.+),(.+)\z/
    
    # テーブルヘッダーの作成
    header = ['name', 'time', 'location', 'feature', 'image', 'url', 'latitude', 'longitude']
    rows = [header]
    
    # scraping
    html = URI.open(BASE_URL + FEATURES_URL).read
    doc = Nokogiri::HTML.parse(html)
    
    doc.css('.flower').each{|flower|
      html = URI.open(BASE_URL + flower['href']).read
      doc = Nokogiri::HTML.parse(html)
    
      doc.css('.flower-spot')[0..3].each{|spot|
        html = URI.open(BASE_URL + spot['href']).read
        doc = Nokogiri::HTML.parse(html)
    
        doc.css('.main-left-layout').each {|main|
          coords = main.css('a').each{|a|
            c = MAPS_REGEXP.match(a[:href])
            break c.to_a[1..2] unless c.nil?
          }
          next if coords.nil?
    
          # 各カラムのデータを取得
          name = main.at_css('.block')&.inner_text
          time = main.css('.spot-info > .t-cell')[1]&.inner_text
          location = main.css('.spot-info > .t-row > .t-cell')[1]&.inner_text
          feature = main.at_css('.text')&.inner_text
          image = main.at_css('img')&.attribute('src')&.value
          url = main.at_css('#information p a')&.[](:href)
    
          # 全てのデータが取得できれば CSV へ追加
          row = [name, time, location, feature, image, url]
          rows << row + coords if row.all?
        }
      }
    }
    
    # CSVに書き込み
    CSV.open('spot_list.csv', 'w', :force_quotes=>true) do |csv|
      rows.each do |row|
        csv << row
      end
    end
    
[/code]

**取得結果**

| name | time | location | feature | image | url | latitude | longitude  
---|---|---|---|---|---|---|---|---  
1 | 兵庫県立フラワーセンターのサルビア | 6月下旬〜8月下旬 | 兵庫県加西市豊倉町飯森1282−1 兵庫県立フラワーセンター |
園内中央には豊かな水に水鳥が遊ぶ亀の倉池... | ![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/40430/ac27a2ba-3576-4b9b-bf3c-2bac4089cbe6.jpg)
| <http://www.hyogo-park.or.jp/flower-center/> | 34.9078 | 134.859  
2 | 千秋公園の蓮 | 7月上旬～8月中旬 | 秋田県秋田市千秋公園1 | 267年続いた秋田藩20万石・佐竹氏の居... |
![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/52534/8c4d7ba6-8f27-4476-851e-9758b2d28160.jpg)
| <http://www.city.akita.akita.jp/city/ur/pc/sensyukouen/default.htm> |
39.7229 | 140.124  
3 | ひまわりガーデン武蔵村山 | 7月中旬〜8月中旬 | 東京都武蔵村山市緑が丘１４６０ ひまわりガーデン武蔵村山 |
村山団地北西部の「ひまわりガーデン武蔵村... | ![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/photo/image/image/7935/5714c69e-e73a-48a3-99c6-bb7e983a7f7f.jpeg)
| <http://www.city.musashimurayama.lg.jp/kankou/spots/himawari/1001598.html> |
35.7466 | 139.407  
4 | サンテパルクたはらのひまわり | 7月中旬〜8月中旬 | 愛知県田原市野田町芦ヶ池8 サンテパルクたはら |
愛知県田原市の農業をテーマにした体験型テ... | ![](https://image-
loconavi.tokubai.co.jp/w=150,h=150,a=2/uploads/spot/placard/image/50197/178c36b2-725a-4dc0-a983-323954df1473.jpg)
| <http://www.santepark.com> | 34.6428 | 137.201  
5 | 第1なぎさ公園のひまわり | 7月中旬〜8月中旬 | 滋賀県守山市今浜町 第1なぎさ公園 | 琵琶湖の東岸にある自然公園の第1なぎさ公... |
![](https://image-
loconavi.tokubai.co.jp/w=150,h=150,a=2/uploads/photo/image/image/2354/1e2ea260-61c3-4f19-ad7a-56492c671e4b.JPG)
| <https://www.moriyamayamamori.jp/spot/329> | 35.1268 | 135.95  
6 | 三ツ石山の紅葉 | 9月中旬 | 岩手県岩手郡雫石町（その他） 県道212号線 | 東北百名山に選ばれている三ツ石山の紅葉は... |
![](https://image-
loconavi.tokubai.co.jp/w=280,h=280,a=2/uploads/spot/placard/image/79390/3427f7f5-7860-4c6c-821e-110116d6a3a9.jpg)
| <http://shizukuishi-kanko.gr.jp/> | 39.8515 | 140.896  
7 | 能取湖の紅葉 | 9月上旬〜9月下旬 | 北海道網走市卯原内60-3 能取湖サンゴ草群生地 | 能取湖に広がるサンゴ草の紅葉網走市にある... |
![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/77276/2b3feacf-979a-4c60-9c75-69379c35d9eb.jpg)
| <http://abashiri.jp/tabinavi/index.html> | 44.0136 | 144.116  
8 | 大雪山(黒岳)の紅葉 | 9月中旬～9月下旬 | 北海道上川郡上川町層雲峡6 | 北海道上川郡上川町 大雪山(黒岳)の紅葉... |
![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/photo/image/image/34003/f5a17d2f-a43a-4f52-8d14-d5575281c4c8.jpg)
| <http://www.town.kamikawa.hokkaido.jp> | 43.7111 | 142.94  
9 | 十勝岳温泉郷の紅葉 | 9月中旬～9月下旬 | 北海道空知郡上富良野町十勝岳温泉 | 上富良野町にある十勝岳温泉郷の紅葉は例年... |
![](https://image-
loconavi.tokubai.co.jp/w=280,h=280,a=2/uploads/photo/image/image/24063/c57e3986-e5d7-4fbd-a384-ba9aa5d2308d.jpg)
| <http://kamifurano.jp> | 43.4136 | 142.643  
10 | 滋賀農業公園ブルーメの丘のバラ | 5月下旬～8月中旬 | 滋賀県蒲生郡日野町西大路843 | ドイツ・バイエルン地方の農村を再現した観... |
![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/47679/19276ed5-2a2b-42b3-a6ab-3643a5f6b063.jpg)
| <http://www.blumenooka.jp> | 35.0142 | 136.28  
11 | イングリッシュローズガーデン | 6月〜9月 | 長野県北佐久郡軽井沢町長倉 軽井沢タリアセン | バラの育種家として著名なデイビッド・オー...
| ![](https://image-
loconavi.tokubai.co.jp/w=280,h=280,a=2/uploads/photo/image/image/18029/ba8457c7-a366-44b8-aa1d-185a2132132f.JPG)
| <http://www.karuizawataliesin.com/> | 36.3284 | 138.597  
12 | タングラム斑尾のラベンダー園 | 7月上旬～8月中旬 | 長野県上水内郡信濃町古海 タングラム斑尾 ラベンダー園 |
長野県上水内郡信濃町にある「タングラム斑... | ![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/52559/b2eb28d6-6a7e-4a00-b189-0e715f0532d6.jpg)
| <https://www.tangram.jp/index.php> | 36.8537 | 138.263  
13 | かんのファームのラベンダー | 6月中旬〜10月上旬 | 北海道空知郡上富良野町西12線北36号 |
上富良野町と美瑛町の境界、美馬牛峠で花と... | ![](https://image-
loconavi.tokubai.co.jp/w=400,h=400,a=2/uploads/spot/placard/image/52903/de7d4914-c9e3-4e0c-b608-e85986435e74.jpg)
| <http://www.kanno-farm.com/> | 43.5429 | 142.439


CC BY-SA 4.0
2021-08-18T13:58:32.163
80892
2021-08-18T23:05:43.393
2021-08-18T23:05:43.393
47127
47127
80864
answer