公開日: | 最終更新日:
【Rails】 Mechanizeを使ってスクレイピングをしよう
![ぴっかちゃん](/packs/media/images/chara-right-e7bf4fa3096d5c964215b40162534f2f.png)
Mechanizeは、スクレイピングを簡単に実装することができるgemです。このgemを使うと、Webページをプログラムで操作し、情報を取得することができます。
Mechanizeとは
Mechanizeは、スクレイピング用のgemです。Webサイトからデータを抽出したり、フォームに自動的に入力して送信したりするのに便利です。このgemを使うと、WebサイトをRubyのコードで操作し、情報を取得することができます。
スクレイピングとは
スクレイピングは、Webサイトから自動的にデータを収集する技術です。特定のサイトから自分が欲しい情報のみを効率的に取得する際に役立ちます。
例えば、特定のテーマやキーワードに関するニュース記事を複数のニュースサイトから集めて、自分用のニュースサイトを作成する場合です。スクレイピングを使うことで自動化することができ、複数のサイトを巡る手間を省くことができます。
導入方法
まずは、Mechanize
をインストールするためにGemfile
に以下のコードを追記し、budle install
コマンドでインストールします。
1
gem 'mechanize'
基本的な使い方
Mechanize
を使うには、以下のようにMechanize
クラスのインスタンスを作成します。
1
agent = Mechanize.new
このインスタンスに対して、Mechanize
が提供するメソッドを使用してデータの収集などを行います。データ収集にはHTMLの構造を確認する必要があるため、ブラウザの検証モードを利用して、取得したいデータにどのようにアプローチするかを考えることが重要です。
Webページへのアクセス
まずはスクレイピングするWebサイトにアクセスする必要があります。その際に使うのがget
メソッドです。引数にはWebサイトのURLを渡します。
以下のように先ほど作成したMechanize
クラスのインスタンスに対し、get
メソッドを使用します。
1
page = agent.get('https://example.com')
データの取得方法
先ほど作成したpage
の中にスクレイピングするWebサイトの情報が入っています。この変数にMechanize
で定義されているメソッドを使って、Webページのさまざまな情報を取得することができます。
titleメソッド
スクレイピングをするWebサイトのタイトルを取得するメソッドです。
1
2
3
4
5
6
agent = Mechanize.new
page = agent.get('https://www.yahoo.co.jp/')
title = page.title
puts title
=> "Yahoo! JAPAN"
このようにHTMLのtitle
タグのテキストを取得することができます。
searchメソッド
スクレイピングをするWebページ内の特定の複数の要素を取得するメソッドです。
引数にはCSSセレクタを渡します。返り値は配列になります。
1
2
3
<div class="example">具体例1</div>
<div class="example">具体例2</div>
<div class="example">具体例3</div>
1
2
3
4
5
6
7
8
9
10
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
elements = page.search(".example")
p elements
=> [
<div class="example">具体例1</div>,
<div class="example">具体例2</div>,
<div class="example">具体例3</div>
]
スクレイピングでは、自分が欲しい情報をピンポイントで取得するため、CSSセレクタを細かく設定することがほとんどです。例えば、以下のHTMLから「サブセクション3.1の内容です」と「サブセクション3.2の内容です」のテキストコンテンツを取得する場合などです。
すべてのp
タグにはcontent
というクラスが付与されているため、クラス名だけでは目的の要素を特定することができません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="container">
<div class="section" id="section1">
<h2>セクション1</h2>
<p class="content">セクション1の内容です</p>
</div>
<div class="section" id="section2">
<h2>セクション2</h2>
<p class="content">セクション2の内容です</p>
</div>
<div class="section" id="section3">
<h2>セクション3</h2>
<div class="subsection">
<h3>サブセクション3.1</h3>
<p class="content">サブセクション3.1の内容です</p>
</div>
<div class="subsection">
<h3>サブセクション3.2</h3>
<p class="content">サブセクション3.2の内容です</p>
</div>
</div>
</div>
そのため以下のように半角スペースで区切ることで階層構造を絞り込み、ターゲットとなる要素を指定します。
1
subsections = page.search('.section .subsection .content')
atメソッド
スクレイピングをするWebページ内の特定の要素を1つだけ取得するメソッドです。
引数にはCSSセレクタを渡します。要素が複数存在する場合は一致する最初の要素のみを取得します。
1
2
3
<div class="example">具体例1</div>
<div class="example">具体例2</div>
<div class="example">具体例3</div>
1
2
3
4
5
6
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
element = page.at(".example")
puts element
=> <div class="example">具体例1</div>
inner_textメソッド
取得した要素のテキストコンテンツを取得するメソッドです。
1
2
3
<div class="example">具体例1</div>
<div class="example">具体例2</div>
<div class="example">具体例3</div>
1
2
3
4
5
6
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
element = page.at(".example")
p element.inner_text
=> "具体例1"
search
メソッドを使って複数の要素を取得した場合は、以下のようにすると要素のテキストコンテンツのみが入った配列を作成することができます。
1
2
3
4
5
6
7
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
elements = page.search(".example")
elements_texts = elements.map(&:inner_text)
p elements_texts
=> ["具体例1", "具体例2", "具体例3"]
get_attributeメソッド
取得した要素のタグの属性を取得するメソッドです。
1
2
3
4
5
<div class="content">
<a href="https://example.com" class="link">Example Link</a>
<a href="https://another-example.com" class="link">Another Example Link</a>
<img src="image.jpg" alt="サンプル画像" class="image">
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
links = page.search('.link')
images = page.search('.image')
links.each do |link|
href = link.get_attribute('href')
puts href
end
# 出力結果
# https://example.com
# https://another-example.com
images.each do |image|
src = image.get_attribute('src')
puts src
end
# 出力結果
# image.jpg
このようにa
タグのhref
属性の値や、img
タグのsrc
属性の値を取得することができます。
また以下のように省略して書くこともできます。
1
2
3
4
5
6
7
# 省略しない書き方
link.get_attribute('href')
image.get_attribute('src')
# 省略した書き方
link[:href]
image[:src]
リンクを辿る方法
この章ではリンク先のページをスクレイピングする際に便利なメソッドを紹介します。
link_withメソッド
ページ内のリンクを検索し、指定した条件に一致する最初のリンクのテキストとURLを取得するメソッドです。
取得した値に対して、text
メソッドを使うとリンクのテキストコンテンツを、href
メソッドを使うとリンクのURLを取得することができます。
1
2
3
4
5
6
7
link = page.link_with(検索する属性: 検索する名前)
p link.text
=> 取得したリンクのテキストコンテンツ
p link.href
=> 取得したリンクのURL
引数のキーは以下の属性を指定できます。
属性 | 検索するもの |
---|---|
text | リンクのテキスト |
href | リンクのURL |
class | リンクのクラス属性 |
id | リンクのid属性 |
以下が具体的な使い方です。
1
2
3
<a href="https://example.com" class="external-link">Example Link</a>
<a href="https://another-example.com" class="external-link">Another Example Link</a>
<a id="special-link" href="https://special-example.com">Special Example Link</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
link_by_text = page.link_with(text: 'Example Link')
link_by_href = page.link_with(href: 'https://another-example.com')
link_by_class = page.link_with(class: 'external-link')
link_by_id = page.link_with(id: 'special-link')
p link_by_text.text
=> "Example Link"
p link_by_text.href
=> "https://example.com"
p link_by_href.text
=> "Another Example Link"
p link_by_href.href
=> "https://another-example.com"
p link_by_class.text
=> "Example Link"
p link_by_class.href
=> "https://example.com"
p link_by_id.text
=> "Special Example Link"
p link_by_id.href
=> "https://special-example.com"
linksメソッド
link_with
メソッドが指定した条件に一致する最初のリンクを取得するのに対し、ページ内の全てのリンクを配列として取得するメソッドです。リンクの一覧を取得したいときに便利です。
1
2
3
<a href="https://example.com" class="external-link">Example Link</a>
<a href="https://another-example.com" class="external-link">Another Example Link</a>
<a id="special-link" href="https://special-example.com">Special Example Link</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
links = page.links
p links
=> [
#<Mechanize::Page::Link "Example Link" "https://example.com">,
#<Mechanize::Page::Link "Another Example Link" "https://another-example.com">,
#<Mechanize::Page::Link "Special Example Link" "https://special-example.com">
]
links_texts = links.map(&:text)
p links_texts
=> ["Example Link", "Another Example Link", "Special Example Link"]
links_href = links.map(&:href)
p links_href
=> ["https://example.com", "https://another-example.com", "https://special-example.com"]
clickメソッド
取得したリンクをクリックした結果としてリンク先のページの情報を取得するメソッドです。link_with
メソッドと組み合わせて使います。
1
2
link = page.link_with(text: 'Example Link')
next_page = link.click
click
メソッドを使うことで、特定のリンクをクリックした後のページ情報を取得し、さらなるスクレイピングを実行することができます。
フォームを送信する
この章ではページ内のフォームを送信するまでの流れを解説します。
form_withメソッド
ページ内のフォームを検索し、指定した条件に一致するフォームを取得するメソッドです。取得したフォームに対してデータを入力することができます。
1
page.form_with(検索する属性: 検索する名前)
引数のキーには以下の属性を指定できます。
属性 | 検索するもの |
---|---|
name | フォームの名前 |
id | フォームのid |
method | フォームの送信方法(GETまたはPOST) |
action | フォームのアクション属性 |
取得したフォームの各入力フィールドには、以下のようにして値を入力することができます。
1
2
3
form = page.form_with(name: 'search_form')
form['inputタグのname属性'] = 入力する値
以下が具体的な使い方です。
1
2
3
4
5
<form action="/search" method="get" id="search-form" name="search_form">
<input type="text" name="name" value="">
<input type="text" name="email" value="">
<input type="submit" value="検索">
</form>
1
2
3
4
5
6
7
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
form = page.form_with(name: 'search_form')
# 取得したフォームの入力フィールドに値を入力
form['name'] = 'Pikawaka'
form['email'] = 'pikawaka@example.com'
button_withメソッド
ページ内の特定のボタン要素を取得するためのメソッドです。このメソッドを使用すると、特定のボタンをクリックして遷移するページの情報を取得することができます。フォームに複数の送信ボタンがある場合に便利です。
1
form.button_with(検索する属性: 検索する名前)
引数のキーには以下の属性を指定できます。
属性 | 検索するもの |
---|---|
name | ボタンのname属性 |
value | ボタンのvalue属性 |
以下は簡単な例です。
1
<button name="next-button" onclick="location.href='/next_page'">次のページへ</button>
1
2
3
4
5
6
7
8
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
# 'name'属性が 'next-button' であるボタンを取得
next_button = page.button_with(name: 'next-button')
# 遷移先のページ情報を取得
next_page = next_button.click
取得したボタンには以下のメソッドを使うことができます。
メソッド | 取得できる値 |
---|---|
name | ボタンのname属性 |
value | ボタンのvalue属性 |
type | ボタンのtype属性 |
node | ボタンのHTMLノード |
以下が具体例です。
1
2
3
4
5
6
7
<form id="user-form" name="user_form" action="/submit" method="post">
<input type="text" name="name" value="">
<input type="text" name="email" value="">
<input type="submit" name="submit_button" value="送信1">
<input type="submit" name="submit_button" value="送信2">
<button type="submit" name="submit_button" value="送信3">submit3</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
agent = Mechanize.new
page = agent.get('上記WebサイトのURL')
form = page.form_with(name: 'user_form')
# 'value'属性が '送信1' であるボタンを取得
submit_button1 = form.button_with(value: '送信1')
# 'value'属性が '送信3' であるボタンを取得
submit_button3 = form.button_with(value: '送信3')
p submit_button1.name
=> "submit_button"
p submit_button1.value
=> "送信1"
p submit_button1.type
=> "submit"
p submit_button1.node
=> #<Nokogiri::XML::Element:0xbd60 name="input" attribute_nodes=[#<Nokogiri::XML::Attr:0xbd24 name="type" value="submit">,
#<Nokogiri::XML::Attr:0xbd38 name="name" value="submit_button">,
#<Nokogiri::XML::Attr:0xbd4c name="value" value="送信1">]>
button
タグで作成されたボタンのテキストは、タグの間に記述した内容になります。一方、input
タグにtype="submit"
を指定して送信ボタンとした場合、ボタンのテキストはvalue
属性で定義されます。
See the Pen rails_mechanize_01 by miyajima yuya (@pikawaka) on CodePen.
そのため、node
メソッドを使用してbutton
要素のテキストを取得できますが、input
要素には対応するテキストがありません。取得しようとすると空文字になるので注意しましょう。その場合は、value
属性を使ってボタンのテキストを取得してください。
1
2
3
4
5
6
7
8
9
10
# buttonタグのテキストを取得
p submit_button3.node.text
=> "submit3"
# inputタグのテキストを取得
p submit_button1.node.text
=> ""
p submit_button1.value
=> "送信2"
submitメソッド
フォームを送信する際に使用するメソッドです。取得したフォームに入力されたデータを送信し、送信後のページ情報を取得することができます。
1
2
3
4
5
6
7
8
9
agent = Mechanize.new
page = agent.get('WebサイトのURL')
form = page.form_with(name: 'search_form')
form['name'] = 'Pikawaka'
form['email'] = 'pikawaka@example.com'
# フォームを送信して次のページ情報を取得
next_page = form.submit
submit
メソッドには引数を渡すことができます。button_with
メソッドで取得したボタン要素を渡すと、submit
時に特定のボタンを指定することができます。フォーム内に複数のボタンがあるときに便利です。
1
2
submit_button = form.button_with(value: '送信1')
form.submit(submit_button)
注意点
スクレイピングを行う際の注意点を紹介します。
ウェブサイトの利用規約を確認する
Webサイトの中にはスクレイピングを禁止しているサイトがあります。スクレイピングを行う前に、スクレイピングを許可しているか、禁止しているか利用規約の確認をしましょう。
負荷を最小限にする
短時間に大量のスクレイピングしてしまうとスクレイピングするサイトのサーバーにかなりの負荷をかけてしまい、迷惑をかけてしまう恐れがあります。リクエストの間に適切な間隔を設ける(例えば、1〜2秒のスリープを挟む)などの処理をしましょう。
また大量のデータを取得する際は、一度にすべてを取得するのではなく、分割して取得する方法を検討しましょう。
この記事のまとめ
Mechanize
はスクレイピングする際に便利なgemです。- スクレイピングを使うことで自動でWebサイトから欲しい情報を取得することができます。
- スクレイピングが禁止されているサイトもあるので、使う前に必ず確認をしましょう。
![ぴっかちゃん](/packs/media/images/chara-general-3dd00c379ab4c2fc7a7bfe5f2d667314.png)