それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

オークファンのスクレーパー。

もっとみんなヤフオクやろうよ。ヤフオクって出品者の性格が全面にでてて、面白いです。面倒くさがり屋、こだわり屋・・・etc
ヤフオクでこれいくらになるんだろう?と思った時に過去ログから調べてくれるのがオークファン。なんでヤフーはこの機能を提供しないかわからないけど。オークファンでとりあえず過去の落札金額を参考にすることは多い。


(オークファン)

だけど、人気商品は何ページもあって面倒なんだよ。

ヤフオクで、相場を調べるのに便利なオークファン。でも何ページもあって面倒だよね。
じゃぁ、それScraperでボットで取得しちゃおう

オークファンの一括取得

takuya@mba: ~ $ ruby オークファン.rb iphone5

とやれば、検索できる限界まで取得しに行ってCSVファイルにします。

CSV でオークファンの一括取得が出来る

〓GPP新製品〓SB版 iPhone 4S ロック解除 simフリー★大決算,2480 ,2012/9/19
□SoftBank iPhone4S 32GB ジャンク品(外装中古)ホワイト□,21000 ,2012/9/19
大黒屋■au iPhone4S 16GB 黒 極美品 白ロム 1円〜 96010 ...,27501 ,2012/9/19
高品質】iPhone4S 液晶ガラス 修理 カスタムキット工具付★紫p,3000 ,2012/9/19
【iOS5.1.1全対応】【iPad2/iPhone4S/iPod】 AVケーブ ...,1480 ,2012/9/19
iPhone4S/iPhone4用可愛立体パンダ型ケース 衝撃緩衝■ブルー,1 ,2012/9/19
未発売◆iphone5デザインのiphone4S 用バックパネル+工具付き,1000 ,2012/9/19
AU iPhone 4S用 アクティベーション アクティベートSIMカードa,580 ,2012/9/19
「iPhone 4S」専用 ガラス製 バックパネル スケルトン ピンク B,980 ,2012/9/19
新品未使用★SoftBank★iPhone4S 16GB★ブラック★MD235検 ...,31980 ,2012/9/19
iphone4sケースカバースヌーピーソフトバンクau新品1円~,530 ,2012/9/19
送料無料★iPhone4sDOCKケーブル 4GS充電USB延長ケーブル 2m,480 ,2012/9/19
〓GPP新製品〓au版 iPhone 4S ロック解除 simフリー★大決算♀,2480 ,2012/9/19
iPhone4S/4カバー/クロコダイルタイプ/カードケース付 黒F30,1000 ,2012/9/19
iPhone4S 64G  白,31000 ,2012/9/19
10高品iPhone4S/4保護ケース☆アクリル樹脂☆10色,1 ,2012/9/19

コマンドの部分

require './aucfun.rb'
raise "検索ワードを引数に\n	" if ARGV.size < 1
aucfun = Aucfun.new
aucfun.login("takuyaXXXXX@gmail.com","******" ) #ログインID
aucfun.search(ARGV.join(' '))

実際のクラス。

#coding:utf-8
#よく使うので読み込んでおく
require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE #とりあえずこれで
$KCODE='u'
require 'date'
require 'kconv'
require 'rubygems'
gem "mechanize" , "=2.5.1"
require 'mechanize'
class Mechanize::HTTP::Agent
	def post_connect uri, response, body_io # :yields: agent, uri, response, body
     @post_connect_hooks.each do |hook|
       begin
         hook.call self, uri, response, body_io
       ensure
         body_io.rewind
       end
     end
   end
end

