かなりすごいブログ

Mechanizeでのスクレイピング時、アクセス元IPアドレスを分散させる

Ruby Advent Calendar 2014 17日目

Ruby Advent Calendar 2014、17日目の記事になります。

スクレイピング時にアクセス元のIPアドレスを分散させたい

ConoHa VPSではVPS1契約ごとに最大16個の追加IPアドレスを購入できます。1つあたり200円と安価で、最安のプランで契約し追加IPアドレスを16個オプションで追加すれば、17個のIPアドレスを持つVPSサーバーの出来上がりです。月額4,180円で、1IPアドレスあたり月額約246円です。非常に安価ですね。

さて、スクレイピング時にアクセス元のIPアドレスを分散させたいということは割とあるかと思います。その是非はここでは置いておくとして、ConoHa VPSを利用することで安価にその土壌となるサーバーを入手できることがわかりました。ただし、Rubyにおいてスクレイピングに有用なgemであるMechanizeには、指定のネットワークインターフェイスを経由してHTTPアクセスを行うといった機能は付いていません。そこで今回はMechanizeにて任意のネットワークインターフェイスにてスクレイピングを行う方法を解説します。

コード

require 'mechanize'
module Benrify
  refine Net::HTTP::Persistent do
    alias_method :connection_for_origin, :connection_for
    attr_accessor :local_host
    attr_accessor :local_port
    def connection_for uri
      conn = connection_for_origin uri
      if conn.local_host != @local_host || conn.local_port != @local_port
        conn.local_host = @local_host
        conn.local_port = @local_port
        conn.finish
      end
      conn
    end
  end
  refine Mechanize do
    def bind(local_host = nil, local_port = nil)
      @agent.http.local_host = local_host
      @agent.http.local_port = local_port
    end
  end
end
using Benrify

これだけです。Net::HTTP::Persistentを上書きすることで実現します。クラスの拡張にはRefinementsを使いましょう。

使用例

require 'mechanize'
module Benrify
  # 省略
end
using Benrify

def test(address = nil)
  $a ||= Mechanize.new
  $a.bind address if address
  res = $a.get 'http://wtfismyip.com/text'
  res.body.strip
end

local_addresses = Socket.getifaddrs.select{|it|
  it.addr.ipv4?
}.select{|it|
  /(?:eth|en)\d+(?::\d+)?/ =~ it.name
}

puts 'Try: default'
puts 'Got: ' + test
puts '-' * 20

local_addresses.each do |it|
  puts "Try: #{it.addr.ip_address}(#{it.name})"
  puts 'Got: ' + test(it.addr.ip_address)
  puts '-' * 20
end

Socket.getifaddrsでネットワークインターフェイスの一覧が取得できるので、そこから好きなものを選んでMechanize.bindメソッドにて割り当てを行うだけです。

この例では、ethもしくはenで始まる全てのインターフェイスを取得し、それらをそれぞれMechanize.bindした上でhttp://wtfismyip.com/textの内容を取得・表示しています。

http://wtfismyip.com/は、アクセス元のIPアドレスを表示してくれるウェブサービスです。XMLやJSON、プレインテキストなどの各種フォーマットでレスポンスを返してくれるURLも用意されており便利ですね。

おわり

スクレイピング時にアクセス元のIPアドレスを複数に分散させたい、ただし複数のサーバーを用意しそれらを管理する構成を作るほどの規模ではないし、予算も抑えたい。そういった場合にこのような手法が有用かと思います。通常こういったアクセス元IPアドレスの分散を行う場合は面倒なqueueの管理が必要かと思いますが、この方法を用いれば割と雑に組んでもそれなりに動くものができて便利です。

ConoHa VPS以外にもIPアドレスを追加購入できるVPSサーバーなどがあったら是非教えてください。