Home > Ruby > Rubyで2chの市況1板のニュースを集めTweetする

Rubyで2chの市況1板のニュースを集めTweetする

  • 2010-05-05 (水) 4:15
  • Ruby

2chの市況1板には多くの人(個人投資家)が集まって、株価に一喜一憂している。そのため、何かよい材料がないか常に探し求めている状況にある。もしよいニュースを見つけたら「キタ━━━━━━(゜∀゜)━━━━━━」とすぐに書き込んでくれる。これは場合によると自分の見ているニュースサイトに掲載されるより早く情報を手に入れられるかも知れない。

しかし、スレッドは多く全部見ているのは大変である。雑談も多いし、必要な情報だけいち早く報告したらトレードに有利なのではないかと考えた。信頼性の高い情報はソースを載せることが多いので、ニュースサイトのURL付きの書き込みを有用度が高いと判断することにする。

スレッド一覧を取得する

スレッドの一覧は前回書いたようにsubject.txtを見れば手に入る。これをmechanizeでgetする。

BOARD = "http://anchorage.2ch.net/livemarket1/"
DAT = "dat/"
SUBJECT = "subject.txt"

このように定数を用意しておく。

株価コードで絞り込む

市況1板では慣例である銘柄についてのスレッドに株価コードを入れることになっている。自分にとって関心のある銘柄のコードで探せば上手く見つけられる。これを事前に用意して配列に入れておく。

STOCK_CODE = [
"2497",
"5401",
"5405",
"5411",
"5713",
"6301",
"6752",
"6758",
"7203",
"7974",
"8002",
"8031",
"8058",
"8306",
"8316",
"8411",
"8802",
"9132",
"9433",
"9984",
"日経"
]

ここでは出来高の大きそうな銘柄と「日経」に関するスレッドを読むことにする。1行ずつSTOCK_CODEの配列にある文字列を含むか見ていき、該当するものだけをdatsという配列に格納する。

#---------------------------------------------------------------------------
# getThreads
#---------------------------------------------------------------------------
def getThreads(list)

	# 読むべきdatリスト
	dats = []

	# 1行ずつ判定
	list.each_line do |line|
		d = line.split( /<>/ )
		STOCK_CODE.each do |code|
			if d[1].include?(code) then
				dats << d[0]
			end
		end
	end

	#重複があれば除去
	dats.uniq!

	return dats

end

不要なDATファイルを削除する

過去に取得したものの、既に板から消えてしまったDATファイルは不要だから削除する。

#---------------------------------------------------------------------------
# deleteThreads
#---------------------------------------------------------------------------
def deleteThreads(dats)

	# カレントディレクトリのファイル一覧を消去候補リストに入れる
	fileList = Dir::entries(Dir::pwd)

	# datファイル以外は消去リストから取り除く
	fileList.delete_if do |x|
		/\.dat$/ !~ x
	end

	# カレントディレクトリの.datファイルでdatsにないものを消す
	fileList.each do |name|

		# flagがtrueのものは消す
		flag = true

		dats.each do |dat|
			if name == dat then
				# 取得したいdatファイルは消さないでおく
				flag = false
			end
		end

		# datファイルで不要なものは削除する
		if flag == true then
			File.delete(name)
			$log.info(name + "を消去しました")
		end

	end
end

必要なスレッドを読む

dats配列に格納されたスレッドを読む。ただし、丸ごと読むとサーバに負担をかけるので差分ダウンロードをする。差分ダウンロードはwgetでもできるが、差分だけ解析する処理が面倒臭いので自力でやることにする。

差分ダウンロードはHTTPのリクエストヘッダにRangeヘッダを追加してリクエストすればよい。まずローカルに保存されたDATファイルがあるなら容量を計算して、それをRangeにセットすればよい。転送量を減らすためにaccept-encodingにgzipを入れておく。

これでリクエストしたものは差分になる。簡易あぼーん対策として、最初に改行コードが来ていなければ、あぼーんによってスレッドの構成が変わっていると判断する。市況1板ではそれほど個人情報さらしなどの悪質な書き込みがないので、簡易対策だけに留めるが、必要なら丸ごと再取得する。

連続してリクエストするとサーバに負荷をかけて迷惑になるのでsleepを適当に入れる。

