Help us understand the problem. What is going on with this article?

Mechanizeでサイトログインしてスクレイピングするときのアレコレ メモ

デバックで使えるメソッドMechanizeメソッド集

Mechanizeクラスのメソッド

agentのページ遷移の履歴表示

p @agent.history

# [https://sample.com/page1.html, https://sample.com/page1.htm2]

Mechanize::Pageクラスのメソッド

ページのステータスコード表示

p page.code

# "200"

HTTP通信関連の表示

リクエスト、レスポンスヘッダ情報などをロガーで標準出力できる
特に、Mechanize内部で、FormのPOSTで送信されるqueryが見えるので、デバッグで重宝する

require 'logger'

@agent = Mechanize.new
@agent.log = Logger.new $stderr

レスポンスヘッダー取得

pp page.header

{"cache-control"=>"private",
 "content-type"=>"text/html; charset=utf-8",
 "x-aspnet-version"=>"Secret",
 "x-frame-options"=>"sameorigin",
 "date"=>"Tue, 05 Apr 2016 07:43:08 GMT",
 "content-length"=>"13577",
 "set-cookie"=>
  "cookie_oishii=1234.0000.4564; expires=Tue, 05-Apr-2016 08:49:09 GMT; path=/, hontoni_oishi=20220.0000; expires=Tue, 05-Apr-2016 08:49:09 GMT; path=/"}

リクエストヘッダの指定

@agent.request_headers = {
  'Origin' => 'https://michadameyo.com',
  'accept-language' => 'ja,en-US;q=0.8,en;q=0.6,zh-CN;q=0.4,zh;q=0.2',
  'Upgrade-Insecure-Requests' => '1',
  'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
}

特定のcookieのvalueの取得・保存・追加

uri = URI.parse("site.com")

# クッキー取得
cookie_str = @agent.cookie_jar.jar["site.com"]["/"]["hoshii_cookie_name"].value
File.write(CAPTCHA_PATH + '/cookie.txt', cookie_str)

# クッキー追加
cookie_str = File.read(CAPTCHA_PATH + '/cookie.txt')
agent.cookie_jar << Mechanize::Cookie.new('hosii_cookie_name', uri.host, {:value => cookie_str, :domain => uri.host, :path=>"/"}) 

ページ遷移・リソースの取得方法あれこれ

URL指定でページ取得(GET)

top_uri = 'https://www.webpage.com/index.html'
uri = URI.parse(top_uri)
top_page  = @agent.get(uri)

URLとPOSTパラメータ指定でページ取得(POST)

uri = 'https://samplesite.com'
response = @agent.post(uri, {
  area: 'kumamoto',
  region: 'mashiki',
  Date: '20160531'
})

doc =  Nokogiri::XML.parse(response.body)
pp doc.css('docroot > array')[0].text.split("\n")

同一のパラメータ名で複数のvalueをPOSTする場合は以下の様に配列でvalueを複数定義する

response = agent.post(uri, {
  params1: ['value1-1', 'value1-2'],
  params2: 'value2',
})

Mechanizeが送るPOST(content-type => application/x-www-form-urlencoded)のqueryを以下のログから確認すると、"params1=value1-1&params1=value1-2&params2=value2"の様にparams1のパラメータ名が送信されていることがわかる。

