Mechanizeの使い方(CookiesはRedisサーバへ保存)

普段は OpenURL 、 JS が必要な場合には Watir-Webdriver を使ってサイトへアクセスするのですが、今回は Cookies が必要なだけだったので、初めて Mechanize を使ってみました。記録しておきます。

今回の目的

Qiita にログインして http://qiita.com/api/notifications (通知)の JSON を取得

Mechanizeインスタンス生成

agent = Mechanize.new

ログインページへ

トップからもログイン出来ますが、ログインページだけは https なのでこちらのほうが好ましいでしょう。
ページ取得は get メソッドを使います。

> page = agent.get('https://qiita.com/login')
=> #<Mechanize::Page
 {url #<URI::HTTPS:0x007faf6f9915f8 URL:https://qiita.com/login>}
 {meta_refresh}
 {title "Login - Qiita"}
 {iframes #<Mechanize::Page::Frame nil "//www.googletagmanager.com/ns.html?id=GTM-TBQWPN">}
 {frames}
 {links
  #<Mechanize::Page::Link "Login with GitHub" "https://qiita.com/auth/github?">
  #<Mechanize::Page::Link "Login with Twitter" "https://qiita.com/auth/twitter?">
  #<Mechanize::Page::Link "sign up." "/signup">
  #<Mechanize::Page::Link "Forget Password?" "https://qiita.com/sessions/forgot_password">}
 {forms
  #<Mechanize::Form
   {name nil}
   {method "POST"}
   {action "/login"}
   {fields
    [hidden:0x3fd7b80503a0 type: hidden name: utf8 value: &#10003;]
    [hidden:0x3fd7b805024c type: hidden name: authenticity_token value: ZP5/wLWqeCZwS6rGFBjsRqd6Im6zbSA4ceZSiWTQyv7663JGQE1tLH8PG9Ie+R6qlv/eW4rPiii9USow4+2tug==]
    [text:0x3fd7b80500e4 type: text name: identity value: ]
    [field:0x3fd7b8055f6c type: password name: password value: ]}
   {radiobuttons}
   {checkboxes}
   {file_uploads}
   {buttons [submit:0x3fd7b8055e40 type: submit name: commit value: Log In]}>}>

Mechanize::Pageインスタンスのメソッド

> ls page
Mechanize::Parser#methods: 
  []   canonical_each  code=  extract_filename  find_free_name  key?      response=  uri=
  []=  code            each   fill_header       header          response  uri      
Mechanize::File#methods: body  body=  content  filename  filename=  save  save!  save_as
Mechanize::Page#methods: 
  /                  encoding_error?  iframe        labels_hash   reset                  
  at                 encodings        iframe_with   link          response_header_charset
  base               form             iframe_with!  link_with     root                   
  base_with          form_with        iframes       link_with!    search                 
  base_with!         form_with!       iframes_with  links         select_bases           
  bases              forms            image         links_with    select_forms           
  bases_with         forms_with       image_urls    mech          select_frames          
  canonical_uri      frame            image_with    mech=         select_iframes         
  content_type       frame_with       image_with!   meta_charset  select_images          
  detected_encoding  frame_with!      images        meta_refresh  select_links           
  encoding           frames           images_with   parser        title                  
  encoding=          frames_with      labels        pretty_print
instance variables: 
  @bases  @encoding   @forms      @iframes      @links              @meta_refresh  @title
  @body   @encodings  @frames     @labels       @mech               @parser        @uri  
  @code   @filename   @full_path  @labels_hash  @meta_content_type  @response    

ヘッダ

header メソッド。エイリアスとして response メソッド。

> page.header
=> {"cache-control"=>"max-age=0, private, must-revalidate",
 "content-encoding"=>"gzip",
 "content-type"=>"text/html; charset=utf-8",
 "date"=>"Tue, 24 Mar 2015 04:42:35 GMT",
 "server"=>"nginx",
 "set-cookie"=>
  "_qiita_login_session=YjliL0RBUTBpN2xNQ25xWG1qUTBSL2NpeHFVUEs1bnZQNGxWZ3VLemI3YkxCUG10OTNqK0N5TXk1S3RkUWpja09OYW5xT2EzaU4zTGNNcFJORWQxQmVVamN0cU80ZDBoQlRaYllScHFidnhpLzc2RHRid2ZpNDJCcnVHQW5UQUk5c2ZHS1d2N2hTUzExaHpSQ0N2TSt2SklTb0VOZFVnU3lTUWNxd2I2bW1jbVM1NkwrM1d1U3dCWHdCQWgySi9CLS1CQmE4QzlBQUl5aE9rOU5nU3l5UmR3PT0%3D--cbffe82afceb3d796bd573499c21f423a83fca8c; domain=.qiita.com; path=/; expires=Tue, 07 Apr 2015 04:42:35 -0000; HttpOnly",
 "status"=>"200 OK",
 "x-content-type-options"=>"nosniff",
 "x-frame-options"=>"SAMEORIGIN",
 "x-request-id"=>"837d74cf-0340-4c58-adb3-df0bb57b4c72",
 "x-runtime"=>"0.057129",
 "x-xss-protection"=>"1; mode=block",
 "transfer-encoding"=>"chunked",
 "connection"=>"keep-alive"}

each メソッドでも取得可能。

> page.each { |k, v| puts k + " | " + v }
cache-control | max-age=0, private, must-revalidate
content-encoding | gzip
content-type | text/html; charset=utf-8
date | Tue, 24 Mar 2015 04:42:35 GMT
server | nginx
set-cookie | _qiita_login_session=YjliL0RBUTBpN2xNQ25xWG1qUTBSL2NpeHFVUEs1bnZQNGxWZ3VLemI3YkxCUG10OTNqK0N5TXk1S3RkUWpja09OYW5xT2EzaU4zTGNNcFJORWQxQmVVamN0cU80ZDBoQlRaYllScHFidnhpLzc2RHRid2ZpNDJCcnVHQW5UQUk5c2ZHS1d2N2hTUzExaHpSQ0N2TSt2SklTb0VOZFVnU3lTUWNxd2I2bW1jbVM1NkwrM1d1U3dCWHdCQWgySi9CLS1CQmE4QzlBQUl5aE9rOU5nU3l5UmR3PT0%3D--cbffe82afceb3d796bd573499c21f423a83fca8c; domain=.qiita.com; path=/; expires=Tue, 07 Apr 2015 04:42:35 -0000; HttpOnly
status | 200 OK
x-content-type-options | nosniff
x-frame-options | SAMEORIGIN
x-request-id | 837d74cf-0340-4c58-adb3-df0bb57b4c72
x-runtime | 0.057129
x-xss-protection | 1; mode=block
transfer-encoding | chunked
connection | keep-alive

応答だけならば code メソッドで。

> page.code
=> "200"

ソース

body メソッド。エイリアスとして content メソッドもあり。

> page.body
=> "<!DOCTYPE html><html xmlns:og=\"http://ogp.me/ns#\"><head><meta charset=\"UTF-8\" /><title>Login - Qiita</title><meta content=\"width=device-width,height=device-height,initial-scale=1\" name=\"viewport\" /><meta content=\"Qiita is a technical knowledge sharing and collaboration platform for programmers. You can record and post programming tips, know-how and notes here.\" name=\"description\" /><meta content=\"summary\" name=\"twitter:card\" /><meta content=\"@Qiita\" name=\"twitter:site\" /><meta content=\"Login - Qiita\" property=\"og:title\" />
(以下略)

URL

url メソッドではなく uri メソッド。

> page.uri
=> #<URI::HTTPS:0x007faf6f9915f8 URL:https://qiita.com/login>

ファイル名

> page.filename
=> "login.html"

タイトル

> page.title
=> "Login - Qiita"

リンク

links メソッド。配列で返す。 link メソッドだと一つ目だけ。

> page.links
=> [#<Mechanize::Page::Link "Login with GitHub" "https://qiita.com/auth/github?">,
 #<Mechanize::Page::Link "Login with Twitter" "https://qiita.com/auth/twitter?">,
 #<Mechanize::Page::Link "sign up." "/signup">,
 #<Mechanize::Page::Link "Forget Password?" "https://qiita.com/sessions/forgot_password">]

links_with メソッド*1を使うと条件を満足するインスタンスのみを配列で返す。 link_with メソッドだと一つ目だけ。
条件に出来るキーは

有効なメソッドと値のペアは Mechanize::Page::Link のメソッドと返り値になります。
Mechanize::Page - Ruby Mechanize wiki (ja)

class Mechanize::Page::Link
具体的には :href, :text 。(:rel も?)

> page.links_with(text: /Login/)
=> [#<Mechanize::Page::Link "Login with GitHub" "https://qiita.com/auth/github?">,
 #<Mechanize::Page::Link "Login with Twitter" "https://qiita.com/auth/twitter?">]
> page.links_with(href: /sign/)
=> [#<Mechanize::Page::Link "sign up." "/signup">]

Mechanize::Page::Link#click メソッドでリンクをクリックできる。
いわゆる history.back したい(前のページへ戻りたい)場合には Mechanize#back メソッド

> agent.back

で OK 。
history は Mechanize#history メソッド。

> agent.history
=> [#<Mechanize::Page
  {url #<URI::HTTPS:0x007faf6fad7818 URL:https://qiita.com/login>}
(以下略)

フォーム

forms メソッド。配列で返す。 form メソッドだと一つ目だけ。
このページにはユーザネーム/パスワードを入れるフォームが1つだけあるので form メソッドで取得。

> auth = page.form
=> #<Mechanize::Form
 {name nil}
 {method "POST"}
 {action "/login"}
 {fields
  [hidden:0x3fd7b80503a0 type: hidden name: utf8 value: &#10003;]
  [hidden:0x3fd7b805024c type: hidden name: authenticity_token value: ZP5/wLWqeCZwS6rGFBjsRqd6Im6zbSA4ceZSiWTQyv7663JGQE1tLH8PG9Ie+R6qlv/eW4rPiii9USow4+2tug==]
  [text:0x3fd7b80500e4 type: text name: identity value: ]
  [field:0x3fd7b8055f6c type: password name: password value: ]}
 {radiobuttons}
 {checkboxes}
 {file_uploads}
 {buttons [submit:0x3fd7b8055e40 type: submit name: commit value: Log In]}>
Mechanize::Formインスタンスのメソッド
> ls auth
Mechanize::Form#methods: 
  []                   dom_class          has_key?                radiobuttons_with    
  []=                  dom_id             has_value?              request_data         
  action               elements           hidden_field?           reset_button?        
  action=              encoding           hiddens                 resets               
  add_button_to_query  encoding=          ignore_encoding_error   save_hash_field_order
  add_field!           enctype            ignore_encoding_error=  select_buttons       
  build_query          enctype=           keygens                 select_checkboxes    
  button               field              keys                    select_fields        
  button_with          field_with         method                  select_file_uploads  
  button_with!         field_with!        method=                 select_radiobuttons  
  buttons              fields             method_missing          set_fields           
  buttons_with         fields_with        name                    submit               
  checkbox             file_upload        name=                   submit_button?       
  checkbox_with        file_upload_with   page                    submits              
  checkbox_with!       file_upload_with!  pretty_print            text_field?          
  checkboxes           file_uploads       radiobutton             textarea_field?      
  checkboxes_with      file_uploads_with  radiobutton_with        textareas            
  click_button         form_node          radiobutton_with!       texts                
  delete_field!        has_field?         radiobuttons            values               
instance variables: 
  @action      @clicked_buttons  @fields        @ignore_encoding_error  @name        
  @buttons     @encoding         @file_uploads  @mech                   @page        
  @checkboxes  @enctype          @form_node     @method                 @radiobuttons
フィールド

fields メソッド。 Mechanize::Form::Field を要素とする配列で返す。 field メソッドだと一つ目だけ。

auth.fields
=> [[hidden:0x3fd7b80503a0 type: hidden name: utf8 value: &#10003;],
 [hidden:0x3fd7b805024c type: hidden name: authenticity_token value: ZP5/wLWqeCZwS6rGFBjsRqd6Im6zbSA4ceZSiWTQyv7663JGQE1tLH8PG9Ie+R6qlv/eW4rPiii9USow4+2tug==],
 [text:0x3fd7b80500e4 type: text name: identity value: ],
 [field:0x3fd7b8055f6c type: password name: password value: ]]

fields_with メソッドだと(links_with メソッドと同様に)条件を満足するインスタンスのみを配列で返す。 field_with メソッドだと一つ目だけ。

> auth.fields_with(type: "text")
=> [[text:0x3fd7b80500e4 type: text name: identity value: ]]
> auth.fields_with(name: "identity")
=> [[text:0x3fd7b80500e4 type: text name: identity value: ]]

:name キーだけはキー指定しなくても OK 。

> auth.fields_with("identity")
=> [[text:0x3fd7b80500e4 type: text name: identity value: ]]
> auth.field_with("password")
=> [field:0x3fd7b8055f6c type: password name: password value: ]
フィールドに値を設定

Mechanize::Form::Field#value メソッドで設定。

> auth.field_with("identity").value = "riocampos"
=> "riocampos"
> auth.field_with("password").value = "password"
=> "password"

確認。

> auth.field_with("identity")
=> [text:0x3fd7b80500e4 type: text name: identity value: riocampos]
> auth.field_with("password")
=> [field:0x3fd7b8055f6c type: password name: password value: password]
ログインボタンを押してSubmit(その1)

Mechanize::Form インスタンス auth にユーザネームとパスワードを設定したので、Mechanize::Form#click_button メソッドで送信する。

> auth.click_button
=> #<Mechanize::Page
 {url #<URI::HTTP:0x007faf6fa58310 URL:http://qiita.com/>}
 {meta_refresh}
 {title "ホーム - Qiita"}
 {iframes #<Mechanize::Page::Frame nil "//www.googletagmanager.com/ns.html?id=GTM-TBQWPN">}
 {frames}
 {links
  #<Mechanize::Page::Link "" "/">
  #<Mechanize::Page::Link
   "Markdownによる情報共有サービス、Qiita:Team"
   "https://teams.qiita.com?utm_source=qiita&utm_medium=header_news">
  #<Mechanize::Page::Link "新規作成" "/drafts">
  #<Mechanize::Page::Link "投稿する" "/drafts/new">
  #<Mechanize::Page::Link "下書き一覧" "/drafts">
  #<Mechanize::Page::Link "ストック一覧" "/riocampos/stock">
  #<Mechanize::Page::Link "通知一覧を見る" "/notifications">
  #<Mechanize::Page::Link "" "#">
  #<Mechanize::Page::Link "マイページ" "/riocampos">
  #<Mechanize::Page::Link "編集リクエスト管理" "/patches">
  #<Mechanize::Page::Link "プロフィール設定" "/settings/profile">
  #<Mechanize::Page::Link "設定" "http://qiita.com/settings/account">
  #<Mechanize::Page::Link "ログアウト" "http://qiita.com/logout">
  #<Mechanize::Page::Link "ノウハウ・Tipsを投稿する" "/drafts/new">
  #<Mechanize::Page::Link "フィード" "/">
  #<Mechanize::Page::Link "すべての投稿" "/public">
  #<Mechanize::Page::Link "自分の投稿 64" "/mine">
  #<Mechanize::Page::Link "ストック" "/stock">
  #<Mechanize::Page::Link "riocampos" "/riocampos">
  #<Mechanize::Page::Link "フィード" "/">
  #<Mechanize::Page::Link "Advent Calendar" "/advent-calendar">
  #<Mechanize::Page::Link "Organization一覧" "/organizations">
  #<Mechanize::Page::Link "hibikiw" "/hibikiw">
  #<Mechanize::Page::Link "ytsuboi" "/ytsuboi">
  #<Mechanize::Page::Link "tkusano" "/tkusano">
  #<Mechanize::Page::Link "takayama" "/takayama">
  #<Mechanize::Page::Link "yataro" "/yataro">
  #<Mechanize::Page::Link "もっと見る" "/recommended_users">
  #<Mechanize::Page::Link "Tweets by @Qiita" "https://twitter.com/Qiita">
  #<Mechanize::Page::Link
   "返信の必要なお問い合わせはこちら"
   "https://increments.zendesk.com/anonymous_requests/new">
  #<Mechanize::Page::Link "Qiitaとは" "http://qiita.com/about">
  #<Mechanize::Page::Link "タグ一覧" "http://qiita.com/tags">
  #<Mechanize::Page::Link "Advent Calendar一覧" "http://qiita.com/advent-calendar">
  #<Mechanize::Page::Link "Organization一覧" "http://qiita.com/organizations">
  #<Mechanize::Page::Link "ユーザー一覧" "http://qiita.com/users">
  #<Mechanize::Page::Link "Developer API" "http://qiita.com/api/v2/docs">
  #<Mechanize::Page::Link "Webhook ドキュメント" "http://qiita.com/api/webhook/docs">
  #<Mechanize::Page::Link "JavaScript License" "/license.txt">
  #<Mechanize::Page::Link "公式ブログ" "http://blog.qiita.com">
  #<Mechanize::Page::Link "利用規約" "http://qiita.com/terms">
  #<Mechanize::Page::Link "プライバシーポリシー" "http://qiita.com/privacy">
  #<Mechanize::Page::Link "特定商取引法に基づく表記" "http://qiita.com/asct">
  #<Mechanize::Page::Link "サポート" "http://support.qiita.com">
  #<Mechanize::Page::Link "お問い合わせ" "https://increments.zendesk.com/anonymous_requests/new">
  #<Mechanize::Page::Link "運営会社" "http://increments.co.jp">
  #<Mechanize::Page::Link "Kobito - プログラミングのメモやスニペットの記録に最適なMacアプリケーショ" "http://kobito.qiita.com">
  #<Mechanize::Page::Link "Qiita:Team - シンプル、スマートかつクローズドな情報共有サービス" "http://teams.qiita.com">
  #<Mechanize::Page::Link "Qiita:Career - プログラマのためのキャリア構築支援サービス" "http://career.qiita.com">}
 {forms
  #<Mechanize::Form
   {name nil}
   {method "GET"}
   {action "/search"}
   {fields
    [hidden:0x3fd7b8154058 type: hidden name: utf8 value: &#10003;]
    [hidden:0x3fd7b8159e68 type: hidden name: sort value: rel]
    [text:0x3fd7b8159cec type: text name: q value: ]
    [selectlist:0x3fd7b8158ea0 type:  name: sort value: rel]}
   {radiobuttons}
   {checkboxes [checkbox:0x3fd7b8159990 type: checkbox name: stocked value: false]}
   {file_uploads}
   {buttons [submit:0x3fd7b8159814 type: submit name: commit value: 検索]}>
  #<Mechanize::Form
   {name nil}
   {method "POST"}
   {action "/feedbacks"}
   {fields
    [hidden:0x3fd7b7e7b5c0 type: hidden name: utf8 value: &#10003;]
    [hidden:0x3fd7b7e7b46c type: hidden name: authenticity_token value: 2+wUxiggBcUxJXHYsInoC1Os4BYM7Sw6dYctJKuHOaAzhisClUU4a1YMTfVmil+vxiOpc01V/rsZTAFUnejQBw==]
    [text:0x3fd7b7e7afd0 type: text name: feedback[name] value: ]
    [textarea:0x3fd7b7e7aa80 type:  name: feedback[message] value: 
]}
   {radiobuttons}
   {checkboxes [checkbox:0x3fd7b7e7b1b0 type: checkbox name: feedback[send_user_info] value: true]}
   {file_uploads}
   {buttons [submit:0x3fd7b7e7b318 type: submit name: commit value: 運営者に意見を送る]}>}>

ログイン出来ました。

ログインボタンを押してSubmit(その2)

agent インスタンスをレシーバとして Mechanize#submit メソッドを使う。その際の引数として Mechanize::Form インスタンスとそのインスタンス中の Mechanize::Form::Submit インスタンスを渡す。

> agent.submit(auth, auth.button)
=> #<Mechanize::Page
 {url #<URI::HTTPS:0x007faf6fa84f50 URL:https://qiita.com/>}
 {meta_refresh}
 {title "Qiita - A technical knowledge sharing platform for programmers."}
(以下同)

Nokogiriオブジェクトへの変換

XPath を指定しての Mechanize::Page#at メソッドで Nokogiri オブジェクトに変換できます。Mechanize::Page#root メソッド及びエイリアスの Mechanize::Page#parser メソッドでルートの Nokogiri オブジェクトが得られます。 Nokogiri 扱いのほうが Mechanize よりも得意な私は、目的のページに辿り着いたら Nokogiri オブジェクトにしてしまったほうが良さそう。
コレクション(NodeSet)を取得したい場合には Mechanize::Page#search メソッドで。

Cookies

確認

Qiitaにログインした状態で Cookies を確認してみます。 Mechanize#cookie_jar メソッドを使います。

> agent.cookie_jar
=> #<Mechanize::CookieJar:0x007faf6f96b650
 @store=
  #<HTTP::CookieJar::HashStore:0x007faf6c2ec980
   @gc_index=13,
   @gc_threshold=150,
   @jar=
    {"qiita.com"=>
      {"/"=>
        {"_qiita_login_session"=>
          #<HTTP::Cookie:name="_qiita_login_session", value="RVVMYzM1WGd2YjFwdVdGWHFXcSt6ajgvSDgrWkxUSVhoTzRtbGk4SkhoSUVJZ1cyZHNiMkVheDNDR2pxdHB0cWI3a0RLQ0wwSHlsODJsTGk2bk91aUluNDNsLzF2UWw0Q0R3cm5xK2RJLytJSTRmdGoyM0hESkUwWWZ5bmFScTNWRWI2Z3MvSWh0dlJLeGRLVXB1aVN5VG9xNlFJRmVZUjlEWXg2aE5ESUhPcmNXR200RTZab1FUV21YbnpRQWU1L2VoTkFsNXNNY1l4OERFckxwRDh5UT09LS1TL1p5RHllSHIvRnpVSE5FU2Z0ZktnPT0%3D--1f3a62190ba2dd206c15b6747bf8ef84c125dc74", domain="qiita.com", for_domain=true, path="/", secure=false, httponly=true, expires=2015-04-07 06:56:12 UTC, max_age=nil, created_at=2015-03-24 15:56:13 +0900, accessed_at=2015-03-24 15:56:13 +0900 origin=http://qiita.com/>,
         "secure_token"=>
          #<HTTP::Cookie:name="secure_token", value="24fb42e01282a333b5d7ea32642bca3457a1b84684759fa5621054a6c4d38fec", domain="qiita.com", for_domain=true, path="/", secure=true, httponly=true, expires=2035-03-24 06:56:11 UTC, max_age=nil, created_at=2015-03-24 15:56:13 +0900, accessed_at=2015-03-24 15:56:13 +0900 origin=https://qiita.com/login>}}},
   @logger=nil,
   @mon_count=0,
   @mon_mutex=#<Mutex:0x007faf6c2ec930>,
   @mon_owner=nil>>

返ってくるのは Mechanize::CookieJar インスタンス

> agent.cookie_jar.class
=> Mechanize::CookieJar

Mechanize::CookieJar インスタンスのメソッドを確認。

> ls agent.cookie_jar
Enumerable#methods: 
  all?            detect           each_with_object  grep      max_by     one?          sort      
  any?            drop             entries           group_by  member?    partition     sort_by   
  chunk           drop_while       find              include?  min        reduce        take      
  collect         each_cons        find_all          inject    min_by     reject        take_while
  collect_concat  each_entry       find_index        lazy      minmax     reverse_each  to_a      
  count           each_slice       first             map       minmax_by  select        to_h      
  cycle           each_with_index  flat_map          max       none?      slice_before  zip       
HTTP::CookieJar#methods: <<  cleanup  clear  cookies  delete  each  empty?  parse  store
Mechanize::CookieJarIMethods#methods: 
  add  add!  clear!  dump_cookiestxt  jar  load_cookiestxt  save_as
Mechanize::CookieJar#methods: load  save
instance variables: @store
Cookiesの保存

Cookies の永続化には Mechanize::CookieJar#save メソッドを使って YAML 形式で保存するのが一般的と思われます。ですが、この save メソッドは引数が IO かファイル名である必要があります。 Heroku などではローカルファイルが cycle 時に消えてしまいますので、このメソッドを使っても消えてしまいます。これでは役に立ちません。ということで Mechanize::CookieJar#save メソッドに StringIO インスタンスを渡すことにより Cookies を YAML 形式にした文字列に変換します。

def cookies_to_yaml_string(agent)
  cookies_io_write = StringIO.new("", 'r+')
  agent.cookie_jar.save(cookies_io_write)
  cookies_io_write.string
end

また Mechanize::CookieJar#load メソッドで Mechanize インスタンスに Cookies をセットします。

def set_cookies(agent, cookies_yaml)
  cookies_io_read = StringIO.new(cookies_yaml, 'r')
  agent.cookie_jar.clear
  agent.cookie_jar.load(cookies_io_read)
end

Cookies を Redis To Go に保存してみます。
既に Redis To Go の登録が済んでおり、 Redis のアクセス URL が "redis://redistogo:cdcddcfedd5ac469f64f4c44f7cc77bf@xxx.redistogo.com:9999/" であるとします。

redis_url = "redis://redistogo:cdcddcfedd5ac469f64f4c44f7cc77bf@xxx.redistogo.com:9999/"
Redis.current = Redis.new(url: redis_url)
cookies = Redis::Value.new('cookies')

では、 Mechanize でログインした時点での Cookies を Redis To Go に保存します。

cookies.value = cookies_to_yaml_string(agent)

保存した Cookies を Mechanize のインスタンス agent に再度セットするには

set_cookies(agent, cookies.value)

とすれば OK です。

ログイン後アクセス

当初の目的の URL から JSON を取得します。

json = JSON.parse(agent.get('http://qiita.com/api/notifications').body)

その直後に、保持している Cookies で Redis 側に保存(更新)します。

cookies.value = cookies_to_yaml_string(agent)

このように、 Cookies がセットされていればログイン手順不要となるサイトは多いと思いますので、この手法はわりと使えると思います。

エラーハンドリング

通常のエラーメソッド (Ruby - エラーをrescueして、rescueしない場合と全く同じエラーメッセージを出力する - Qiita)に加えて、 Exception: Mechanize::ResponseCodeError では response_code メソッドが使えますので、エラーコードにより振り分けを行う事が可能です。
参考

*1:links_with メソッドは links メソッドのエイリアス。他の xxx_with メソッドも全て xxx メソッドのエイリアス