【Ruby+Nokogiri】でyahooニュースをスクレイピング→CSV保存

以前Pythonの**BeautifulSoup**を使ってスクレイピングをしたことがあったのですが、Rubyでも**Nokogiri**というライブラリで実現できるということで試してみました。

でまずは完成コード、完成品から

```ruby:scraping.rb

require 'nokogiri'
require 'open-uri'
require "csv"

require "byebug"


url_base = "https://news.yahoo.co.jp/"

def get_categories(url)
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  categories = doc.css(".yjnHeader_sub_cat li a")
  categories.map do |category|
    cat_name = category.text
    cat = category[:href]
  end
end

@cat_list = get_categories(url_base)
@infos = []


@cat_list.each do |cat|
  url = "#{url_base + cat}"
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  titles = doc.css(".topicsListItem a")
  i = 1
  titles.each do |title|
    @infos << [i,title.text]
    i += 1
  end
end

CSV.open("result.csv", "w") do |csv|
  @infos.each do |info|
    csv << info
    puts "-------------------------------"
    puts info
  end
end

```

![スクリーンショット (94).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/49e39e27-f3e5-b19f-8172-1588a2046233.png)


それぞれ解説していきます。

## ファイルの読み込み

```
require 'nokogiri'
require 'open-uri'
require "csv"

require "byebug"

```

今回使うのは**Nokogiriとopen-uri**、そしてCSV保存のための**csv**です。

NokogiriはHTMLやXMLコードを解析し、セレクターによって抜き出してくれるRubyのライブラリです。セレクターは**css**の他に**xpath**でも指定できるので、複雑な構造をしたページでもスクレイピングがスムーズに行えます。

## スクレイピング先のページ構造

![スクリーンショット (88).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/8d1ce420-9b7a-73fc-e96a-78bb46f1ade0.png)
![スクリーンショット (89).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/00595afb-ce1c-dcc2-0e45-8da0a05cb577.png)

今回は各トピックのタイトルを取得していき、最終的にCSVファイルにまとめます。

トピックのページは**yjnHeader_sub**というクラスのliの中にあるリンク(aタグ)から繋がっているようです。


## カテゴリーごとのリンクを取得

```ruby
url_base = "https://news.yahoo.co.jp/"

def get_categories(url)
  html = open(url)
  #parseで読み込んだURLのHTMLコードを取得
  doc = Nokogiri::HTML.parse(html)
  #cssセレクターを使い、先ほどのカテゴリーに繋がっているaタグをすべて取得
  categories = doc.css(".yjnHeader_sub_cat li a")
  categories.map do |category|
    #取得したaタグからhrefの中身(リンク先のURL)をひとつづつ取り出して返します
    cat = category[:href]
  end
end

#@cat_listとして取得したリンクをまとめておきます
@cat_list = get_categories(url_base)
```

## トピックのタイトルを取得

先ほど取得したリンクを用いてトピックごとのタイトルを取得していきます。

```ruby

@infos = []

@cat_list.each do |cat|
　#トピックページのURLは元のURL + 取得したURLのため
  url = "#{url_base + cat}"
  html = open(url)
  doc = Nokogiri::HTML.parse(html)
  titles = doc.css(".topicsListItem a")
  i = 1
  titles.each do |title|
　　#CSVにまとめるためにトピックのナンバーとタイトルをセットで格納します
    @infos << [i,title.text]
    i += 1
  end
end
```

## 取得したタイトルをCSVにまとめる

最後にまとめたタイトルをCSVに保存します。

```ruby

#CSVライブラリを使い"result.csv"を新規作成
CSV.open("result.csv", "w") do |csv|
  @infos.each do |info|
　　#csvに追加するとともにログとして使いした項目を出力しています。
    csv << info
    puts "-------------------------------"
    puts info
  end
end
```


![スクリーンショット (90).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/5c45303a-04f6-cbfb-294c-042b54b1a2b0.png)

## 文字化け対策

しかしこのままではおそらく文字化けしてしまうためBOMを付けて保存し直します。
(本来であればCSV保存中に行うのが正しいのですが、上手くいかなかったためいったんこちらで対応しました)

"result.csv"を**メモ帳**でopenして、上書き保存を選択。


![スクリーンショット (92).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/e2237569-6892-ceb8-f512-533c5a860d46.png)


![スクリーンショット (93).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/2ec3573e-9dc8-4a90-17e1-dac0ecd8db33.png)

この時**UTF-8(BOM付)**を選択して保存し直してください。

![スクリーンショット (94).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536448/ad6344df-765f-0bab-c673-f2a02f332e67.png)

もう一度csvを開くと文字化けが解消されています。

## 最後に

まだまだ至らない点が多いと思うのでご指摘があればコメントいただけると幸いです。