2009-02-19
■[Ruby]2ちゃんねるBOTの作り方 実装編1
必要な機能を思いだそう
最初にAPIを決めよう
最初にAPIを決めるのはテスト駆動開発を進めたり、美しいコードを書く上で有利です。まずはスケルトンコードっぽく実装
module Bot2ch class Menu def get_board(subdir) end end class Board def get_threads end end class Thread def get_images end end class NormalImageDownloader def download end end class App def execute(subdir) menu = Menu.new board = menu.get_board(subdir) threads = board.get_threads threads.each do |thread| images = thread.get_images images.each do |image| image.download end end end end end Bot2ch::App.new.execute('news4vip')
1.掲示板一覧からニュー速VIPのURLを取得
掲示板のホストは頻繁に変更されるので、最も変更が少ないであろうサブディレクトリからURLを取得しよう。ニュー速VIPのサブディレクトリはnews4vipです。
require 'open-uri' class Menu def initialize @bbsmenu = 'http://menu.2ch.net/bbsmenu.html' end def get_board(subdir) reg = Regexp.new("href=(.+#{subdir})", Regexp::IGNORECASE) open(@bbsmenu) do |f| f.each do |line| return Board.new($1) if line =~ reg end end end end
2.ニュー速VIPの全てのスレッド情報を取得
掲示板URL/subject.txtからスレッド情報を取得しよう。「DATファイル名<>タイトル」となってます。
※2chのデータは全てSJISなので環境に合わせて変換しましょう。
class Board def initialize(url) @url = url @subject = "#{url}/subject.txt" end def get_threads threads = [] open(@subject) do |f| lines = f.read.toutf8 lines.each do |line| dat, title = line.split('<>') threads << Thread.new("#{@url}/dat/#{dat}", title) end end threads end end
3.全てのスレッドから全ての画像URLを取得
次にDATから画像のURLを取得します。DATは<>で要素を区切られており、
4つめの内容が実際の書き込み内容になっているので、そこから画像URLを探せば良いのであります。
また、色々なアップローダーに対応できるような仕組みにしましょう。
URLらしきもの全てに対して、対応するダウンローダークラスでダウンロードする感じです。
class Thread attr_accessor :title def initialize(url, title) @dat = url @title = title.strip end def get_images images = [] downloaders = [NormalImageDownloader] open(@dat) do |f| lines = f.read.toutf8 lines.each do |line| contents = line.split('<>')[3] while contents =~ /\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/i url = "http:#{$&}" contents = $' image_downloader = downloaders.find { |d| d.match(url) } next unless image_downloader images << image_downloader.new(url) end end end images end def dat_no File.basename(@dat, '.dat') end end
4.全ての画像をダウンロード
とりあえず通常画像のダウンローダーを実装しましょう。URLの拡張子がjpgならばダウンロードできるクラスです。
class NormalImageDownloader def initialize(url) @url = url end def download(saveTo) puts "download: #{@url}" open(saveTo, 'wb') do |f| open(@url) do |img| f.write img.read end end end def self.match(url) url =~ /.jpg$/i end end
つじつまあわせ
一通り実装できたのでAPPクラスの辻褄合わせをします。
class App def execute(board) image_root_dir = File.dirname(__FILE__) + '/images' menu = Menu.new board = menu.get_board(board) threads = board.get_threads puts "total: #{threads.length} threads" threads.each do |thread| images = thread.get_images next if images.empty? parent_dir = "#{image_root_dir}/#{thread.dat_no}" Dir.mkdir(parent_dir) unless File.exists?(parent_dir) puts "#{thread.title}: #{images.length} pics" images.each_with_index do |image, index| image.download("#{parent_dir}/#{index}.jpg") rescue next sleep(0.2) end end end end
以上のように、前回決めたディレクトリ構成に沿ってスレッド毎連番で画像を保存するように変更しました。
実行
ruby bot2ch.rb
ここまで書いて気づいたのですが、5.の実装を忘れてた。
起動毎に同じ画像がダウンロードされてしまうので対策しなければいけません。
疲れたので、明日にでも。
ちなみに完走させてません。実行すると鬼のようにダウンロードしてくるはずです。
実装編1おしまい!
トラックバック - http://d.hatena.ne.jp/gioext/20090219/1234976693
リンク元
- 4 http://twitturls.com/
- 3 http://pic2ch.giox.org/
- 2 http://pic2ch.giox.org/thread/5183
- 2 http://www.google.co.jp/search?hl=ja&q=2ch画像まとめ&meta=lr=&aq=f
- 1 http://b.hatena.ne.jp/entry/http://pic2ch.giox.org/
- 1 http://b.hatena.ne.jp/keyword/ruby?sort=eid
- 1 http://blog-search.yahoo.co.jp/search?ei=UTF-8&p=2ちゃんねる&n=10&so=dd&merge=on&tflg=none&sq=M&b=2
- 1 http://blog-search.yahoo.co.jp/search?so=dd&merge=&p=twitter
- 1 http://d.hatena.ne.jp/
- 1 http://d.hatena.ne.jp/diarylist?of=100&mode=rss&type=public
おとなり日記
- 2009-02-15 hijouguchiの日記 4/27 14%
- 2009-02-16 不動産屋のラノベ読み 4/51 7%
- 2009-02-16 主に言語とシステム開発に関して 4/61 6%
- 2009-02-18 ihirokyの日記 4/75 5%