class Aucfun
	attr_accessor :m, :out
	def initialize()
		@m = Mechanize.new
		@login = false
	end
	def login(id,pw)
		@m.get "http://aucfan.com/"
		@m.page.links.select {|e| e.text.toutf8=~/ログイン/}.first.click
		f = m.page.forms[1]
		f.field_with( :name=>"id").value = id
		f.field_with( :name=>"pw").value = pw
		f.submit
		@login = true
	end
	def search(keyword)
		url = "http://aucfan.com/"
		@m.get url
		@m.page.forms[2].field_with({:name=> "search",:type=>"text"}).value = keyword
		@m.page.forms[2].submit
		start_url = @m.page.uri.to_s
		@out = open("オークファン- #{keyword}.csv", "w")
		self.start start_url
	end


	def start(start_url)
		@m.post_connect_hooks << lambda{|ua,uri,res,body_io|
			body = body_io.read.toutf8
			body.gsub! /euc-jp/,"utf-8"
			body_io.truncate body_io.pos
			body_io.rewind
			body_io.puts body
		}
		begin 
			@m.page.body.strip.toutf8 =~ /(\d+)(\d+)/
			year = $1
			month = $2
			$stderr.puts @m.page.title
			$stderr.flush
			begin 
				self.collect_results
			end while self.go_next_page
		end while self.go_next_month
		@out.close
	end
	def collect_results
		d = current_month
		year = d.year
		month = d.month
		$stderr.puts "#{year}/#{month}を解析"
		ret = m.page.search("div.main>table>tbody>tr.results_bid").map{|e|
			 title  = e.search("a.item_title").text.toutf8.strip.gsub( /,/ , "" ) 
			 price  = e.search("a.item_price").text.toutf8.strip.gsub(/,/,"").gsub(/$/, "") 
			 date   = e.search(".results-limit").text.toutf8.strip
			 if  date =~// then
				 date   = date.gsub(/^'(\d+)/, ($1.to_i+2000).to_s).gsub(/||/ , "/").gsub(/\/$/, "")
			 else
				 date   = date.gsub(/^/, "#{year}/").gsub(//, "/").gsub(//, "")          
			 end
			 list = []
			 list << title
			 list << price
			 list << date
			 #list << "*枚数チェック!" if price.to_i > 5000
			 list 
		}
		ret.each{|e|
			@out.puts e.join(",")
			@out.flush
		}
	end
	def current_month
		@m.page.body.to_s.strip.toutf8 =~ /(\d+)(\d+)/
		year = $1.to_i
		month = $2.to_i
		d = Date.new(year,month,1)
	end

	def has_more_page?
		m.page.links.select{|e| e.text.toutf8.strip=~/次の30件/}.size > 0
	end
	def go_next_page
		return false unless has_more_page?
		m.page.links.select{|e| e.text.toutf8.strip=~/次の30件/}.first.click
	end
	def has_more_month?
		date = self.current_month
		return false if @login == true   and date <= (Date.today-365) #ログインしても1年以上遡れないので
		return false if @login == false  and date <= (Date.today-90)  #非ログインなら90日まで
		m.page.links.select{|e| e.text.toutf8.strip=~/次の月/}.size > 0
	end
	def go_next_month
		return false unless has_more_month? 
		m.page.links.select{|e| e.text.toutf8.strip=~/次の月/}.first.click
	end
end

ボットアクセス便利ですね。

Mechanize の細かい所

Mechanizeにはなぜか post_connect_hook に不思議なコードがあって、レスポンスbodyを書き換えできない。
そもそも、書き換えを意図した使用だと思うけど。bodyは書き換えできない。なのでモンキーパッチを当てている。

オークファンの場合、ヤフオクの仕様に引きづられて、文字コードEUCなんですね。

Mechanizeの文字コード周りは解決してると思ってたら、やっぱりダメで、モンキーパッチが必要でした。

オークファンみたいなアーカイブって

ログっ取ってるだけだし。毎日定期バッチで済むし。なんで、誰も類似やんないの?
検索結果だって読み込みだけし、結果はキャッシュ効くし。とっても簡単なシステムだと思うけど。。。。

ヤフオクの行動原理は面白そう


オークションをもっと簡単にできたら便利なのになぁ。