D, [2020-04-17T15:11:55.066392 #59898] DEBUG -- : query: "params1=value1-1&params1=value1-2&params2=value2"
I, [2020-04-17T15:11:55.066596 #59898]  INFO -- : Net::HTTP::Post: /
D, [2020-04-17T15:11:55.066614 #59898] DEBUG -- : request-header: accept-encoding => gzip,deflate,identity
D, [2020-04-17T15:11:55.066623 #59898] DEBUG -- : request-header: accept => */*
D, [2020-04-17T15:11:55.066630 #59898] DEBUG -- : request-header: user-agent => Mechanize/2.7.6 Ruby/2.5.3p105 (http://github.com/sparklemotion/mechanize/)
D, [2020-04-17T15:11:55.066638 #59898] DEBUG -- : request-header: accept-charset => ISO-8859-1,utf-8;q=0.7,*;q=0.7
D, [2020-04-17T15:11:55.066644 #59898] DEBUG -- : request-header: accept-language => en-us,en;q=0.5
D, [2020-04-17T15:11:55.066654 #59898] DEBUG -- : request-header: host => minaide_kudasai.com
D, [2020-04-17T15:11:55.066673 #59898] DEBUG -- : request-header: content-type => application/x-www-form-urlencoded
D, [2020-04-17T15:11:55.066680 #59898] DEBUG -- : request-header: content-length => 48

クリックでページ遷移する

list_page = top_page.link_with(text: '記事一覧ページ').click
list_page = top_page.link_with(href: '/list?src=ichiran').click

遷移先ページ内のFormを取得して、POSTするパラメータを追加し、送信する

form = page.form_with(name: 'register')
form.action     = 'https://minaide_kudasai/login.php'
form.LoginName = 'ID'
form.Passwd    = 'PASS'
form.checkbox_with(:name => 'ChkBox').value = '1'

login_page = agent.submit(form)

input fieldの名前によっては、上記の記載ができない場合がある。
その場合はこんな感じで書くと良い。

form = page.forms[0]
form.field_with(name: 'UserId').value   = 'ID'
form.field_with(name: 'Password').value = 'PASS'
login_page = agent.submit(form)

puts login_page.body

Formの送信ボタン(submitボタン)にFormパラメータのname,valueが設定されているときは、submitだけでは送信ボタンに付けられたFormパラメータがqueryに追記されないので、submitではなくボタンclickで処理する必要がある。

form = page.forms[0]
button = form.button_with(name: 'login_button') 
login_page = @agent.submit(form, button)

mechanizeが保持していないフォームフィールドを自分で追加する

form.add_field!('date', '20160606')

ユーザーエージェントを指定する

@agent = Mechanize.new
@agent.user_agent_alias = 'Windows IE 11'

ページのヒストリーを保持しない

@agent = Mechanize.new
# バックボタンが使える最小個数を保持する
@agent.max_history = 2

画像ファイルなどをダウンロードする

url = base_url + page.search('#LoginWebCaptcha > div.igc_CaptchaImageArea > img').attribute('src').value

@agent.get(url).save_as(CAPTCHA_PATH + "/latest_captcha.gif")

実例集

アパレルECサイトで商品をカートに入れて、買い物を進める感じの一連の操作

require 'mechanize'
require 'pp'

# ログインページ取得
agent = Mechanize.new
agent.max_history = 2
page  = agent.get('https://minaide_kudasai/login.php')
page.encoding = 'Shift_JIS'

# ログイン処理
form = page.form_with(name: 'register')
form.action     = 'https://minaide_kudasai/dologin.php'
form.LoginName = 'ID'
form.Passwd    = 'PASS'
form.checkbox_with(:name => 'ChkBox').value = '1'


login_page = agent.submit(form)
login_page.encoding = 'Shift_JIS'

# トップページへ遷移
top_page = login_page.link_with(text: "トップへ").click

# トップページに表示されたランキング3位の商品ページへ遷移(1-2位は在庫が無かったので3位)
item_page_url = top_page.search('#container > ul:nth-child(2) > li:nth-child(3) > a').attribute('href').value
item_page = top_page.link_with(href: item_page_url).click

# 商品ページ内のカートに入れる
item_id = item_page.search('#container > table tr:nth-child(3) > td:nth-child(3) > div > span').attribute('id').value

cart_in_form = item_page.form_with(name: 'cart_form')
cart_in_form.qId = item_id
cart_in_form.qRetId = item_id
cart_in_form.qMode = 'store'
cart_in_form.qNum = 1

cart_page = agent.submit(cart_in_form)

puts cart_page

気づき

  • Formのパラメータやリクエストヘッダ(特にCookie)をあれこれ手動で設定したくなるが、意外なほどMechanizeは普通のブラウザと同じように振る舞って、情報を集めてくれる。
    本当に追加が必要な情報かどうか、しっかり調べてから、必要なForm、リクエストヘッダのみ追記するようにすること。
    (特に、FormはブラウザとMechanizeのForm 生queryを比較すれば、過不足は明確に分けられるので。)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away