#---------------------------------------------------------------------------
# 必要なスレッドをクロールする
#---------------------------------------------------------------------------
def crawl( name )

	daturl = BOARD + DAT + name
	$log.info( daturl + "を取得中")

	header = {}
	header['User-Agent']= 'Monazilla/1.00 (super-hikky/1.0)'

	length = 0
	if File.exist?( name )
		length = File.stat( name ).size
		datfile = open( name, 'a' )
	else
		datfile = open(  name, 'w' )
	end

	if length > 0
		header['Range'] = "bytes=#{length-1}-"
		header['accept-encoding'] = "gzip"
	end

	url = URI.parse(daturl)
	res = Net::HTTP.start(url.host, url.port) do |http|
		http.get(url.path,header)
	end

	#簡易あぼーん対策
	if res.body[0] != '\n' then
		datfile.print res.body
   		analyze(res.body)
	end

	datfile.close
	sleep(10)
end

ニュースを取り出す

書き込まれたURLが全てニュースとは限らない。そこで、ニュースサイトのドメインを用意しておく。

NEWS_SITE =
[
"www.bloomberg.co.jp",
"jp.reuters.com/article",
"www.nikkei.co.jp/news",
"www3.nhk.or.jp/news",
"www.yomiuri.co.jp",
"www.asahi.com",
"www.business-i.jp",
"sankei.jp.msn.com",
"news.livedoor.com/article",
"headlines.yahoo.co.jp",
"www.tokyo-np.co.jp/article",
"news.searchina.ne.jp",
"www.jiji.com",
"www.worldtimes.co.jp",
]

これに該当するものだけをニュースサイトであると判断する。過不足があればこの配列に足してもよい。2chではhttpをttpと書くこともあるので、ドメイン名のみにしておく。これを用いて判断する。

重複したニュースを読むのはコストの無駄なので、recent_news.txtに既に読んだニュースのURLを記録しておく。

新しく取得した差分から1行ずつURLが含まれているかscanする。URLが含まれるときはrecent_news.txtに含まれていないのを確認して、news配列に入れていく。また、recent_news.txtにも記録する。もしかするとrecent_news.txtに記録するタイミングはTwitterにつぶやいた後の方がいいかもしれない。

