<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>週刊（月刊？）プレカリアート</title>
	<atom:link href="http://blog.neoneet.jp/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.neoneet.jp</link>
	<description>ニートになりそうな私が好き勝手書きます</description>
	<lastBuildDate>Sun, 30 May 2010 14:11:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>ミルカさんボット</title>
		<link>http://blog.neoneet.jp/2010/05/%e3%83%9f%e3%83%ab%e3%82%ab%e3%81%95%e3%82%93%e3%83%9c%e3%83%83%e3%83%88/</link>
		<comments>http://blog.neoneet.jp/2010/05/%e3%83%9f%e3%83%ab%e3%82%ab%e3%81%95%e3%82%93%e3%83%9c%e3%83%83%e3%83%88/#comments</comments>
		<pubDate>Mon, 17 May 2010 16:28:17 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[コンピュータ]]></category>
		<category><![CDATA[プログラミング]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=260</guid>
		<description><![CDATA[				
				ベクトルってつぶやきに反応してこっそり「&#8230;ヴェクタ」とつぶやくbotとかいないかな
				
				という要望があったので作ってみた。
				基本
				ほとんどの手順は株ニュースと同じ。今回はTwitter::Searchを使う。結果をHashie::Meshで受け取るので、必要なものだけ取り出す。一度RTしたものには反応しないことにする。
				以下ソース。
				
#!/opt/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'
require 'logger'

require 'oauth'
require 'twitter'

#---------------------------------------------------------------------------
# 定数
#---------------------------------------------------------------------------

CONSUMER_KEY = "きー"
CONSUMER_SECRET = "しーくれっと"
ACCESS_TOKEN = "とーくん"
ACCESS_TOKEN_SECRET = "とーくんしーくれっと"

#---------------------------------------------------------------------------
# Retweet
#---------------------------------------------------------------------------
def retweet( r )

	user = r["from_user"]
	text = r["text"]
	id = r["id"].to_s

	# もともと「ヴェクタ」を喋るものには反応しない
	return if text.include?("ヴェクタ")

	# 手抜き
	f = open("recent_tweet.txt", "a")
	f.close

	# 過去にRTしているか調べる
	f = open("recent_tweet.txt", "r")
	while recent_id = f.gets
		if recent_id.include?(id) then
			f.close
			return
		end
	end

	# RTする
	f = open("recent_tweet.txt", "a")
	f.puts(id)
	consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, :site => "http://twitter.com")
	oauth = [...]]]></description>
			<content:encoded><![CDATA[				<blockquote><p>
				ベクトルってつぶやきに反応してこっそり「&#8230;ヴェクタ」とつぶやくbotとかいないかな
				</p></blockquote>
				<p>という要望があったので作ってみた。</p>
				<h2>基本</h2>
				<p>ほとんどの手順は株ニュースと同じ。今回はTwitter::Searchを使う。結果をHashie::Meshで受け取るので、必要なものだけ取り出す。一度RTしたものには反応しないことにする。</p>
				<p>以下ソース。</p>
				<pre class="prettyprint">
#!/opt/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'
require 'logger'

require 'oauth'
require 'twitter'

#---------------------------------------------------------------------------
# 定数
#---------------------------------------------------------------------------

CONSUMER_KEY = "きー"
CONSUMER_SECRET = "しーくれっと"
ACCESS_TOKEN = "とーくん"
ACCESS_TOKEN_SECRET = "とーくんしーくれっと"

#---------------------------------------------------------------------------
# Retweet
#---------------------------------------------------------------------------
def retweet( r )

	user = r["from_user"]
	text = r["text"]
	id = r["id"].to_s

	# もともと「ヴェクタ」を喋るものには反応しない
	return if text.include?("ヴェクタ")

	# 手抜き
	f = open("recent_tweet.txt", "a")
	f.close

	# 過去にRTしているか調べる
	f = open("recent_tweet.txt", "r")
	while recent_id = f.gets
		if recent_id.include?(id) then
			f.close
			return
		end
	end

	# RTする
	f = open("recent_tweet.txt", "a")
	f.puts(id)
	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)

	tweet = "...ヴェクタ QT @#{user} #{text}"
	twitter_client.update(tweet, {:in_reply_to_status_id => id})
#	twitter_client.update(tweet)
	$log.info("Twitterに投稿しました：" + tweet)
	f.close

end

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

	Twitter::Search.new('ベクトル').each do |r|

		if r.class == Hashie::Mash then
			retweet(r)
		end
	end
end

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

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

main
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/%e3%83%9f%e3%83%ab%e3%82%ab%e3%81%95%e3%82%93%e3%83%9c%e3%83%83%e3%83%88/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rubyでmixiの日記をバックアップする</title>
		<link>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7mixi%e3%81%ae%e6%97%a5%e8%a8%98%e3%82%92%e3%83%90%e3%83%83%e3%82%af%e3%82%a2%e3%83%83%e3%83%97%e3%81%99%e3%82%8b/</link>
		<comments>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7mixi%e3%81%ae%e6%97%a5%e8%a8%98%e3%82%92%e3%83%90%e3%83%83%e3%82%af%e3%82%a2%e3%83%83%e3%83%97%e3%81%99%e3%82%8b/#comments</comments>
		<pubDate>Wed, 05 May 2010 09:50:09 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=253</guid>
		<description><![CDATA[				mixiを退会すると日記は消えてしまいます。その日記を手っ取り早く手元に残しておきたいとき、1つずつ手作業でやっていると大変です。ここではそれを自動化します。
				準備
				まず普通にWebブラウザでmixiにログインして日記を見ます。FirefoxだとFirebugが便利です。
				
				FirebugでDOMツリーを見ていくと該当の部分が青く反転するので、必要な場所を絞り込んでいきます。最後に、右クリックをして「XPathをコピー」を選んでやると、XPathを入手できます。
				
				xpathを入手したら、これを利用して解析していきます。ただし、Firefoxではtbodyタグを勝手に挟むため、これを除去します。
				コード
				
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'

# 簡易待ち付きget
def get(url)
	sleep(10)
	$agent.get(url);
	$agent.page.encoding = "UTF-8"
end

#変数
owner_id = mixiのID
diary_url = "http://mixi.jp/list_diary.pl?id=#{owner_id}"

# mixiのトップページにアクセス
$agent = Mechanize.new
$agent.get("http://mixi.jp/")
$agent.page.encoding = "UTF-8"

# ログイン
$agent.page.form_with(:name => "login_form" ) do &#124;f&#124;
	f.field_with(:name => 'email').value = "めーるあどれす"
	f.field_with(:name => 'password').value = "ぱすわーど"
	f.click_button
end

puts "ログインしました"

# 日記トップを取得
get(diary_url)
parser = Nokogiri::HTML.parse($agent.page.body, nil)
diary = parser.xpath("/html/body/div/div[2]/div/div[3]/div[2]/div[2]").to_html.toutf8
diaries = diary.scan(/
.*?\"(.*?)\".*?\"(.*?)\".*?li>/)

diaries.each do &#124;d&#124;
	each_diary = "http://mixi.jp/#{d[0]}"
	puts "#{d[1]}を処理"
	Dir::mkdir(d[1])
	Dir::chdir(d[1])

	get(each_diary)

	parser = Nokogiri::HTML.parse($agent.page.body, nil)
	diary_list = parser.xpath('//*[@id="bodyMainAreaMain"]').to_html.toutf8
	entrirs = diary_list.scan(/
.*?\"(view_diary.*?)\">(.*?)]]></description>
			<content:encoded><![CDATA[				<p>mixiを退会すると日記は消えてしまいます。その日記を手っ取り早く手元に残しておきたいとき、1つずつ手作業でやっていると大変です。ここではそれを自動化します。</p>
				<h2>準備</h2>
				<p>まず普通にWebブラウザでmixiにログインして日記を見ます。FirefoxだとFirebugが便利です。</p>
				<p><img src="http://www.onaneet.org/blog/wp-content/uploads/2009/04/firebug.png" /></p>
				<p>FirebugでDOMツリーを見ていくと該当の部分が青く反転するので、必要な場所を絞り込んでいきます。最後に、右クリックをして「XPathをコピー」を選んでやると、XPathを入手できます。</p>
				<p><img src="http://www.onaneet.org/blog/wp-content/uploads/2009/04/xpath.png"/></p>
				<p>xpathを入手したら、これを利用して解析していきます。ただし、Firefoxではtbodyタグを勝手に挟むため、これを除去します。</p>
				<h3>コード</h3>
				<pre class="prettyprint">
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'

# 簡易待ち付きget
def get(url)
	sleep(10)
	$agent.get(url);
	$agent.page.encoding = "UTF-8"
end

#変数
owner_id = mixiのID
diary_url = "http://mixi.jp/list_diary.pl?id=#{owner_id}"

# mixiのトップページにアクセス
$agent = Mechanize.new
$agent.get("http://mixi.jp/")
$agent.page.encoding = "UTF-8"

# ログイン
$agent.page.form_with(:name => "login_form" ) do |f|
	f.field_with(:name => 'email').value = "めーるあどれす"
	f.field_with(:name => 'password').value = "ぱすわーど"
	f.click_button
end

puts "ログインしました"

# 日記トップを取得
get(diary_url)
parser = Nokogiri::HTML.parse($agent.page.body, nil)
diary = parser.xpath("/html/body/div/div[2]/div/div[3]/div[2]/div[2]").to_html.toutf8
diaries = diary.scan(/
<li>.*?\"(.*?)\".*?\"(.*?)\".*?li>/)

diaries.each do |d|
	each_diary = "http://mixi.jp/#{d[0]}"
	puts "#{d[1]}を処理"
	Dir::mkdir(d[1])
	Dir::chdir(d[1])

	get(each_diary)

	parser = Nokogiri::HTML.parse($agent.page.body, nil)
	diary_list = parser.xpath('//*[@id="bodyMainAreaMain"]').to_html.toutf8
	entrirs = diary_list.scan(/
<dt>.*?\"(view_diary.*?)\">(.*?)</)

	entrirs.each do |e|
		entry_url = "http://mixi.jp/#{e[0]}"
		get(entry_url)
		parser = Nokogiri::HTML.parse($agent.page.body, nil)
		contents = parser.xpath('//*[@id="bodyMainAreaMain"]').to_html.toutf8
		name = e[1].gsub("/", "／")
		f = open(name + ".html",'w')
		f.puts "<html>"
		f.puts "<head>"
		f.puts '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'
		f.puts "</head>"
		f.puts "<body>"
		f.puts contents
		f.puts "</body>"
		f.puts "</html>"
		f.close
	end

	Dir::chdir("../")
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7mixi%e3%81%ae%e6%97%a5%e8%a8%98%e3%82%92%e3%83%90%e3%83%83%e3%82%af%e3%82%a2%e3%83%83%e3%83%97%e3%81%99%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyでSBIランキングの売り越し額を調べる</title>
		<link>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7sbi%e3%83%a9%e3%83%b3%e3%82%ad%e3%83%b3%e3%82%b0%e3%81%ae%e5%a3%b2%e3%82%8a%e8%b6%8a%e3%81%97%e9%a1%8d%e3%82%92%e8%aa%bf%e3%81%b9%e3%82%8b/</link>
		<comments>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7sbi%e3%83%a9%e3%83%b3%e3%82%ad%e3%83%b3%e3%82%b0%e3%81%ae%e5%a3%b2%e3%82%8a%e8%b6%8a%e3%81%97%e9%a1%8d%e3%82%92%e8%aa%bf%e3%81%b9%e3%82%8b/#comments</comments>
		<pubDate>Wed, 05 May 2010 09:35:42 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=250</guid>
		<description><![CDATA[				SBI証券は個人投資家が多く使っている証券会社です。通常は個人投資家は機関投資家に搾取されるようにできています。株価が暴落すると個人投資家は飛びつき、下げ止まらないとどんどんナンピン買いをして損を膨らませていきます。投資のスタイルは人それぞれですが、個人が買い越している間は手を出さずに売り越しに転じた辺りで買うというのもよいと思っています。
				これを毎日、前場と後場にランキングを見て、買い代金と売り代金を比較してソートするのは面倒ですので、これを自動でやらせます。
				方針
				いつものようにMechanizeで該当のページを読み、xpathで必要な部分だけ取り出して解析します。
				売りと買いを配列に入れて1つずつ照合、同じコードを持つもので買い代金と売り代金の差を取って新しい配列に入れます。これをソートして出力すればOK。
				実装例
				
#!/usr/bin/ruby -Ku
require 'rubygems'
require 'nokogiri'
require 'mechanize'
require 'logger'
require 'kconv'

SBI_RANKING = "https://trading1.sbisec.co.jp/ETGate/?_ControlID=WPLETmgR001Control&#38;_DataStoreID=DSWPLETmgR001Control&#38;burl=search_market&#38;dir=ranking%2F&#38;file=et_trading_value.html&#38;cat1=market&#38;cat2=ranking&#38;getFlg=on"

BUY_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr/td[2]/table[4]/tr/td[1]/table"
SELL_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[4]/tr/td[3]/table"
DATE_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[3]/tr/td[1]/b"
TIME_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[3]/tr/td[2]"

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

	agent = Mechanize.new
	log = Logger.new(STDOUT)
#	log = Logger.new("sbi_ranking.log")
	log.level = Logger::INFO
	agent.get(SBI_RANKING)
	agent.page.encoding = "Shift_JIS"

	html = agent.page.body
	parser = Nokogiri::HTML.parse(html, nil)

	# 時刻
	ranking_time = parser.xpath(TIME_XPATH)
	/(\d{2,2}):(\d{2,2})$/ =~ ranking_time.to_html.toutf8
	sbi_hour = $1
	sbi_min = $2

	# 日付
	ranking_date = parser.xpath(DATE_XPATH)
	/.*?(\d{1,2})\/(\d{1,2})/ =~ ranking_date.to_html.toutf8
	sbi_month = $1
	sbi_day = $2

	buy_code = []
	buy_name = [...]]]></description>
			<content:encoded><![CDATA[				<p>SBI証券は個人投資家が多く使っている証券会社です。通常は個人投資家は機関投資家に搾取されるようにできています。株価が暴落すると個人投資家は飛びつき、下げ止まらないとどんどんナンピン買いをして損を膨らませていきます。投資のスタイルは人それぞれですが、個人が買い越している間は手を出さずに売り越しに転じた辺りで買うというのもよいと思っています。</p>
				<p>これを毎日、前場と後場にランキングを見て、買い代金と売り代金を比較してソートするのは面倒ですので、これを自動でやらせます。</p>
				<h2>方針</h2>
				<p>いつものようにMechanizeで該当のページを読み、xpathで必要な部分だけ取り出して解析します。</p>
				<p>売りと買いを配列に入れて1つずつ照合、同じコードを持つもので買い代金と売り代金の差を取って新しい配列に入れます。これをソートして出力すればOK。</p>
				<h3>実装例</h3>
				<pre class="prettyprint">
#!/usr/bin/ruby -Ku
require 'rubygems'
require 'nokogiri'
require 'mechanize'
require 'logger'
require 'kconv'

SBI_RANKING = "https://trading1.sbisec.co.jp/ETGate/?_ControlID=WPLETmgR001Control&amp;_DataStoreID=DSWPLETmgR001Control&amp;burl=search_market&amp;dir=ranking%2F&amp;file=et_trading_value.html&amp;cat1=market&amp;cat2=ranking&amp;getFlg=on"

BUY_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr/td[2]/table[4]/tr/td[1]/table"
SELL_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[4]/tr/td[3]/table"
DATE_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[3]/tr/td[1]/b"
TIME_XPATH = "/html/body/div[@id='middleArea']/div[@id='middleAreaM']/table/tr/td[1]/div[@id='main']/table[2]/tr[1]/td[2]/table[3]/tr/td[2]"

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

	agent = Mechanize.new
	log = Logger.new(STDOUT)
#	log = Logger.new("sbi_ranking.log")
	log.level = Logger::INFO
	agent.get(SBI_RANKING)
	agent.page.encoding = "Shift_JIS"

	html = agent.page.body
	parser = Nokogiri::HTML.parse(html, nil)

	# 時刻
	ranking_time = parser.xpath(TIME_XPATH)
	/(\d{2,2}):(\d{2,2})<\/td>$/ =~ ranking_time.to_html.toutf8
	sbi_hour = $1
	sbi_min = $2

	# 日付
	ranking_date = parser.xpath(DATE_XPATH)
	/.*?(\d{1,2})\/(\d{1,2})/ =~ ranking_date.to_html.toutf8
	sbi_month = $1
	sbi_day = $2

	buy_code = []
	buy_name = []
	buy_trading_value = []

	sell_code = []
	sell_name = []
	sell_trading_value = []

	for i in 3..22 do
		buy = parser.xpath(BUY_XPATH + "/tr[#{i}]")
		e = buy.to_html.toutf8.gsub("\n", "")
		/stock_sec_code_mul=(\d{4,4}).*?exchange_code.*?>(.*?)<.*?([\d,]+?)<\/div>/ =~ e
		buy_code << $1
		buy_name << $2
		buy_trading_value << $3.gsub(",", "")
	end

	for i in 3..22 do
		sell = parser.xpath(SELL_XPATH + "/tr[#{i}]")
		e = sell.to_html.toutf8.gsub("\n", "")
		/stock_sec_code_mul=(\d{4,4}).*?exchange_code.*?>(.*?)<.*?([\d,]+?)<\/div>/ =~ e
		sell_code << $1
		sell_name << $2
		sell_trading_value << $3.gsub(",", "")
	end

	result = []
	for i in 0..buy_code.size - 1 do
		for j in 0..sell_code.size - 1 do
			if buy_code[i] == sell_code[j] then
				result.push( [buy_code[i], buy_name[i], ( buy_trading_value[i].to_i - sell_trading_value[j].to_i )] )
			end
		end
	end

	result = result.sort{ |a, b| a[2] <=> b[2] }

	for i in 0..result.size - 1 do
		puts "#{result[i][0]}, #{result[i][1]}, #{result[i][2]}"
	end
end

#---------------------------------------------------------------------------
# エントリポイント
# 単にmainを呼ぶだけ
#---------------------------------------------------------------------------
main
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7sbi%e3%83%a9%e3%83%b3%e3%82%ad%e3%83%b3%e3%82%b0%e3%81%ae%e5%a3%b2%e3%82%8a%e8%b6%8a%e3%81%97%e9%a1%8d%e3%82%92%e8%aa%bf%e3%81%b9%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyでNHKラジオ講座のストリーミングを保存する</title>
		<link>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7nhk%e3%83%a9%e3%82%b8%e3%82%aa%e8%ac%9b%e5%ba%a7%e3%81%ae%e3%82%b9%e3%83%88%e3%83%aa%e3%83%bc%e3%83%9f%e3%83%b3%e3%82%b0%e3%82%92%e4%bf%9d%e5%ad%98%e3%81%99%e3%82%8b/</link>
		<comments>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7nhk%e3%83%a9%e3%82%b8%e3%82%aa%e8%ac%9b%e5%ba%a7%e3%81%ae%e3%82%b9%e3%83%88%e3%83%aa%e3%83%bc%e3%83%9f%e3%83%b3%e3%82%b0%e3%82%92%e4%bf%9d%e5%ad%98%e3%81%99%e3%82%8b/#comments</comments>
		<pubDate>Wed, 05 May 2010 06:58:09 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=245</guid>
		<description><![CDATA[				NHKのラジオ講座は安価に語学を学ぶことができる。ラジオを持っていない人でもストリーミングで聴くことができる。ただしロシア語などいくつかの講座は提供されていない。
				ストリーミングは電車の中でiPodに入れて聴きたいと思ったときには不便である。ここではストリーミングファイルを自動的にダウンロードしてmp3に変換する。
				ツールを手に入れる
				ストリーミングファイルをダウンロードするにはRTMPDumpを使う。ビルド版もあるし、portやyumなどでインストールしてもいい。
				flvをmp3に変換するにはFFmpegを使う。これは多種のコーデックをサポートするツールだが、そのぶんビルドが面倒臭い。既にビルドされているものを使うか、やはりportやyumを使って入手するとよい。
				準備
				まず語学講座のURLを連想配列に入れる。また、flvの情報が記録されているXMLファイルlistdataflv.xmlやストリーミングファイル置き場のURLも定数に入れておく。
				
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'
require 'logger'
require "fileutils"

RTMPDUMP = "rtmpdump "
FFMPEG   = "ffmpeg "
STREAMING_BASE = "rtmp://flv9.nhk.or.jp/flv9/_definst_/gogaku/streaming/flv/"
COURSE_XML = "listdataflv.xml"

COURSE = {
	"基礎英語1" => "http://www.nhk.or.jp/gogaku/english/basic1/",
	"基礎英語2" => "http://www.nhk.or.jp/gogaku/english/basic2/",
	"基礎英語3" => "http://www.nhk.or.jp/gogaku/english/basic3/",
	"英語5分間トレーニング" => "http://www.nhk.or.jp/gogaku/english/training/",
	"ラジオ英会話"  => "http://www.nhk.or.jp/gogaku/english/kaiwa/",
	"入門ビジネス英語" => "http://www.nhk.or.jp/gogaku/english/business1/",
	"実践ビジネス英語" => "http://www.nhk.or.jp/gogaku/english/business2/",
	"まいにち中国語" => "http://www.nhk.or.jp/gogaku/chinese/kouza/",
	"まいにちフランス語" => "http://www.nhk.or.jp/gogaku/french/kouza/",
	"まいにちイタリア語" => "http://www.nhk.or.jp/gogaku/italian/kouza/",
	"まいにちハングル講座" => "http://www.nhk.or.jp/gogaku/hangeul/kouza/",
	"まいにちドイツ語" => "http://www.nhk.or.jp/gogaku/german/kouza/",
	"まいにちスペイン語" => "http://www.nhk.or.jp/gogaku/spanish/kouza/"
}
				ダウンロードメソッドを作る
				まず連想配列の名前を与えてdownloadメソッドを呼ぶ。存在しない講座ならreturnしておく。
				ファイルが乱雑に置かれると面倒なので講座ごとにフォルダを作る。まだフォルダがなければ作成しておく。
				MechanizeでXMLファイルをgetして、NokogiriのXMLパーサで解析する。解析にはxpathを使う。ファイル名はfile属性にある。講座名や日付もあるので利用する。
				まだファイルがないときには、RTMPDumpを用いてflvファイルをダウンロードする。mp3ファイルがまだないときは続けてFFmpegを使って変換する。
				最後に連続してアクセスしないように待ちを入れる。
				
#---------------------------------------------------------------------------
# ラジオ講座のダウンロード
#---------------------------------------------------------------------------

def download(course)

	if COURSE[course] == nil then
		return
	end

	unless( [...]]]></description>
			<content:encoded><![CDATA[				<p>NHKのラジオ講座は安価に語学を学ぶことができる。ラジオを持っていない人でもストリーミングで聴くことができる。ただしロシア語などいくつかの講座は提供されていない。</p>
				<p>ストリーミングは電車の中でiPodに入れて聴きたいと思ったときには不便である。ここではストリーミングファイルを自動的にダウンロードしてmp3に変換する。</p>
				<h2>ツールを手に入れる</h2>
				<p>ストリーミングファイルをダウンロードするには<a href="http://rtmpdump.mplayerhq.hu/">RTMPDump</a>を使う。ビルド版もあるし、portやyumなどでインストールしてもいい。</p>
				<p>flvをmp3に変換するには<a href="http://www.ffmpeg.org/">FFmpeg</a>を使う。これは多種のコーデックをサポートするツールだが、そのぶんビルドが面倒臭い。既にビルドされているものを使うか、やはりportやyumを使って入手するとよい。</p>
				<h2>準備</h2>
				<p>まず語学講座のURLを連想配列に入れる。また、flvの情報が記録されているXMLファイルlistdataflv.xmlやストリーミングファイル置き場のURLも定数に入れておく。</p>
				<pre class="prettyprint">
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'kconv'
require 'logger'
require "fileutils"

RTMPDUMP = "rtmpdump "
FFMPEG   = "ffmpeg "
STREAMING_BASE = "rtmp://flv9.nhk.or.jp/flv9/_definst_/gogaku/streaming/flv/"
COURSE_XML = "listdataflv.xml"

COURSE = {
	"基礎英語1" => "http://www.nhk.or.jp/gogaku/english/basic1/",
	"基礎英語2" => "http://www.nhk.or.jp/gogaku/english/basic2/",
	"基礎英語3" => "http://www.nhk.or.jp/gogaku/english/basic3/",
	"英語5分間トレーニング" => "http://www.nhk.or.jp/gogaku/english/training/",
	"ラジオ英会話"  => "http://www.nhk.or.jp/gogaku/english/kaiwa/",
	"入門ビジネス英語" => "http://www.nhk.or.jp/gogaku/english/business1/",
	"実践ビジネス英語" => "http://www.nhk.or.jp/gogaku/english/business2/",
	"まいにち中国語" => "http://www.nhk.or.jp/gogaku/chinese/kouza/",
	"まいにちフランス語" => "http://www.nhk.or.jp/gogaku/french/kouza/",
	"まいにちイタリア語" => "http://www.nhk.or.jp/gogaku/italian/kouza/",
	"まいにちハングル講座" => "http://www.nhk.or.jp/gogaku/hangeul/kouza/",
	"まいにちドイツ語" => "http://www.nhk.or.jp/gogaku/german/kouza/",
	"まいにちスペイン語" => "http://www.nhk.or.jp/gogaku/spanish/kouza/"
}</pre>
				<h2>ダウンロードメソッドを作る</h2>
				<p>まず連想配列の名前を与えてdownloadメソッドを呼ぶ。存在しない講座ならreturnしておく。</p>
				<p>ファイルが乱雑に置かれると面倒なので講座ごとにフォルダを作る。まだフォルダがなければ作成しておく。</p>
				<p>MechanizeでXMLファイルをgetして、NokogiriのXMLパーサで解析する。解析にはxpathを使う。ファイル名はfile属性にある。講座名や日付もあるので利用する。</p>
				<p>まだファイルがないときには、RTMPDumpを用いてflvファイルをダウンロードする。mp3ファイルがまだないときは続けてFFmpegを使って変換する。</p>
				<p>最後に連続してアクセスしないように待ちを入れる。</p>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# ラジオ講座のダウンロード
#---------------------------------------------------------------------------

def download(course)

	if COURSE[course] == nil then
		return
	end

	unless( FileTest.exist?(course) ) then
		FileUtils.mkdir_p course
	end

	agent = Mechanize.new
	url = COURSE[course] + COURSE_XML
	agent.get(url)
	parser = Nokogiri::XML.parse(agent.page.body, nil)
	parser.xpath("musicdata/music").each do |element|
		flvname = element.attr("file")
		filename = course + "/" + element.attr("kouza") + "_" + element.attr("hdate")

		unless( FileTest.exist?("#{filename}.flv") ) then
			command = RTMPDUMP + "-o #{filename}.flv -r #{STREAMING_BASE+flvname}"
			system(command)
		end

		unless( FileTest.exist?("#{filename}.mp3") ) then
			command = FFMPEG + "-i #{filename}.flv #{filename}.mp3"
			system(command)
		end
	end
	sleep(1)
end
</pre>
				<h2>聴きたい講座をダウンロードする</h2>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# エントリポイント
#---------------------------------------------------------------------------

download("基礎英語1")
</pre>
				<p>聴きたい講座を指定してdownloadメソッドを呼ぶ。</p>
				<h3>実行結果</h3>
				<pre>
RTMPDump&nbsp;v2.2
(c)&nbsp;2010&nbsp;Andrej&nbsp;Stepanchuk,&nbsp;Howard&nbsp;Chu,&nbsp;The&nbsp;Flvstreamer&nbsp;Team;&nbsp;license:&nbsp;GPL
Connecting&nbsp;...
Starting&nbsp;download&nbsp;at:&nbsp;0.000&nbsp;kB
Metadata:
&nbsp;&nbsp;duration&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;900.76
&nbsp;&nbsp;audiodatarate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;64.00
&nbsp;&nbsp;audiodelay&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.04
&nbsp;&nbsp;audiocodecid&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.00
&nbsp;&nbsp;canSeekToEnd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TRUE
7577.419&nbsp;kB&nbsp;/&nbsp;900.81&nbsp;sec&nbsp;(100.0%)
Download&nbsp;complete
FFmpeg&nbsp;version&nbsp;0.5.1,&nbsp;Copyright&nbsp;(c)&nbsp;2000-2009&nbsp;Fabrice&nbsp;Bellard,&nbsp;et&nbsp;al.
&nbsp;&nbsp;configuration:&nbsp;--prefix=/opt/local&nbsp;--disable-vhook&nbsp;--enable-gpl&nbsp;--enable-postproc&nbsp;--enable-swscale&nbsp;--enable-avfilter&nbsp;--enable-avfilter-lavf&nbsp;--enable-libmp3lame&nbsp;--enable-libvorbis&nbsp;--enable-libtheora&nbsp;--enable-libdirac&nbsp;--enable-libschroedinger&nbsp;--enable-libfaac&nbsp;--enable-libfaad&nbsp;--enable-libxvid&nbsp;--enable-libx264&nbsp;--enable-nonfree&nbsp;--mandir=/opt/local/share/man&nbsp;--enable-shared&nbsp;--enable-pthreads&nbsp;--cc=/usr/bin/gcc-4.2&nbsp;--arch=x86_64
&nbsp;&nbsp;libavutil&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;49.15.&nbsp;0&nbsp;/&nbsp;49.15.&nbsp;0
&nbsp;&nbsp;libavcodec&nbsp;&nbsp;&nbsp;&nbsp;52.20.&nbsp;1&nbsp;/&nbsp;52.20.&nbsp;1
&nbsp;&nbsp;libavformat&nbsp;&nbsp;&nbsp;52.31.&nbsp;0&nbsp;/&nbsp;52.31.&nbsp;0
&nbsp;&nbsp;libavdevice&nbsp;&nbsp;&nbsp;52.&nbsp;1.&nbsp;0&nbsp;/&nbsp;52.&nbsp;1.&nbsp;0
&nbsp;&nbsp;libavfilter&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;4.&nbsp;0&nbsp;/&nbsp;&nbsp;1.&nbsp;4.&nbsp;0
&nbsp;&nbsp;libswscale&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;7.&nbsp;1&nbsp;/&nbsp;&nbsp;1.&nbsp;7.&nbsp;1
&nbsp;&nbsp;libpostproc&nbsp;&nbsp;&nbsp;51.&nbsp;2.&nbsp;0&nbsp;/&nbsp;51.&nbsp;2.&nbsp;0
&nbsp;&nbsp;built&nbsp;on&nbsp;Apr&nbsp;11&nbsp;2010&nbsp;12:49:33,&nbsp;gcc:&nbsp;4.2.1&nbsp;(Apple&nbsp;Inc.&nbsp;build&nbsp;5646)&nbsp;(dot&nbsp;1)
Input&nbsp;#0,&nbsp;flv,&nbsp;from&nbsp;'基礎英語1/基礎英語14月30日放送分.flv':
&nbsp;&nbsp;Duration:&nbsp;00:15:00.75,&nbsp;start:&nbsp;0.000000,&nbsp;bitrate:&nbsp;64&nbsp;kb/s
&nbsp;&nbsp;&nbsp;&nbsp;Stream&nbsp;#0.0:&nbsp;Audio:&nbsp;mp3,&nbsp;44100&nbsp;Hz,&nbsp;mono,&nbsp;s16,&nbsp;64&nbsp;kb/s
Output&nbsp;#0,&nbsp;mp3,&nbsp;to&nbsp;'基礎英語1/基礎英語14月30日放送分.mp3':
&nbsp;&nbsp;&nbsp;&nbsp;Stream&nbsp;#0.0:&nbsp;Audio:&nbsp;libmp3lame,&nbsp;44100&nbsp;Hz,&nbsp;mono,&nbsp;s16,&nbsp;64&nbsp;kb/s
Stream&nbsp;mapping:
&nbsp;&nbsp;Stream&nbsp;#0.0&nbsp;-&gt;&nbsp;#0.0
Press&nbsp;[q]&nbsp;to&nbsp;stop&nbsp;encoding
[libmp3lame&nbsp;@&nbsp;0x101812800]lame:&nbsp;output&nbsp;buffer&nbsp;too&nbsp;small&nbsp;(buffer&nbsp;index:&nbsp;9404,&nbsp;free&nbsp;bytes:&nbsp;388)
Audio&nbsp;encoding&nbsp;failed
</pre>
				<p>FFmpegのバージョンによってはAudio encoding failedになる。解決策が<a href="http://wiki.crav-ing.com/index.php?%E5%90%8C%E6%A2%B1ffmpeg%E3%81%AElame%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E4%B8%8D%E5%85%B7%E5%90%88">ここ</a>にある。</p>
				<pre>
[libmp3lame&nbsp;@&nbsp;0x101812800]lame:&nbsp;output&nbsp;buffer&nbsp;too&nbsp;small&nbsp;(buffer&nbsp;index:&nbsp;9404,&nbsp;free&nbsp;bytes:&nbsp;388)
Audio&nbsp;encoding&nbsp;failed
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7nhk%e3%83%a9%e3%82%b8%e3%82%aa%e8%ac%9b%e5%ba%a7%e3%81%ae%e3%82%b9%e3%83%88%e3%83%aa%e3%83%bc%e3%83%9f%e3%83%b3%e3%82%b0%e3%82%92%e4%bf%9d%e5%ad%98%e3%81%99%e3%82%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rubyで2chの市況1板のニュースを集めTweetする</title>
		<link>http://blog.neoneet.jp/2010/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/</link>
		<comments>http://blog.neoneet.jp/2010/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/#comments</comments>
		<pubDate>Tue, 04 May 2010 19:15:22 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=235</guid>
		<description><![CDATA[				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 &#124;line&#124;
		d = line.split( // )
		STOCK_CODE.each do &#124;code&#124;
			if d[1].include?(code) then
				dats  0
		header['Range'] = "bytes=#{length-1}-"
		header['accept-encoding'] = "gzip"
	end

	url = URI.parse(daturl)
	res = Net::HTTP.start(url.host, url.port) do &#124;http&#124;
		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 &#124;each_res&#124;

		# URLのみ取り出す
		r = [...]]]></description>
			<content:encoded><![CDATA[				<p>2chの市況1板には多くの人（個人投資家）が集まって、株価に一喜一憂している。そのため、何かよい材料がないか常に探し求めている状況にある。もしよいニュースを見つけたら「キタ━━━━━━(゜∀゜)━━━━━━」とすぐに書き込んでくれる。これは場合によると自分の見ているニュースサイトに掲載されるより早く情報を手に入れられるかも知れない。</p>
				<p>しかし、スレッドは多く全部見ているのは大変である。雑談も多いし、必要な情報だけいち早く報告したらトレードに有利なのではないかと考えた。信頼性の高い情報はソースを載せることが多いので、ニュースサイトのURL付きの書き込みを有用度が高いと判断することにする。</p>
				<h2>スレッド一覧を取得する</h2>
				<p>スレッドの一覧は前回書いたようにsubject.txtを見れば手に入る。これをmechanizeでgetする。</p>
				<pre class="prettyprint">
BOARD = "http://anchorage.2ch.net/livemarket1/"
DAT = "dat/"
SUBJECT = "subject.txt"
</pre>
				<p>このように定数を用意しておく。</p>
				<h3>株価コードで絞り込む</h3>
				<p>市況1板では慣例である銘柄についてのスレッドに株価コードを入れることになっている。自分にとって関心のある銘柄のコードで探せば上手く見つけられる。これを事前に用意して配列に入れておく。</p>
				<pre class="prettyprint">
STOCK_CODE = [
"2497",
"5401",
"5405",
"5411",
"5713",
"6301",
"6752",
"6758",
"7203",
"7974",
"8002",
"8031",
"8058",
"8306",
"8316",
"8411",
"8802",
"9132",
"9433",
"9984",
"日経"
]
</pre>
				<p>ここでは出来高の大きそうな銘柄と「日経」に関するスレッドを読むことにする。1行ずつSTOCK_CODEの配列にある文字列を含むか見ていき、該当するものだけをdatsという配列に格納する。</p>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# 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
</pre>
				<h3>不要なDATファイルを削除する</h3>
				<p>過去に取得したものの、既に板から消えてしまったDATファイルは不要だから削除する。</p>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# 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
</pre>
				<h2>必要なスレッドを読む</h2>
				<p>dats配列に格納されたスレッドを読む。ただし、丸ごと読むとサーバに負担をかけるので差分ダウンロードをする。差分ダウンロードはwgetでもできるが、差分だけ解析する処理が面倒臭いので自力でやることにする。</p>
				<p>差分ダウンロードはHTTPのリクエストヘッダにRangeヘッダを追加してリクエストすればよい。まずローカルに保存されたDATファイルがあるなら容量を計算して、それをRangeにセットすればよい。転送量を減らすためにaccept-encodingにgzipを入れておく。</p>
				<p>これでリクエストしたものは差分になる。簡易あぼーん対策として、最初に改行コードが来ていなければ、あぼーんによってスレッドの構成が変わっていると判断する。市況1板ではそれほど個人情報さらしなどの悪質な書き込みがないので、簡易対策だけに留めるが、必要なら丸ごと再取得する。</p>
				<p>連続してリクエストするとサーバに負荷をかけて迷惑になるのでsleepを適当に入れる。</p>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# 必要なスレッドをクロールする
#---------------------------------------------------------------------------
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
</pre>
				<h2>ニュースを取り出す</h2>
				<p>書き込まれたURLが全てニュースとは限らない。そこで、ニュースサイトのドメインを用意しておく。</p>
				<pre class="prettyprint">
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",
]
</pre>
				<p>これに該当するものだけをニュースサイトであると判断する。過不足があればこの配列に足してもよい。2chではhttpをttpと書くこともあるので、ドメイン名のみにしておく。これを用いて判断する。</p>
				<p>重複したニュースを読むのはコストの無駄なので、recent_news.txtに既に読んだニュースのURLを記録しておく。</p>
				<p>新しく取得した差分から1行ずつURLが含まれているかscanする。URLが含まれるときはrecent_news.txtに含まれていないのを確認して、news配列に入れていく。また、recent_news.txtにも記録する。もしかするとrecent_news.txtに記録するタイミングはTwitterにつぶやいた後の方がいいかもしれない。</p>
				<pre class="prettyprint">
#------------------------------------------------------------------------------------------
# ニュースサイトを抽出
#------------------------------------------------------------------------------------------
def analyze( body )
	body.each_line do |each_res|

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

		# 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
</pre>
				<h2>ニュースを読む</h2>
				<p>ニュースを読み、Twitterにつぶやく。まず、前回取得したOAuthのキー類を用意する。</p>
				<pre class="prettyprint">
CONSUMER_KEY = "GDzTag（略）"
CONSUMER_SECRET = "7TbeOj9waUkqVCOiP（略）"
ACCESS_TOKEN = "91938679-（略）"
ACCESS_TOKEN_SECRET = "hvwhVudtK8QOD（略）"
</pre>
				<p>これを用いて</p>
				<pre class="prettyprint">
#------------------------------------------------------------------------------------------
# ニュースサイトを抽出
#------------------------------------------------------------------------------------------
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
</pre>
				<h2>実行する</h2>
				<pre class="prettyprint">
#---------------------------------------------------------------------------
# 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
</pre>
				<h2>実行結果</h2>
				<pre>
1272986450.dat&lt;&gt;■■■明日の日経平均を予想するスレ14685■■■&nbsp;(54)
1272984242.dat&lt;&gt;日経225先物オプション実況スレ6264&nbsp;(907)
9241004012.dat&lt;&gt;【２ちゃんねる十周年】携帯からお試し●を使ってみよう！&nbsp;【規制?】&nbsp;(30)
（中略）
I,&nbsp;[2010-05-05T03:10:00.196231&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;1272744747.datを消去しました
（中略）
I,&nbsp;[2010-05-05T03:10:00.198558&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;1272876392.datを消去しました
I,&nbsp;[2010-05-05T03:10:00.198603&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;http://anchorage.2ch.net/livemarket1/dat/1272986450.datを取得中
I,&nbsp;[2010-05-05T03:10:01.709770&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;http://anchorage.2ch.net/livemarket1/dat/1272984242.datを取得中
（中略）
I,&nbsp;[2010-05-05T03:11:03.133612&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;http://anchorage.2ch.net/livemarket1/dat/1266856385.datを取得中
I,&nbsp;[2010-05-05T03:11:04.344469&nbsp;#858]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;http://anchorage.2ch.net/livemarket1/dat/1270186921.datを取得中
I,&nbsp;[2010-05-05T03:33:28.767688&nbsp;#1070]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;ニュースあり
getting&nbsp;http://www.asahi.com/photonews/images/TKY201004290227.jpg
getting&nbsp;http://jp.reuters.com/article/topNews/idJPJAPAN-15081720100430
I,&nbsp;[2010-05-05T03:33:30.066649&nbsp;#1070]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;ギリシャは厳しい緊縮財政措置の構え、市場は安心感で回復
|&nbsp;Reuters

getting&nbsp;http://www.bloomberg.co.jp/apps/news?pid=90920008&amp;sid=au._wm7SnJDQ
I,&nbsp;[2010-05-05T03:33:33.845826&nbsp;#1070]&nbsp;&nbsp;INFO&nbsp;--&nbsp;:&nbsp;&nbsp;&nbsp;
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ギリシャでデモ活動が激化、遺跡アクロポリス占拠も−緊縮財政に抗議&nbsp;&nbsp;-&nbsp;Bloomberg.co.jp
</pre>
				<p>なお、ここでつぶやいたニュースは<a href="http://twitter.com/kabu_news">ボット</a>をフォローすれば読むことができる。</p>
				<h2>参考になる本</h2>
				<div class="tmkm-amazon-view">
					<p><a href="http://www.amazon.co.jp/%E9%9B%86%E5%90%88%E7%9F%A5%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-Toby-Segaran/dp/4873113644%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4873113644"><img src="http://ecx.images-amazon.com/images/I/51FgSThMzVL._SL160_.jpg" border="0" alt="" /></a></p>
					<p><a href="http://www.amazon.co.jp/%E9%9B%86%E5%90%88%E7%9F%A5%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-Toby-Segaran/dp/4873113644%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4873113644">集合知プログラミング</a></p>
					<p><em>著者／訳者：</em>Array</p>
					<p><em>出版社：</em>オライリージャパン( 2008-07-25 )</p>
					<p><em>定価：</em>￥ 3,570</p>
					<p>大型本 ( 392 ページ )</p>
					<p>ISBN-10 : 4873113644</p>
					<p>ISBN-13 : 9784873113647</p>
				<hr class="tmkm-amazon-clear" /></div>
				<p>どちらかというと機械学習の本で、サポートベクターマシン(SVM)とか面白い話題にも踏み込んである。</p>
				<div class="tmkm-amazon-view">
					<p><a href="http://www.amazon.co.jp/Java%E3%82%B9%E3%83%91%E3%82%A4%E3%83%80%E3%83%BC%E3%83%84%E3%83%BC%E3%83%AB%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB-%E3%82%AF%E3%83%83%E3%82%AF%E3%83%96%E3%83%83%E3%82%AF%E2%80%95%E8%87%AA%E5%8B%95%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9-%E5%8F%8E%E9%9B%86%E3%83%BB%E5%8A%A0%E5%B7%A5%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0-%E5%B7%9D%E5%B4%8E-%E5%85%8B%E5%B7%B3/dp/4798010618%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4798010618"><img src="http://ecx.images-amazon.com/images/I/51BBV7Q87RL._SL160_.jpg" border="0" alt="" /></a></p>
					<p><a href="http://www.amazon.co.jp/Java%E3%82%B9%E3%83%91%E3%82%A4%E3%83%80%E3%83%BC%E3%83%84%E3%83%BC%E3%83%AB%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB-%E3%82%AF%E3%83%83%E3%82%AF%E3%83%96%E3%83%83%E3%82%AF%E2%80%95%E8%87%AA%E5%8B%95%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9-%E5%8F%8E%E9%9B%86%E3%83%BB%E5%8A%A0%E5%B7%A5%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0-%E5%B7%9D%E5%B4%8E-%E5%85%8B%E5%B7%B3/dp/4798010618%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4798010618">Javaスパイダーツールサンプル&クックブック―自動アクセス&収集・加工プログラム</a></p>
					<p><em>著者／訳者：</em>Array</p>
					<p><em>出版社：</em>秀和システム( 2005-05 )</p>
					<p><em>定価：</em>￥ 2,730</p>
					<p>単行本 ( 377 ページ )</p>
					<p>ISBN-10 : 4798010618</p>
					<p>ISBN-13 : 9784798010618</p>
				<hr class="tmkm-amazon-clear" /></div>
				<p>好きな本だけど新品で手に入れることは難しいようだ。</p>
				<div class="tmkm-amazon-view">
					<p><a href="http://www.amazon.co.jp/Spidering-hacks%E2%80%95%E3%82%A6%E3%82%A7%E3%83%96%E6%83%85%E5%A0%B1%E3%83%A9%E3%82%AF%E3%83%A9%E3%82%AF%E5%8F%96%E5%BE%97%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF101%E9%81%B8-Kevin-Hemenway/dp/4873111870%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4873111870"><img src="http://ecx.images-amazon.com/images/I/41AT4JG2KQL._SL160_.jpg" border="0" alt="" /></a></p>
					<p><a href="http://www.amazon.co.jp/Spidering-hacks%E2%80%95%E3%82%A6%E3%82%A7%E3%83%96%E6%83%85%E5%A0%B1%E3%83%A9%E3%82%AF%E3%83%A9%E3%82%AF%E5%8F%96%E5%BE%97%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF101%E9%81%B8-Kevin-Hemenway/dp/4873111870%3FSubscriptionId%3DAKIAJUJFHUMWVQBGDBFQ%26tag%3Donaneet-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4873111870">Spidering hacks―ウェブ情報ラクラク取得テクニック101選</a></p>
					<p><em>著者／訳者：</em>Kevin Hemenway Tara Calishain </p>
					<p><em>出版社：</em>オライリー・ジャパン( 2004-05 )</p>
					<p><em>定価：</em>￥ 3,675</p>
					<p>単行本 ( 516 ページ )</p>
					<p>ISBN-10 : 4873111870</p>
					<p>ISBN-13 : 9784873111872</p>
				<hr class="tmkm-amazon-clear" /></div>
				<p>やや古いけどいい本。Perlで書いてあるけど内容は問題ない。マナーについてなど単に技術的な話だけに留まらないので是非読むべき。オライリーなので<a href="http://books.google.co.jp/books?id=vlf1qnRWSCYC&#038;lpg=PA2&#038;ots=Z9kAox0qAS&#038;dq=%E3%82%B9%E3%83%91%E3%82%A4%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%20Hacks&#038;pg=PR3#v=onepage&#038;q&#038;f=false">Google Books</a>で読むことができる。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/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/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RubyでTwitter</title>
		<link>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7twitter/</link>
		<comments>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7twitter/#comments</comments>
		<pubDate>Mon, 03 May 2010 10:41:12 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=226</guid>
		<description><![CDATA[				最初に書いたoauth.rbでトラブルに遭って困った。
				
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'oauth'
require 'twitter'

				すると
				
./oauth.rb:13:&#160;uninitialized&#160;constant&#160;OAuth&#160;(NameError)
	from&#160;/opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in&#160;`gem_original_require'
	from&#160;/opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in&#160;`require'
	from&#160;oauth.rb:3

				となってしまう。原因は簡単で、ファイル名がoauth.rbだったからである。requireはカレントディレクトリを優先して読みに行くので、自分自身をrequireしていた。解決策もシンプルで別の名前にすればいい。
				OAuth認証でTwitterを使う
				まず
				
[root@PC&#160;twitter]#&#160;gem&#160;install&#160;twitter
When&#160;you&#160;HTTParty,&#160;you&#160;must&#160;party&#160;hard!
Building&#160;native&#160;extensions.&#160;&#160;This&#160;could&#160;take&#160;a&#160;while...
Successfully&#160;installed&#160;oauth-0.4.0
Successfully&#160;installed&#160;hashie-0.2.0
Successfully&#160;installed&#160;crack-0.1.6
Successfully&#160;installed&#160;httparty-0.5.2
Successfully&#160;installed&#160;yajl-ruby-0.7.6
Successfully&#160;installed&#160;twitter-0.9.5
6&#160;gems&#160;installed
Installing&#160;ri&#160;documentation&#160;for&#160;oauth-0.4.0...
Installing&#160;ri&#160;documentation&#160;for&#160;hashie-0.2.0...
Installing&#160;ri&#160;documentation&#160;for&#160;crack-0.1.6...
Installing&#160;ri&#160;documentation&#160;for&#160;httparty-0.5.2...
Installing&#160;ri&#160;documentation&#160;for&#160;yajl-ruby-0.7.6...
Installing&#160;ri&#160;documentation&#160;for&#160;twitter-0.9.5...
Installing&#160;RDoc&#160;documentation&#160;for&#160;oauth-0.4.0...
Installing&#160;RDoc&#160;documentation&#160;for&#160;hashie-0.2.0...
Installing&#160;RDoc&#160;documentation&#160;for&#160;crack-0.1.6...
Installing&#160;RDoc&#160;documentation&#160;for&#160;httparty-0.5.2...
Installing&#160;RDoc&#160;documentation&#160;for&#160;yajl-ruby-0.7.6...
Installing&#160;RDoc&#160;documentation&#160;for&#160;twitter-0.9.5...

				一緒にoauth-0.4.0もインストールされる。
				アプリケーションを登録する
				
				Twitterのアカウントがなければ取得する
				Twitterにログインする
				ログインした状態でWebブラウザで「Twitter / アプリケーション」にアクセスする
				「新しいアプリケーションを追加」をする
				必要事項を記入する
				
				アプリ名などは自由に記入すればいい。わかりにくいのは「あなたの招待状」は「送信」を選ぶとよい。そうすると出てくる「Default Access type」は「Read &#038; Write」にしておいた方がいいだろう。
				必要事項を記入するとConsumer keyとConsumer secretなどが表示されるので記録しておく。
				Access token, Access token secretを取得する
				
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'oauth'

# http://d.hatena.ne.jp/shibason/20090802/1249204953を参考にしました

#OAuth
CONSUMER_KEY = "きー"
CONSUMER_SECRET = "しーくれっと"

consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, :site => "http://twitter.com")
request_token = consumer.get_request_token

puts "Access this URL and approve => #{request_token.authorize_url}"
print "Input OAuth Verifier: "
oauth_verifier = gets.chomp.strip

access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)

puts "Access token: #{access_token.token}"
puts "Access token [...]]]></description>
			<content:encoded><![CDATA[				<p>最初に書いたoauth.rbでトラブルに遭って困った。</p>
				<pre class="prettyprint">
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'oauth'
require 'twitter'
</pre>
				<p>すると</p>
				<pre>
./oauth.rb:13:&nbsp;uninitialized&nbsp;constant&nbsp;OAuth&nbsp;(NameError)
	from&nbsp;/opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in&nbsp;`gem_original_require'
	from&nbsp;/opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in&nbsp;`require'
	from&nbsp;oauth.rb:3
</pre>
				<p>となってしまう。原因は簡単で、ファイル名がoauth.rbだったからである。requireはカレントディレクトリを優先して読みに行くので、自分自身をrequireしていた。解決策もシンプルで別の名前にすればいい。</p>
				<h2>OAuth認証でTwitterを使う</h2>
				<p>まず</p>
				<pre>
[root@PC&nbsp;twitter]#&nbsp;gem&nbsp;install&nbsp;twitter
When&nbsp;you&nbsp;HTTParty,&nbsp;you&nbsp;must&nbsp;party&nbsp;hard!
Building&nbsp;native&nbsp;extensions.&nbsp;&nbsp;This&nbsp;could&nbsp;take&nbsp;a&nbsp;while...
Successfully&nbsp;installed&nbsp;oauth-0.4.0
Successfully&nbsp;installed&nbsp;hashie-0.2.0
Successfully&nbsp;installed&nbsp;crack-0.1.6
Successfully&nbsp;installed&nbsp;httparty-0.5.2
Successfully&nbsp;installed&nbsp;yajl-ruby-0.7.6
Successfully&nbsp;installed&nbsp;twitter-0.9.5
6&nbsp;gems&nbsp;installed
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;oauth-0.4.0...
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;hashie-0.2.0...
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;crack-0.1.6...
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;httparty-0.5.2...
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;yajl-ruby-0.7.6...
Installing&nbsp;ri&nbsp;documentation&nbsp;for&nbsp;twitter-0.9.5...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;oauth-0.4.0...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;hashie-0.2.0...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;crack-0.1.6...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;httparty-0.5.2...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;yajl-ruby-0.7.6...
Installing&nbsp;RDoc&nbsp;documentation&nbsp;for&nbsp;twitter-0.9.5...
</pre>
				<p>一緒にoauth-0.4.0もインストールされる。</p>
				<h3>アプリケーションを登録する</h3>
				<ol>
				<li>Twitterのアカウントがなければ取得する</li>
				<li>Twitterにログインする</li>
				<li>ログインした状態でWebブラウザで「<a href="http://twitter.com/oauth_clients">Twitter / アプリケーション</a>」にアクセスする</li>
				<li>「新しいアプリケーションを追加」をする</li>
				<li>必要事項を記入する</li>
				</ol>
				<p>アプリ名などは自由に記入すればいい。わかりにくいのは「あなたの招待状」は「送信」を選ぶとよい。そうすると出てくる「Default Access type」は「Read &#038; Write」にしておいた方がいいだろう。</p>
				<p>必要事項を記入するとConsumer keyとConsumer secretなどが表示されるので記録しておく。</p>
				<h3>Access token, Access token secretを取得する</h3>
				<pre class="prettyprint">
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'oauth'

# http://d.hatena.ne.jp/shibason/20090802/1249204953を参考にしました

#OAuth
CONSUMER_KEY = "きー"
CONSUMER_SECRET = "しーくれっと"

consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, :site => "http://twitter.com")
request_token = consumer.get_request_token

puts "Access this URL and approve => #{request_token.authorize_url}"
print "Input OAuth Verifier: "
oauth_verifier = gets.chomp.strip

access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)

puts "Access token: #{access_token.token}"
puts "Access token secret: #{access_token.secret}"
</pre>
				<p>これを適切な名前を付けて保存する。oauth.rbにしたらひどいトラブルで苦労したので気をつける。実行すると</p>
				<pre>
Access&nbsp;this&nbsp;URL&nbsp;and&nbsp;approve&nbsp;=&gt;&nbsp;http://twitter.com/oauth/authorize?oauth_token=とーくん
Input&nbsp;OAuth&nbsp;Verifier:&nbsp;
</pre>
				<p>と出てくるので、URLにアクセスして出てきた数値を入力すると、Access tokenとAccess token secretが得られる。これを記録しておく。</p>
				<h3>Twitterに何か書いてみる</h3>
				<pre class="prettyprint">
#!/usr/local/bin/ruby -Ku
require 'rubygems'
require 'oauth'
require 'twitter'

CONSUMER_KEY = "きー"
CONSUMER_SECRET = "しーくれっと"
ACCESS_TOKEN = "とーくん"
ACCESS_TOKEN_SECRET = "とーくんしーくれっと"

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("こけこっこ")
</pre>
				<p>実行すると「こけこっこ」とつぶやきが投稿される。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/ruby%e3%81%a7twitter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rubyスクレーピング本を出したい</title>
		<link>http://blog.neoneet.jp/2010/05/ruby%e3%82%b9%e3%82%af%e3%83%ac%e3%83%bc%e3%83%94%e3%83%b3%e3%82%b0%e6%9c%ac%e3%82%92%e5%87%ba%e3%81%97%e3%81%9f%e3%81%84/</link>
		<comments>http://blog.neoneet.jp/2010/05/ruby%e3%82%b9%e3%82%af%e3%83%ac%e3%83%bc%e3%83%94%e3%83%b3%e3%82%b0%e6%9c%ac%e3%82%92%e5%87%ba%e3%81%97%e3%81%9f%e3%81%84/#comments</comments>
		<pubDate>Sun, 02 May 2010 17:18:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=218</guid>
		<description><![CDATA[				というわけで、原稿を書きためることにする。手元に原稿を置いてもいいけど、ブログにまとめた方がモチベーションが上がりそう。あまり細かいことに拘らず、とにかく色々書く。
				2chの板をスクレーピングする
				2chのスレッドから情報を得たいとする。スレッドは板と呼ばれるグループに所属している。ここでは例として「台湾」板から情報を得る。各々の板にはsubject.txtというファイルがある。内容は
				
				1141905139.dat韓国より台湾だと思う人→ (718)
				1236575332.dat麻煩ね～青木由香　堂々と不法就労で警察沙汰 (753)
				1268142329.dat■中山北路２段９３巷１７号ビル名「小室哲哉」■ (32)
				1249183264.dat牛肉麺 (200)
				
				のようなデータがたくさん入っている。subject.txtから、スレッドのタイトルと、スレッドが格納されているファイル名、レス数がわかる。&#60;&#62;が区切り文字になっている。
				次にスレッドを取得する。スレッドはdatというディレクトリの中にある。スレッドを取得するときは、http://academy6.2ch.net/taiwan/dat/1141905139.datのようにリクエストすればいい。ダウンロードをするときにはmechanizeでもよいが、差分ダウンロードなどの機能を考えるとwgetが便利である。
				wgetの-Nオプションはファイルのタイムスタンプを比較して、差分ダウンロードを試みる。2chのスレッドのように少しずつ新しい内容が付け足されていくタイプのファイルをダウンロードする場合には差分でダウンロードする方がよい。
				datファイルの中身は
				
				美麗島の名無桑sage2006/03/09(木) 20:52:19  台湾わ日本を尊敬してるから偉いな 韓国より台湾だと思う人→
				美麗島の名無桑sage2006/03/09(木) 21:03:28  それよりもまずナガノ 
				美麗島の名無桑2006/03/11(土) 23:19:10  観光・グルメ・安全性の面で台湾が上だな。  漢字だから日本と違和感無いし。 
				美麗島の名無桑2006/03/12(日) 03:05:12  台湾ﾏﾝｾｰヽ(&#8216;Д&#8217;)人(&#8216;Д&#8217;)人(&#8216;Д&#8217;)人(&#8216;Д&#8217;)/ 
				
				このようになっている。1行が1レスになる。subject.txtと同様に区切りは&#60;&#62;である。
				コード
				
#!/usr/bin/ruby -Ku
require 'rubygems'
require 'nokogiri'
require 'mechanize'
require 'kconv'

HOST = "http://academy6.2ch.net/taiwan/"
SUBJECT = "/subject.txt"
DAT = "/dat/"

agent = Mechanize.new
agent.get(HOST + SUBJECT)
agent.page.body.toutf8.each do &#124;line&#124;
	array = line.split(//)
	command = "wget -N " + HOST + DAT + array[0]
	puts array[1].chomp + "をダウンロードします"
	system(command)
	sleep(10)
	exit
end

				コードの説明
				取得したsubject.txtを1行ずつeachで見ていく。区切り文字が&#60;&#62;なので、それでsplitする。全てのスレッドを取得することもできるが、サンプルなので1回ダウンロードしたらexitで抜けるようにしてある。あまり連続してアクセスするのはサーバに負担をかけるので1回につき10秒の待ちを入れてある。
				実行例
				
[onaneet@PC [...]]]></description>
			<content:encoded><![CDATA[				<p>というわけで、原稿を書きためることにする。手元に原稿を置いてもいいけど、ブログにまとめた方がモチベーションが上がりそう。あまり細かいことに拘らず、とにかく色々書く。</p>
				<h2>2chの板をスクレーピングする</h2>
				<p>2chのスレッドから情報を得たいとする。スレッドは板と呼ばれるグループに所属している。ここでは例として「台湾」板から情報を得る。各々の板にはsubject.txtというファイルがある。内容は</p>
				<blockquote><p>
				1141905139.dat<>韓国より台湾だと思う人→ (718)<br />
				1236575332.dat<>麻煩ね～青木由香　堂々と不法就労で警察沙汰 (753)<br />
				1268142329.dat<>■中山北路２段９３巷１７号ビル名「小室哲哉」■ (32)<br />
				1249183264.dat<>牛肉麺 (200)
				</p></blockquote>
				<p>のようなデータがたくさん入っている。subject.txtから、スレッドのタイトルと、スレッドが格納されているファイル名、レス数がわかる。&lt;&gt;が区切り文字になっている。</p>
				<p>次にスレッドを取得する。スレッドはdatというディレクトリの中にある。スレッドを取得するときは、http://academy6.2ch.net/taiwan/dat/1141905139.datのようにリクエストすればいい。ダウンロードをするときにはmechanizeでもよいが、差分ダウンロードなどの機能を考えるとwgetが便利である。</p>
				<p>wgetの-Nオプションはファイルのタイムスタンプを比較して、差分ダウンロードを試みる。2chのスレッドのように少しずつ新しい内容が付け足されていくタイプのファイルをダウンロードする場合には差分でダウンロードする方がよい。</p>
				<p>datファイルの中身は</p>
				<blockquote><p>
				美麗島の名無桑<>sage<>2006/03/09(木) 20:52:19 <> 台湾わ日本を尊敬してるから偉いな <>韓国より台湾だと思う人→<br />
				美麗島の名無桑<>sage<>2006/03/09(木) 21:03:28 <> それよりもまずナガノ <><br />
				美麗島の名無桑<><>2006/03/11(土) 23:19:10 <> 観光・グルメ・安全性の面で台湾が上だな。 <br /> 漢字だから日本と違和感無いし。 <><br />
				美麗島の名無桑<><>2006/03/12(日) 03:05:12 <> 台湾ﾏﾝｾｰヽ(&#8216;Д&#8217;)人(&#8216;Д&#8217;)人(&#8216;Д&#8217;)人(&#8216;Д&#8217;)/ <>
				</p></blockquote>
				<p>このようになっている。1行が1レスになる。subject.txtと同様に区切りは&lt;&gt;である。</p>
				<h3>コード</h3>
				<pre class="prettyprint">
#!/usr/bin/ruby -Ku
require 'rubygems'
require 'nokogiri'
require 'mechanize'
require 'kconv'

HOST = "http://academy6.2ch.net/taiwan/"
SUBJECT = "/subject.txt"
DAT = "/dat/"

agent = Mechanize.new
agent.get(HOST + SUBJECT)
agent.page.body.toutf8.each do |line|
	array = line.split(/<>/)
	command = "wget -N " + HOST + DAT + array[0]
	puts array[1].chomp + "をダウンロードします"
	system(command)
	sleep(10)
	exit
end
</pre>
				<h3>コードの説明</h3>
				<p>取得したsubject.txtを1行ずつeachで見ていく。区切り文字が&lt;&gt;なので、それでsplitする。全てのスレッドを取得することもできるが、サンプルなので1回ダウンロードしたらexitで抜けるようにしてある。あまり連続してアクセスするのはサーバに負担をかけるので1回につき10秒の待ちを入れてある。</p>
				<h3>実行例</h3>
				<pre>
[onaneet@PC ~/Desktop/Ruby本]# ruby 2ch.rb
韓国より台湾だと思う人→ (718)をダウンロードします
--2010-05-03 02:02:10--  http://academy6.2ch.net/taiwan//dat/1141905139.dat
Resolving academy6.2ch.net (academy6.2ch.net)... 206.223.152.60
Connecting to academy6.2ch.net (academy6.2ch.net)|206.223.152.60|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 226448 (221K) [text/plain]
Saving to: `1141905139.dat'
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2010/05/ruby%e3%82%b9%e3%82%af%e3%83%ac%e3%83%bc%e3%83%94%e3%83%b3%e3%82%b0%e6%9c%ac%e3%82%92%e5%87%ba%e3%81%97%e3%81%9f%e3%81%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Good luck new year!</title>
		<link>http://blog.neoneet.jp/2009/12/good-luck-new-year/</link>
		<comments>http://blog.neoneet.jp/2009/12/good-luck-new-year/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 17:15:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[コンピュータ]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=210</guid>
		<description><![CDATA[				かなり久しぶりに更新します。外国の友人から貰ったメールが変なので報告したいけど、別ブログにはあんまりそぐわないからこっちに。
				Good luck new year!
				
				Dear friends,how are you ?
				I found a website ( www.motonn.com),
				There are many products, laptop computers,mobile phones, digital
				cameras, DJ Equipment, etc.
				The most important is that the price is very cheap, and they delivery fast .
				I have received my product, quality is very good,
				I know you are interested in electronic products,
				Therefore write and tell [...]]]></description>
			<content:encoded><![CDATA[				<p>かなり久しぶりに更新します。外国の友人から貰ったメールが変なので報告したいけど、別ブログにはあんまりそぐわないからこっちに。</p>
				<h2>Good luck new year!</h2>
				<blockquote><p>
				Dear friends,how are you ?<br />
				I found a website ( www.motonn.com),<br />
				There are many products, laptop computers,mobile phones, digital<br />
				cameras, DJ Equipment, etc.<br />
				The most important is that the price is very cheap, and they delivery fast .<br />
				I have received my product, quality is very good,<br />
				I know you are interested in electronic products,<br />
				Therefore write and tell you,<br />
				You don&#8217;t miss this chance,<br />
				Hope this product  will bring good luck in 2010 for you,<br />
				Wish you good luck in the New Year, happy every day!
				</p></blockquote>
				<p>I&#8217;ve just received a greeting mail from my friend; however, it is a suspicious mail. The content of this mail is to advertise a website. Therefore, I come to the conclusion that it&#8217;s spam and my friend was infected with a computer virus.</p>
				<p>I googled a sentence of this mail but I didn&#8217;t find.</p>
				<p>友達から年賀メール（まだ12月30日だけど）を貰うのは有り難いのだけど、これはスパムですよね、どうみても。ということは、ウィルスにでも感染してスパムを送っているのかなんなのか知らないけど、たぶん本人が送ったものではないと思います。</p>
				<p>文面で検索をしてもひっかからないから、まだ未知のもののようです。2010年の年賀メールだから、最近出たのでしょうね。</p>
				<h2>ヘッダを読む</h2>
				<p>長いので全部は載せないけど</p>
				<blockquote><p>
				Return-Path: 	< ○○@gmail.com><br />
					X-Original-To: 	○○@○○.u-tokyo.ac.jp<br />
					Delivered-To: 	○○@○○.u-tokyo.ac.jp<br />
					Received:　　（略）<br />
				Received: 	by 10.91.138.5 with SMTP id q5mr8273279agn.103.1262103696252; Tue,  29 Dec 2009 08:21:36 -0800 (PST)
				</p></blockquote>
				<p>を見る限りだと、これはgmailからの送信ではないのかな。gmailのIPアドレス割当を知らないけど、hostしてもtracerouteしても怪しさ満点です。そもそも文面が友人が宣伝しているのとはずいぶん違う。</p>
				<p>皆様もウィルスにはお気をつけ下さい。</p>
				<h2>さっそくGoogleにクロールされた</h2>
				<p>仕事が早い。これでこの文面で検索した人は他にも事例があることに気づくはず。英語で書いておいた方がいいのかな。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2009/12/good-luck-new-year/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CFNetworkでHTTP POST</title>
		<link>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-post/</link>
		<comments>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-post/#comments</comments>
		<pubDate>Sun, 10 May 2009 19:59:40 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[プログラミング]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=208</guid>
		<description><![CDATA[				前回の続き。
				
CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://mixi.jp/login.pl"), NULL);

				mixiのログインをやってみる。
				
CFHTTPMessageRef theRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), theURL, kCFHTTPVersion1_1);

				POSTを指定してやる。
				
CFDataRef thePostData = CFStringCreateExternalRepresentation( NULL, CFSTR("next_url=%2Fhome.pl&#038;email=メアド%40ドメイン.org&#038;password=パスワード&#038;x=71&#038;y=14"), CFStringGetSystemEncoding(), '?');

				CFStringCreateExternalRepresentation()を使ってCFDataRefをCFStringRefから作る。使い回すわけでもないのでCFSTR()で作っちゃう。後ろのx=71とかy=14って何だろう？エンコーディングはCFStringGetSystemEncoding()でよい。最後の&#8217;?'は解釈できない文字を?で置き換えるという意味らしい。
				
CFHTTPMessageSetBody(theRequest, thePostData);

				POSTデータをくっつける。あとは前回と同じでPOSTできる。とりあえずmixiがcookieを返すので、あとは色々できそうだ。check.plがいきなりリダイレクトするので、その処理もやらないと。あと、ステータスコードも取得しないと。
				Rubyのリファレンスページにあるリダイレクトのやり方。
				
def fetch( uri_str, limit = 10 )
  # 適切な例外クラスに変えるべき
  raise ArgumentError, 'http redirect too deep' if limit == 0

  response = Net::HTTP.get_response(URI.parse(uri_str))
  case response
  when Net::HTTPSuccess     then [...]]]></description>
			<content:encoded><![CDATA[				<p>前回の続き。</p>
				<pre class="prettyprint">
CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://mixi.jp/login.pl"), NULL);
</pre>
				<p>mixiのログインをやってみる。</p>
				<pre class="prettyprint">
CFHTTPMessageRef theRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), theURL, kCFHTTPVersion1_1);
</pre>
				<p>POSTを指定してやる。</p>
				<pre class="prettyprint">
CFDataRef thePostData = CFStringCreateExternalRepresentation( NULL, CFSTR("next_url=%2Fhome.pl&#038;email=メアド%40ドメイン.org&#038;password=パスワード&#038;x=71&#038;y=14"), CFStringGetSystemEncoding(), '?');
</pre>
				<p>CFStringCreateExternalRepresentation()を使ってCFDataRefをCFStringRefから作る。使い回すわけでもないのでCFSTR()で作っちゃう。後ろのx=71とかy=14って何だろう？エンコーディングはCFStringGetSystemEncoding()でよい。最後の&#8217;?'は解釈できない文字を?で置き換えるという意味らしい。</p>
				<pre class="prettyprint">
CFHTTPMessageSetBody(theRequest, thePostData);
</pre>
				<p>POSTデータをくっつける。あとは前回と同じでPOSTできる。とりあえずmixiがcookieを返すので、あとは色々できそうだ。check.plがいきなりリダイレクトするので、その処理もやらないと。あと、ステータスコードも取得しないと。</p>
				<p>Rubyのリファレンスページにあるリダイレクトのやり方。</p>
				<pre class="prettyprint">
def fetch( uri_str, limit = 10 )
  # 適切な例外クラスに変えるべき
  raise ArgumentError, 'http redirect too deep' if limit == 0

  response = Net::HTTP.get_response(URI.parse(uri_str))
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then fetch(response['location'], limit - 1)
  else
    response.error!
  end
end

print fetch('http://www.ruby-lang.org')
</pre>
				<p>なるほど、上限を決めて再帰すればいいのね。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-post/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CFNetworkでHTTP GET</title>
		<link>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-get/</link>
		<comments>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-get/#comments</comments>
		<pubDate>Sat, 09 May 2009 11:42:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[プログラミング]]></category>

		<guid isPermaLink="false">http://blog.neoneet.jp/?p=200</guid>
		<description><![CDATA[				似たような記事はあちこちにあるけど、とりあえずまとめてみる。
				とりあえずコード。単なるテストなのでごちゃごちゃ。
				
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
	CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://mixi.jp"), NULL);
	CFHTTPMessageRef theRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), theURL, kCFHTTPVersion1_1);
	CFReadStreamRef theStream = CFReadStreamCreateForHTTPRequest(NULL, theRequest);
	CFReadStreamOpen(theStream);
	CFIndex n;

	UInt8 theBuffer[8192];
	CFMutableDataRef theCFData = CFDataCreateMutable( NULL, 0 );

	while(true)
	{
		n = CFReadStreamRead(theStream, theBuffer, sizeof(theBuffer));
		if( n == [...]]]></description>
			<content:encoded><![CDATA[				<p>似たような記事はあちこちにあるけど、とりあえずまとめてみる。</p>
				<p>とりあえずコード。単なるテストなのでごちゃごちゃ。</p>
				<pre class="prettyprint">
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
	CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://mixi.jp"), NULL);
	CFHTTPMessageRef theRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), theURL, kCFHTTPVersion1_1);
	CFReadStreamRef theStream = CFReadStreamCreateForHTTPRequest(NULL, theRequest);
	CFReadStreamOpen(theStream);
	CFIndex n;

	UInt8 theBuffer[8192];
	CFMutableDataRef theCFData = CFDataCreateMutable( NULL, 0 );

	while(true)
	{
		n = CFReadStreamRead(theStream, theBuffer, sizeof(theBuffer));
		if( n == 0 )
			break;

		CFDataAppendBytes( theCFData, theBuffer, n );
	}
	CFStringRef theContents = CFStringCreateFromExternalRepresentation( NULL, theCFData, kCFStringEncodingEUC_JP );
	CFRelease(theCFData);
//	CFShowStr(theContents);

//	CFIndex stringLength1 = CFStringGetMaximumSizeOfFileSystemRepresentation( theContents );
	CFIndex stringLength = CFStringGetMaximumSizeForEncoding( CFStringGetLength(theContents), kCFStringEncodingUTF8 );

	char *c = new char[stringLength];
	CFStringGetCString( theContents, c, stringLength, kCFStringEncodingUTF8 );
	puts(c);
	delete [] c;

	puts("----------------------");
	CFHTTPMessageRef responseHeader = (CFHTTPMessageRef)CFReadStreamCopyProperty(theStream, kCFStreamPropertyHTTPResponseHeader);
	CFDictionaryRef headers = CFHTTPMessageCopyAllHeaderFields(responseHeader);

	CFIndex count = CFDictionaryGetCount(headers);
	if( count > 0 )
	{
		CFStringRef *keys = new CFStringRef[count];
		CFStringRef *values = new CFStringRef[count];

		CFDictionaryGetKeysAndValues(headers, (const void **)keys, (const void **)values);

		for( int i = 0; i < count; i++ )
		{
			CFShow( keys[i] );
			CFShow( values[i] );
			puts("");
		}

		delete [] keys;
		delete [] values;
	}
	CFRelease(headers);

	puts("----------------------");

	CFRelease(theRequest);
	CFRelease(theURL);

	[pool drain];
    return 0;
}
</pre>
				<h2>解説</h2>
				</pre>
				<pre class="prettyprint">
CFURLRef theURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://mixi.jp"), NULL);
</pre>
				<p>まずCFURLRefを作る。CFなんとかRefというのは<b>実はポインタ型</b>なので、*はつけなくていい。</p>
				<p>CFSTR()は普通のC文字列（charの配列）から簡易CFStringRef型の文字列を作る。確か、内部で使い回しの領域に文字列オブジェクトを置くので、<b>CFRelease()しなくてよい</b>ので使いやすい。その代わり、いつまでも内容が保証されるわけじゃないので、今回のようにすぐに渡してそれっきりにするのがよい。</p>
				<p>CFURLCreateWithString()でCFURLRefインスタンスを作る。</p>
				<p>一般にCFなんとかを生成する関数（コンストラクタ？）はアロケータを指定するけど、<b>大抵はkCFAllocatorDefaultとかNULLを渡しておけばよいようだ</b>。</p>
				<pre class="prettyprint">
CFHTTPMessageRef theRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), theURL, kCFHTTPVersion1_1);
</pre>
				<p>CFHTTPMessageRefのインスタンスを作る。GETリクエストをするわけだけど、使い回すわけでもないのでCFSTR()を使う。あとはさっき作ったURLインスタンスを渡す。HTTPのバージョンは1.1でいいのでこれも決めうちで。アロケータもデフォルト。</p>
				<pre class="prettyprint">
CFReadStreamRef theStream = CFReadStreamCreateForHTTPRequest(NULL, theRequest);
CFReadStreamOpen(theStream);
</pre>
				<p>リクエストからストリームを作る。特に指定する物はないので簡単。ついでにストリームを開いちゃう。一緒にやってくれたらいいのに・・・ストリームを作って開かないってあるのかな。</p>
				<h3>ストリームから読み込む</h3>
				<pre class="prettyprint">
	CFIndex n;
	UInt8 theBuffer[8192];
	CFMutableDataRef theCFData = CFDataCreateMutable( NULL, 0 );

	while(true)
	{
		n = CFReadStreamRead(theStream, theBuffer, sizeof(theBuffer));
		if( n == 0 )
			break;

		CFDataAppendBytes( theCFData, theBuffer, n );
	}
</pre>
				<p>CFIndexはintらしい（調べてない）。UInt8はunsigned charで、バッファ用に適当な長さの配列を作っておく。最終的にCFStringにしたいので、CFDataに放り込む。可変長のものはCFMutableDataRef。一般にMutableが付いているのは中身を変更できる。</p>
				<p>CFReadStreamRead()でストリームから読み込む。ネットワークはしばしばサイズ上限まで読まないことがあるのでnに読んだバイト数を記録する。Cの文字列のように&#8217;\0&#8242;終端じゃないから、長さを覚えておいてやる必要がある。読み込みサイズが0バイトなら終わり。そうでなければ、CFDataAppendBytes()で後ろにくっつける。</p>
				<h3>CFStringを作る</h3>
				<pre class="prettyprint">
CFStringRef theContents = CFStringCreateFromExternalRepresentation( NULL, theCFData, kCFStringEncodingEUC_JP );
CFRelease(theCFData);
</pre>
				<p>CFStringをCFDataから作る。その際にkCFStringEncodingEUC_JPを指定している。CFDataは単なるバイト列なのでエンコーディング情報がない（往々にしてアルファベット文化圏はその辺に疎いけど、CFStringの設計者は理解しているようだ）ので指定する。</p>
				<p>CFDataはもう要らないので解放する。解放のタイミングは不要になったらすぐに解放するのか、しっぽでまとめて解放する方が見やすいのか。</p>
				<pre class="prettyprint">
//	CFShowStr(theContents);
</pre>
				<p>CFShowStr()は文字列情報を出力するようなので不適。</p>
				<pre class="prettyprint">
//	CFIndex stringLength1 = CFStringGetMaximumSizeOfFileSystemRepresentation( theContents );
	CFIndex stringLength = CFStringGetMaximumSizeForEncoding( CFStringGetLength(theContents), kCFStringEncodingUTF8 );

	char *c = new char[stringLength];
	CFStringGetCString( theContents, c, stringLength, kCFStringEncodingUTF8 );
	puts(c);
	delete [] c;
</pre>
				<p>仕方がないのでC文字列を作る。</p>
				<p>CFStringは「文字数」で表すようだが、char配列はバイト数である。よく「2バイト文字」と言うが、1文字=1バイトあるいは2バイトと決め打ちするのはよくない。Unicodeはたしか1文字を21ビットで表すが、これはあまり使いやすくないので適当にエンコードして使う。UTF-8は基本8ビット（＝１バイト）だが、収まらないときは２バイトとか３バイトになるようだ。日本語は一般に３バイトになるが、重箱の隅をつつくと１文字が１５バイトになる（株式会社って文字とか）ものもあるそうだ。</p>
				<p>そういうわけで、文字数とエンコーディングを指定してCFStringGetMaximumSizeForEncoding()すると必要なバイト数を返す。CFStringGetMaximumSizeOfFileSystemRepresentation()は大きすぎるようだ。</p>
				<p>必要なバイト数を得てから、char配列を作りCFStringGetCString()で流し込む。puts()して終わったら忘れないうちに解放。</p>
				<h3>HTTPレスポンスヘッダを得る</h3>
				<pre class="prettyprint">
	CFHTTPMessageRef responseHeader = (CFHTTPMessageRef)CFReadStreamCopyProperty(theStream, kCFStreamPropertyHTTPResponseHeader);
</pre>
				<p>どういうわけか、<b>HTTPの本文を読んでからじゃないとNULLが返る</b>ようなので、終わってから呼ぶ。CFReadStreamCopyProperty()は色々できるようだが、今回はkCFStreamPropertyHTTPResponseHeaderを指定する。</p>
				<pre class="prettyprint">
	CFDictionaryRef headers = CFHTTPMessageCopyAllHeaderFields(responseHeader);
</pre>
				<p>CFDictionaryを得る。CFDictionaryは連想配列で、キーに対して値を問い合わせるコンテナ。C++のSTLだとstd::mapのようなもの。</p>
				<pre class="prettyprint">
	CFIndex count = CFDictionaryGetCount(headers);
	if( count > 0 )
	{
		（略）
	}
	CFRelease(headers);
</pre>
				<p>連想配列にいくつキーと値のペアがあるか調べる。0より大きければ次のコードを実行する。終わったらheaderを解放する。</p>
				<pre class="prettyprint">
	CFStringRef *keys = new CFStringRef[count];
	CFStringRef *values = new CFStringRef[count];

	CFDictionaryGetKeysAndValues(headers, (const void **)keys, (const void **)values);
</pre>
				<p>CFDictionaryGetKeysAndValues()でキーと値のペアを取得。CFStringRefの配列へのポインタを渡してやると、CFStringRefの配列が返される。</p>
				<pre class="prettyprint">
	for( int i = 0; i < count; i++ )
	{
		CFShow( keys[i] );
		CFShow( values[i] );
		puts("");
	}

	delete [] keys;
	delete [] values;
</pre>
				<p>全てのペアを表示すればヘッダの内容がわかる。</pre>
				<h2>BSD Socketがいいか、CFNetworkがいいか</h2>
				<p>正直なところ、こんなに面倒くさいことをするならBSD Socketを直接叩いた方がいいような気がする。URLのパース、DNSの問い合わせ、何かと古くさいSocketを直接叩くのは結構ダサいけど、面倒は少ない。必要ならBSD Socketをラップしちゃえばいいのだからね。</p>
				<p>あとは、ヘッダもSocketから読むとゴチャゴチャ読むので、パースしてやる必要がある。これもそうたいした手間ではないし、CFなんとかを使っても面倒くさいから、どっちがいいかはよくわからない。</p>
				<p>さらにPUTしてやろうとすると、SetBodyなんとかを使ったりするんだろう。Socketならwriteでがりがり書いてやるだけである。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.neoneet.jp/2009/05/cfnetwork%e3%81%a7http-get/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
