gemを使ってスクレイピングしてみた

# はじめに
本記事は、[2021 RUNTEQアドベントカレンダー](https://qiita.com/advent-calendar/2021/runteq)5日目の記事になります！  
現在ポートフォリオを作成しており、その作成過程でスクレイピングを用いて必要な情報を取得しDBに格納しました。今回はそこで使用した**gem Mechanize**について書いていこうと思います！
<br>
# この記事でわかること
 
**gem Mechanizeについて**

- できること
- 提供しているメソッド
- PFで用いたDBに格納する方法 
<br>
<br>
<br>

##できること

まず概要を[公式](https://github.com/sparklemotion/mechanize)より見てみます

> Mechanizeライブラリは、ウェブサイトとの対話を自動化するために使用されます。Mechanizeは、自動的にクッキーを保存、送信し、リダイレクトを行い、リンクをたどり、フォームを送信することができます。フォームフィールドは入力して送信することができます。また、Mechanizeは、あなたが訪問したサイトを履歴として記録します。
 
ざっくりとした解釈ですが、自動的にwebサイトに訪問して、フォームがあれば入力もできちゃうよ〜ってことですね。 webサイトを自動で訪問できるので、そこでスクレイピングしたいページにとび、取得したいデータのみ抽出するという処理も書けちゃうわけです
<br>
##提供しているメソッド

### 下準備  
メソッドの説明に入っていくのですが、gemをinstallすると、mechanizeクラスが使えるようになります。
公式では下準備としてインスタンス化してくださいとあるので、下記のように設定します

```ruby
# 公式には下記の記載がありますが、Ruby1.9以降では必要ないのでコメントアウトします
# require 'rubygems'
require 'mechanize'

class Scraping
  agent = Mechanize.new
end

```
<br>

### get 
スクレイピングしたいwebサイトのHTMLを取得してくれます。

```ruby
require 'mechanize'

class Scraping
  def self.hoge
    agent = Mechanize.new
    page = agent.get("https://www.hogehoge.co.jp/")
  end
end


```
<br>
コンソールで実行してみます
<br>

```
irb(main):001:0> Scraping.hoge
=> #<Mechanize::Page
 {url #<URI::HTTPS https://www.hogehoge.co.jp/>}
 {meta_refresh}
 {title "公式サイト | Japan"}
 {iframes
  #<Mechanize::Page::Frame
   nil
...
  #<Mechanize::Page::Link "ホーム" "/">
  #<Mechanize::Page::Link "\n" + "メニュー\n" + "\n" + "\n" "/menu/">
  #<Mechanize::Page::Link "おすすめ" "/menu/">
  #<Mechanize::Page::Link "バーガー" "/menu/burger/">
  #<Mechanize::Page::Link "セット" "/menu/set/">
  #<Mechanize::Page::Link "サイドメニュー" "/menu/side/">
  #<Mechanize::Page::Link "ドリンク" "/menu/drink/">
...
```

これ以上は長くなってしまうので省きますが、これだけでもざっくりページの情報を得られているのがわかると思います〜〜
<br>

### search 
cssを指定するのと同じ要領で要素を指定できます。タグはそのまま指定。
classを指定する際は、「.(ドット) class名」。idを指定する際は、「#(シャープ) id名」といった感じですね。返り値は配列の形式で返ってきます。
試しに`li a` を指定し、格納した配列を出力してみます

```ruby
class Scraping
  def self.hoge
    agent = Mechanize.new
    page = agent.get("https://www.hogehoge.co.jp/")
    elements = page.search('li a')
    puts elements
  end
end
```
<br>
コンソール
<br>

```
irb(main):001:0> Scraping.hoge
<a href="/" class="no-underline block h-xs line-height-md p-2:lg my-0 font-semibold active">ホーム</a>
<a href="/menu/" class="no-underline block h-xs line-height-md p-2:lg my-0 font-semibold flex justify-between items-center">
<span>メニュー</span>

<span class="header-submenu-toggle-btn"></span>
</a>
<a href="/menu/" class="no-underline">おすすめ</a>
<a href="/menu/burger/" class="no-underline">バーガー</a>
<a href="/menu/set/" class="no-underline">セット</a>
<a href="/menu/side/" class="no-underline">サイドメニュー</a>
<a href="/menu/drink/" class="no-underline">ドリンク</a>...

```

先程とは違い、要素を取得できているのがわかります！
<br>  
### at
searchメソッドと同じ要領で要素を指定し、該当する1件のみ取得することができます
先程の文法のまま、searchの部分をatメソッドに変更すると、コンソール上では1件のみ取得できるのがわかります！

```
irb(main):001:0> Scraping.hoge
<a href="/" class="no-underline block h-xs line-height-md p-2:lg my-0 font-semibold active">ホーム</a>
```
<br>
### get_attribute 
タグの属性値を取得することができます。属性値とは例えばaタグの中の、href属性に指定されてるURLのことを指します。先程searchメソッドで取得した配列のうち、get_attributeメソッドを使用して、URLの情報を取得してみます

```ruby
class Scraping
  def self.hoge
    agent = Mechanize.new
    page = agent.get("https://www.hogehoge.co.jp/")
    elements = page.search('li a')
    urls = []
    elements.each do |element|
      urls << element.get_attribute(:href)
    end
    puts urls
  end
end
```
<br>
コンソール
<br>

```
irb(main):001:0> Scraping.hoge
/
/menu/
/menu/
/menu/burger/
/menu/set/
/menu/side/
/menu/drink/
```
<br>

### inner_text
searchメソッドやatメソッドではタグも含めて全て取り出していましたが、inner_textメソッドではタグの中身のテキストだけを取り出すことができます。
searchで取得した情報にinner_textメソッドを使用してみましょう

```ruby
class Scraping
  def self.hoge
    agent = Mechanize.new
    page = agent.get("https://www.hogehoge.co.jp/")
    elements = page.search('li a').inner_text
    puts elements
  end
end

```
<br>
コンソール
<br>

```
irb(main):001:0> Scraping.hoge
ホーム
メニュー
おすすめバーガーセットをご案内私たちの責任お店・サービス
季節の商品はこちらから！
会員登録不要
...

```
テキストだけ取得することができました。
<br>
このような形で、サイトに応じてメソッドを使用すればDBに必要な情報を好きなように取得し、格納することもできそうです
<br>

## PFで用いたDBに格納する方法

上記のメソッドを活用して、実際にDBにスクレイピングしたデータを格納していきます。
仮想のページから、簡易的ですが商品名(name)、価格(price)、説明(description)のカラムを持ったfoodsテーブルを作成し、こちらに格納していきます。テーブル作成は割愛します

```ruby
require 'mechanize'

class Scraping
  def self.hoge
    agent = Mechanize.new
    links = []

    page = agent.get("https://www.hogehoge.co.jp/menu/burger/")
    
　# elementsには商品詳細へのHTMLが格納されています
    elements = page.search('.product-list-card a')
　

　# each doメソッドで各HTMLに対し処理を実行。get_attributeメソッドを活用してリンクを取得しlinksに格納しています
    elements.each do |ele|
    links << ele.get_attribute(:href)
    end


　#　取得した各リンクを引数としget_foodメソッドを実行します
    links.each do |link|
      get_food("https://www.hogehoge.co.jp/#{link}")
    end
  end
```
<br>
まず第一段階で一覧ページから、各商品詳細情報のリンクを取得し、linksの配列に格納していきます
<br>

```ruby
require 'mechanize'

class Scraping
  def self.hoge
     ...
  end

  def self.get_food(link)
    agent = Mechanize.new
    page = agent.get(link)

    name = page.at('.pdp__product-info h1').inner_text
    price = page.at('.items-baseline').inner_text
    description = page.at('.pdp__product-info p').inner_text
    Food.create(name: name, price: price, description: description)
  end
end

```
第二段階で、取得した各商品詳細情報から、必要な情報を指定して取得し、最後にcreateするとDBに格納することができます！！

<br>
# 終わりに
最後までご一読いただきありがとうございました！　
同じように、ポートフォリオ作成を考えていらっしゃる方の参考になれば幸いです！
また、もし間違っている点等あれば、ご指摘いただけると嬉しいです。ありがとうございました！
<br>

# 参考URL
- [Github 公式](https://github.com/sparklemotion/mechanize)

- [Rails スクレイピング手法 Mechanizeの使い方](https://qiita.com/shizuma/items/d04facaa732f606f00ff)

- [スクレイピングでサイト情報を取得し、データベースにストアするまでの流れをまとめてみた[Rails]](https://qiita.com/takuyanin/items/01256773e1cbfc84f51a#%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AEhtml%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)