2009年4月19日日曜日

Ruby でスクレイピング (2) - Basic 認証を用いたサイトへアクセス

Ruby の Hpricot でスクレイピング」の続き。

1. Basic 認証を用いたサイトをスクレイピングしたい

ルータに割り当てられた IP アドレスを取得したい。

ブラウザでルータの設定画面をアクセスすると、IP アドレスを知ることができる。

ルータの設定画面にアクセスするには、パスワードを入力する必要がある。このときの認証方式は、Basic 認証。

上記を Ruby を用いて、スクレイピングする。

 

2.  方法の概略

  1. Net::HTTP を用いて、Basic 認証を行う。
  2. Hprictot で、HTML  から IP アドレスを表す文字列を抽出する。

 

3. Net:HTTP を用いて Basic 認証を行う

IP アドレスが表示されるページ

090419-018ルータは、AirStation を使っている。最初に、Basic 認証でルータの設定ページにアクセスできるか確かめる。

現在の IP アドレスは、AirStation の

  • 「システム情報」

において表示される。このページはフレームで作成されている。IP アドレスが表示されるのは、右側のフレーム。このフレームだけ表示し、 URL を確認する。

 

Basic 認証

を参考にして Basic 認証を行う。認証を行うには、Net::HTTP::Get クラスを利用する。

Net::HTTPRequest によると、

HTTP リクエストを抽象化するクラスです。Net::HTTPRequest は抽象クラスなので実際にはサブクラスの

  • Net::HTTP::Get
  • Net::HTTP::Post
  • Net::HTTP::Head
  • Net::HTTP::Put

を使用してください。

Net::HTTP を使い、IP アドレスが表示されるページを取得するために、GET リクエストを投げる。

require 'net/https'
require 'kconv'

IP_ADDRESS = '192.168.11.1'
ID = 'root'
PASSWD = 'パスワード'
SETTING_PAGE = '/cgi-bin/cgi?req=frm&frm=info.html'

def getHtml(ip, id, passwd, page)
  req = Net::HTTP::Get.new(page)
  req.basic_auth(id, passwd)
  Net::HTTP.start(ip) do|http|
    response = http.request(req)
    return Kconv.tosjis(response.body)
  end
end

puts getHtml(IP_ADDRESS, ID, PASSWD, SETTING_PAGE)

これにより、「システム情報」のページが出力されることを確認した。

 

4. XPath を利用して、HTML から IP アドレスを抽出する

HMTL から XPath を利用して、要素を取得する関数

次に、上記で取得した HTML を Hpricot で解析。XPath を使って、IP アドレスが書かれた文字列を取得する。

関数 getIpAddress は、HTML から XPath を用いて要素を取得する。

上記の getHtml 関数で得た「システム情報」ページの HTML を、関数 getIpAddress の引数 html 与える。

def getIpAddress(html, xpath)
  doc = Hpricot.parse(html)
  (doc/xpath).each do |td|
   return $1 if /(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ =~ td.inner_html
  end
end

XPATH = '/html/body/form/table/tr[3]/td[2]/table/tr[5]/td[2]'
puts getIpAddress(html, XPATH)

これにより、IP アドレスが表示された。

 

Firebug で XPath を取得

XPath を記述するために、Firefox のアドオン Firebug を利用した。 Firebug で XPath を得るには、

  1. HTML の要素を選択し、
  2. 「右クリック > XPath をコピー」。

取得した XPath は、以下の通り。

/html/body/form/table/tbody/tr[3]/td[2]/table/tbody/tr[5]/td[2]

しかし、Hpricot で、この XPath を用いて IP アドレスを取得できなかった。

 

Firefox, Google Chrome で自動的に挿入される HTML 要素

なぜか上記の XPath から `tbody’ を削除することによって、要素を取得できた。

これは、http://ircarchive.info/ruby-lang/2007/4/12/10.html によると、

firefox inserts tbodies after tables if they're not there

Firefox は、table 要素の後に tbody 要素がないと、tbody 要素を挿入する。

試しに、HTML で tbody 要素を書かずに table を書き、Firebug で確かめたら、確かに tbody が挿入されているのを確認した。 Google Chrome も同じ現象が生じた。

 

5. 全体のソースコード

require 'net/https'
require 'kconv'
require 'hpricot'

# ルータの IP アドレス
IP_ADDRESS = '192.168.11.1'
# Basic 認証のための ID とパスワード
ID = 'root'
PASSWD = ''
# 「システム情報」のページ
SETTING_PAGE = '/cgi-bin/cgi?req=frm&frm=info.html'

# IP アドレスを取得するための XPath
XPATH = '/html/body/form/table/tr[3]/td[2]/table/tr[5]/td[2]'

def getHtml(ip, id, passwd, page)
  req = Net::HTTP::Get.new(page)
  req.basic_auth(id, passwd)
  Net::HTTP.start(ip) do|http|
    response = http.request(req)
    return Kconv.tosjis(response.body)
  end
end

def getIpAddress(html, xpath)
  doc = Hpricot.parse(html)
  (doc/xpath).each do |td|
   return $1 if /(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ =~ td.inner_html
  end
end

html = getHtml(IP_ADDRESS, ID, PASSWD, SETTING_PAGE)
puts getIpAddress(html, XPATH)