#------------------------------------------------------------------------------------------
# ニュースサイトを抽出
#------------------------------------------------------------------------------------------
def analyze( body )
	body.each_line do |each_res|

		# URLのみ取り出す
		r = each_res.scan(/ttp\:\/\/([\w\+\$\;\?\.\%\,\!\#\~\*\/\:\@\&\\\=\_\-]+)/)

		# URLが入っている場合
		if r.size > 0 then

			# 全てのURLを精査
			r.each do |url|

				NEWS_SITE.each do |news|

					if url[0].include?( news ) then

						# 過去に読んでいるならtrue
						flag = false
						news_url = "http://" + url[0]

						recent_news = open("recent_news.txt", "a+")
						recent_news.each do |line|
							if line.include?(news_url) then
								flag = true
								break
							end
						end

						if flag == false then
							$news << news_url
							recent_news.puts(news_url)
						end

						recent_news.close

					end
				end # each
			end # each
		end # if
	end #each
end

ニュースを読む

ニュースを読み、Twitterにつぶやく。まず、前回取得したOAuthのキー類を用意する。

CONSUMER_KEY = "GDzTag(略)"
CONSUMER_SECRET = "7TbeOj9waUkqVCOiP(略)"
ACCESS_TOKEN = "91938679-(略)"
ACCESS_TOKEN_SECRET = "hvwhVudtK8QOD(略)"

これを用いて

#------------------------------------------------------------------------------------------
# ニュースサイトを抽出
#------------------------------------------------------------------------------------------
def readNews

	$news.each do |url|

		begin
			$agent.get(url)
			puts "getting " + url

			title = $agent.page.title.toutf8.chomp
			$log.info(title)

			text = title + " " + url

			#OAuth
			# http://d.hatena.ne.jp/shibason/20090802/1249204953を参考にしました
			consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, :site => "http://twitter.com")
			oauth = Twitter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
			oauth.authorize_from_access(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
			twitter_client = Twitter::Base.new(oauth)
			twitter_client.update(text)

		rescue
		rescue Timeout::Error
		ensure
		end

	end
end

実行する

#---------------------------------------------------------------------------
# main
#---------------------------------------------------------------------------
def main

	# 板一覧を取得
	$agent.get(BOARD + SUBJECT)
	board = $agent.page.body.toutf8

	puts board

	# 読むべきスレッド名を取得
	dats = getThreads(board)

	# 不要なDATファイルを消去する
	deleteThreads(dats)

	# 必要なスレッドを読む
	dats.each do |name|
		crawl(name)
	end

	# ニュースをげっと
	if $news.size > 0 then
		$log.info("ニュースあり")
		readNews
	else
		$log.info("ニュースがありません><")
	end
end

#---------------------------------------------------------------------------
# エントリポイント
#---------------------------------------------------------------------------

#$log = Logger.new("log.txt")
$log = Logger.new(STDOUT)
$log.level = Logger::INFO
$agent = Mechanize.new

#かき集めたニュースURLが入る配列
$news = []

#mainを呼ぶ
main

実行結果

1272986450.dat<>■■■明日の日経平均を予想するスレ14685■■■ (54)
1272984242.dat<>日経225先物オプション実況スレ6264 (907)
9241004012.dat<>【2ちゃんねる十周年】携帯からお試し●を使ってみよう! 【規制?】 (30)
(中略)
I, [2010-05-05T03:10:00.196231 #858]  INFO -- : 1272744747.datを消去しました
(中略)
I, [2010-05-05T03:10:00.198558 #858]  INFO -- : 1272876392.datを消去しました
I, [2010-05-05T03:10:00.198603 #858]  INFO -- : http://anchorage.2ch.net/livemarket1/dat/1272986450.datを取得中
I, [2010-05-05T03:10:01.709770 #858]  INFO -- : http://anchorage.2ch.net/livemarket1/dat/1272984242.datを取得中
(中略)
I, [2010-05-05T03:11:03.133612 #858]  INFO -- : http://anchorage.2ch.net/livemarket1/dat/1266856385.datを取得中
I, [2010-05-05T03:11:04.344469 #858]  INFO -- : http://anchorage.2ch.net/livemarket1/dat/1270186921.datを取得中
I, [2010-05-05T03:33:28.767688 #1070]  INFO -- : ニュースあり
getting http://www.asahi.com/photonews/images/TKY201004290227.jpg
getting http://jp.reuters.com/article/topNews/idJPJAPAN-15081720100430
I, [2010-05-05T03:33:30.066649 #1070]  INFO -- : 
    ギリシャは厳しい緊縮財政措置の構え、市場は安心感で回復
| Reuters

getting http://www.bloomberg.co.jp/apps/news?pid=90920008&sid=au._wm7SnJDQ
I, [2010-05-05T03:33:33.845826 #1070]  INFO -- :   
		       ギリシャでデモ活動が激化、遺跡アクロポリス占拠も−緊縮財政に抗議  - Bloomberg.co.jp

なお、ここでつぶやいたニュースはボットをフォローすれば読むことができる。

参考になる本

集合知プログラミング

著者/訳者:Array

出版社:オライリージャパン( 2008-07-25 )

定価:¥ 3,570

大型本 ( 392 ページ )

ISBN-10 : 4873113644

ISBN-13 : 9784873113647


どちらかというと機械学習の本で、サポートベクターマシン(SVM)とか面白い話題にも踏み込んである。

Javaスパイダーツールサンプル&クックブック―自動アクセス&収集・加工プログラム

著者/訳者:Array

出版社:秀和システム( 2005-05 )

定価:¥ 2,730

単行本 ( 377 ページ )

ISBN-10 : 4798010618

ISBN-13 : 9784798010618


好きな本だけど新品で手に入れることは難しいようだ。

Spidering hacks―ウェブ情報ラクラク取得テクニック101選

著者/訳者:Kevin Hemenway Tara Calishain

出版社:オライリー・ジャパン( 2004-05 )

定価:¥ 3,675

単行本 ( 516 ページ )

ISBN-10 : 4873111870

ISBN-13 : 9784873111872


やや古いけどいい本。Perlで書いてあるけど内容は問題ない。マナーについてなど単に技術的な話だけに留まらないので是非読むべき。オライリーなのでGoogle Booksで読むことができる。

Comments:0

Comment Form
Remember personal info

Trackback+Pingback:0

TrackBack URL for this entry
http://blog.neoneet.jp/2010/05/05/ruby%e3%81%a72ch%e3%81%ae%e5%b8%82%e6%b3%811%e6%9d%bf%e3%81%ae%e3%83%8b%e3%83%a5%e3%83%bc%e3%82%b9%e3%82%92%e9%9b%86%e3%82%81tweet%e3%81%99%e3%82%8b/trackback/
Listed below are links to weblogs that reference
Rubyで2chの市況1板のニュースを集めTweetする from 週刊(月刊?)プレカリアート

Home > Ruby > Rubyで2chの市況1板のニュースを集めTweetする

Search
Feeds
Meta

Page Top