上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--/--|スポンサー広告||TOP↑
http://d.hatena.ne.jp/shimobayashi/20090718/1247894330
上記ページのコードを元に自分に必要な変更を加えてみました。
shimobayashiさんに感謝
  • Ruby 1.9系対応(1.9.2動作確認。1.8系は使ってないけど、動かないはず)
  • ソースはUTF-8前提
  • 出来るだけアクセスが少なくなるようにしてみた。(初期化を遅延させてます)
  • 漫画にも対応。
  • イラストと漫画での統一されたインターフェース。(イラストにeach入れただけだけど)
  • ファイル保存時に便利な名前を返すfilenameメソッド付き。
  • 大量データアクセス時にメモリ不足になるのを防ぐため、画像データはライブラリで保持しません。
  • キャメルケースなメソッドをアンダースコア区切りに変更しました。
  • Mechanizeの便利なメソッドを使い切れてない気がします。
  • ソースコードを綺麗に貼り付けたいです(pre, codeタグ付けてもなんか勝手に改行消えるし)
下記のコードはライブラリ部のみです。実用スクリプトは自分で組んでみてください。
require 'rubygems'
require 'mechanize'
require 'uri'
class Mechanize
  class Page
    def utf8
      b = body
      b.force_encoding("UTF-8") if b
      b
    end
  end
end
class Object
  def self.lazy_attr_reader(bind, *names)
    names.each do |name|
      define_method name do
        send bind
        instance_variable_get :"@#{name}"
      end
    end
  end
end
class Pixiv
  attr_reader :agent, :bookmark_new_illust
  def initialize(pixiv_id, pass)
    @agent = Mechanize.new
    @agent.max_history = 1
    login(pixiv_id, pass)
    @bookmark_new_illust = BookmarkNewIllust.new(self)
  end
  class LoginFailedError < StandardError; end
  def login(pixiv_id, pass)
    form = get('http://www.pixiv.net/index.php').forms.first
    form.pixiv_id = pixiv_id
    form.pass = pass
    raise LoginFailedError unless @agent.submit(form).utf8 =~ /ログアウト/
  end
  
  def get(url, options={})
    wait_time = options[:sleep] || 1
    puts "get: #{url}, options:#{options}"
    sleep wait_time if wait_time
    @agent.get(url, options.fetch(:query, []), options[:refer])
  end
  
  def member_illust_list(id)
    MemberIllustList.new self, id
  end
  def search(word, s_mode)
    Search.new(self, word, s_mode)
  end
  def search_by_tag(word)
    search(word, 's_tag')
  end
  def search_by_title_and_caption(word)
    search(word, 's_tc')
  end
  
  def member_illust(id)
    MemberIllust.new(self, id)
  end
  class MemberIllust
    attr_reader :id, :url, :pixiv
    lazy_attr_reader :init_page, :title, :artist, :artist_id, :type, :illust, :manga
    def initialize(pixiv, id)
      @pixiv = pixiv
      @id = id.to_i
      @url = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=#{id}"
      @init_page = false
      @illust = nil
      @manga = nil
    end
    def medium
      @illust.medium if illust?
    end
    def big
      @illust.big if illust?
    end
    
    def each(&b)
      if illust?
        b.call(illust)
      else
        manga.each(&b)
      end
    end
    
    def manga?
      @manga ? true : false
    end
    
    def illust?
      @illust ? true : false
    end
    
  private
    
    lazy_attr_reader :init_page, :manga_urls, :manga_page_url
    
    def init_page
      unless @init_page
        page = @pixiv.get(@url)
        # Get Title and Artist
        page.title =~ /\A「(.+)」\/「(.+)」の(イラスト|漫画) \[pixiv\]\z/
        @title, @artist, @type = $1, $2, $3
        page.utf8 =~ %r[<a href="/member.php\?id=(\d+)" class="avatar_m" [^>]*>]
        @artist_id = $1
        if @type == "イラスト"
          # Get Medium Size URL
          @illust = Illust.new(self, page)
        else
          # Manga
          @manga = Manga.new(self, page)
        end
        @init_page = true
      end
    end
    
    class Picture
      def self.delegate(*names)
        names.each do |name|
          define_method name do |*args, &b|
            @member_illust.send(name, *args, &b)
          end
        end
      end
      
      def initialize(member_illust)
        @member_illust = member_illust
      end
      
      delegate :pixiv, :id, :title, :artist, :artist_id
    end
    
    class Illust < Picture
      def initialize(member_illust, page)
        super(member_illust)
        page.utf8 =~ /"(http:\/\/.+\.pixiv\.net\/img\/.+\/\d+_m(\..{3})(?:\?\d+)?)"/
        @medium_url = $1
        @ext = $2
        
        @big_page_url = "http://www.pixiv.net/member_illust.php?mode=big&illust_id=#{id}"
        @init_big = false
      end
      
      attr_reader :medium_url, :ext, :big_page_url
      lazy_attr_reader :init_big, :url, :big_url
      
      def data
        big
      end
      
      def medium
        pixiv.get(medium_url, refer: @member_illust.url).body
      end
      
      def big
        pixiv.get(big_url, refer: big_page_url, sleep: nil).body
      end
      
      def filename
        "#{id}_#{title}#{ext}"
      end
      
    private
      
      def init_big
        unless @init_big
          # Get Big Size URL
          bigpage = pixiv.get(big_page_url, refer: @member_illust.url)
          bigpage.utf8 =~ %r[<img src="(http://img\d+\.pixiv\.net/img/[^/]+/\d+\..{3}(?:\?\d+)?)" border="0">]
          @url = @big_url = $1
          @init_big = true
        end
      end
    end
    
    class Manga
      include Enumerable
      
      NUMBER_OF_PAGE_PER_SCREEN = 50
      def initialize(member_illust, page)
        @member_illust = member_illust
        @id = member_illust.id
        @url = "http://www.pixiv.net/member_illust.php?mode=manga&illust_id=#{id}&type=scroll"
        @pages = nil
      end
      
      def pixiv
        @member_illust.pixiv
      end
      
      attr_reader :id, :member_illust, :illust_url, :url
      
      def init_manga
        unless @pages
          @pages = []
          loop do
            i = 0
            if @pages.empty?
              manga_page = pixiv.get(scroll_url(@pages.size), refer: member_illust.url)
            else
              manga_page = pixiv.get(scroll_url(@pages.size), refer: scroll_url(@pages.size - 1))
            end
            manga_page.utf8.scan(%r[<a href=".*"><img src="(http://img\d+\.pixiv\.net/img/[^/]+/\d+_p(\d+)(\..{3})(?:\?\d+)?)"></a>]) do |m|
              @pages << Page.new(self, m[1].to_i, m[0], m[2])
              i += 1
            end
            break unless i == NUMBER_OF_PAGE_PER_SCREEN
          end
        end
      end
      
      def scroll_url(idx)
        scroll_page = (idx / NUMBER_OF_PAGE_PER_SCREEN) + 1
        "#{url}&p=#{scroll_page}"
      end
      
      def [](idx)
        init_manga
        @pages[idx]
      end
      
      def each(&b)
        init_manga
        @pages.each(&b)
      end
      
      class Page < Picture
        def initialize(manga, index, url, ext)
          super(manga.member_illust)
          @manga = manga
          @index = index
          @url = url
          @ext = ext
        end
        
        attr_reader :index, :url, :ext
        
        def data
          pixiv.get(url, refer: @manga.scroll_url(index), sleep: nil).body
        end
        
        def filename
          "#{id}_#{title}_p#{index}#{ext}"
        end
      end
    end
  end
  class MemberIllustListBase
    include Enumerable
    
    NUMBER_OF_ILLUST_PER_PAGE = 20
    def initialize(pixiv)
      @pixiv = pixiv
      @member_illusts = []
      @reach_last = false
    end
    
    attr_reader :pixiv
    def [](idx)
      if not @reach_last and idx >= @member_illusts.size
        start_p = @member_illusts.size / NUMBER_OF_ILLUST_PER_PAGE + 1
        goal_p = idx / NUMBER_OF_ILLUST_PER_PAGE + 1
        for pn in start_p..goal_p
          url = generate_url(pn)
          page = pixiv.get(url)
          i = 0
          page.links.each do |link|
            if link.href =~ /member_illust\.php\?mode=medium&illust_id=(\d+)/
              member_illust = MemberIllust.new(pixiv, $1)
              @member_illusts << member_illust
              i += 1
            end
          end
          unless i == NUMBER_OF_ILLUST_PER_PAGE
            @reach_last = true
            break
          end
        end
      end
      return @member_illusts[idx]
    end
    def each(&b)
      if b
        i = 0
        while c = self[i]
          b.(c)
          i += 1
        end
        self
      else
        enum_for :each
      end
    end
  end
  class BookmarkNewIllust < MemberIllustListBase
    def generate_url(pn)
      "http://www.pixiv.net/bookmark_new_illust.php?mode=new&p=#{pn}"
    end
  end
  
  class MemberIllustList < MemberIllustListBase
    def initialize(pixiv, member_id)
      super(pixiv)
      @member_id = member_id
    end
    
    attr_reader :member_id
    def generate_url(pn)
      "http://www.pixiv.net/member_illust.php?id=#{member_id}&p=#{pn}"
    end
  end
  
  class Search < MemberIllustListBase
    def initialize(pixiv, word, s_mode)
      super(pixiv)
      @word = word
      @s_mode = s_mode
    end
    def generate_url(pn)
      "http://www.pixiv.net/search.php?word=#{URI.encode_www_form_component(@word)}&s_mode=#{@s_mode}&p=#{pn}"
    end
  end
end

スポンサーサイト
01/30|プログラムコメント(0)トラックバック(0)TOP↑
最近Railsを触っているが、とりあえず環境への配置は、アプリが動き始めたらすぐやっておいたほうがいいと、RailsによるアジャイルWebアプリケーション開発に書かれていたので、練習用サーバーを立てることにした。
LAN内からしか見えないので、本来不要なものもあるが、練習のためいろいろやってみる。

余っていたPC1号機(C2D E8400 / DDR2-1066 4GB / HDD 500GB)に、光学ドライブを新規購入して装着して利用。
まずは、サクラでデフォルトになってるのがCentOSなので、CentOS 5.5 x86_64をインストールする。
インストーラーでとりあえず使いそうなもの、Apache、mysql、開発ツール等は全部入れる。
さらにGUIのパッケージマネージャーでRubyも入れるが、バージョンが1.8.5だったので1.9.2をソースからビルドしてインストール。ああ、Unix互換システムは素晴らしい。
# gem install rails
したら、Passengerを導入する。ここはインストーラーの指示通りでよし。

適当にインストールしたら、SSHを準備する。PC3号機(Windows7)にTeraTermをインストールして、鍵を生成。公開鍵をサーバー1(WHS)経由で渡して、authenticate_keysに加える。
SSHでの接続に成功。FileZillaもインストール設定してSFTP経由でファイルを移動できるようにする。

サンプルのRailsアプリケーションをサーバーにアップロードし、Apacheへ設定開始。
しかし、VirtualHostを使うためには、ドメイン名が必要という当然のことに今更気がつく。
(実際はアプリが1つならなくても良かったが)

しょうが無いので、DNSを設定する。まずはBINDをインストールし、とりあえずLAN内向けに設定。
設定に関する情報がいろいろ出てきて、どうしたらいいかわからない。
それっぽい設定ができて稼働を確認したが、PC3から見たときに応答なし。
調査の結果、ファイアウォールでポート開けてませんでしたという、間抜けなオチ。
とりあえず開けて動くのを確認。

しかし、Apacheが動いていない。いろいろ探しまわった結果、SELinuxが原因と判明。
「まあいいか」と、とりあえずオフにする。

ここでやっとRailsのデフォルトページが表示される。
実際にアプリケーションのページを見ると、DB設定前なのでそのへんのエラー発生。

次に、mysqlのDBを作り、config/database.ymlを設定して
$ rake RAILS_ENV=production db:migrate
をしてみるがうまくいかない。
メッセージを読むとどうやら、sqliteがないと駄目らしい。
# yum install sqlite
しかし、エラー発生。
# yum install sqlite-devel
やってみて、
$ gem install sqlite3-ruby
してみるが、途中でエラー。
メッセージによるとsqliteが古いとのこと。
仕方ないので、これもsqliteをビルドしてインストール。
今度は成功する。

次はmysqlのライブラリが無いと怒られたので、mysqlライブラリを入れる。
しかし、ビルドできずエラー。
# yum install mysql-devel
で解決。

しかし、再びmysqlが見つからないと怒られる。
いつの間にかbundleして居たようだが、mysqlが入ってなかったので、bundleしなおす。

無事、Railsアプリが表示されて終わり。
10/24|環境構築コメント(0)トラックバック(0)TOP↑
Windows 7で使用感をまとめよう。
主にVistaからの乗り換えという視点で見る。

まず、OSの基本的な機能はわかりきっていたとはいえ、Vistaの改良という印象が強い。
これは、VistaがXPから多くの部分を一新したのとは異なる。
とはいえ、それでもいろいろと変化は多いようだ。

操作感の向上に焦点が当てられているようで、ウィンドウ機能、UAC、エクスプローラーなどに変更が見られる。

ウィンドウの左右半分表示はとても便利だ。他のウィンドウ関係はそれほどでもないが。

UACもコントロールパネルなどでばしばしダイアログを出していたのも改善された。(管理者権限が必要なことを示すマークの有るボタンを押したときは、勝手に認証されるようになった)

だが、納得出きない箇所もある。まず、ガジェット周り。

サイドバーが廃止された。サイドバーは比較的大型のワイドディスプレイでは固定表示することで、ウィンドウの最大化でも表示できたのだが、これが廃止された。
ウィンドウの左右半分表示でじゃまになると踏んで廃止したのだろうか・・・。
また、サイドバー廃止により、ガジェットはデスクトップに貼り付ける形に変更された。
これがとてもまずく、Vistaではできた、他のWindow上にかぶせて表示するということが出きないのだ。
ガジェットを表示するためには、他のウィンドウを最小化するしかないが、最大化したウィンドウの内容を確認しながらガジェットを操作するためには、最大化を解除しなくてはならない。
つまり、ウィンドウの最大化とガジェットの利用で相性が悪くなってしまった。

かつてはガジェット呼び出しだった、Win+Spaceはただの全ウィンドウ透明化、つまりデスクトッププレビューとなり、ガジェットの最前面表示機能ではなくなってしまったのだ。

と、ここでガジェットの呼び出しにWin+Gが使えることに気づいた。
でも、マウス操作で呼び出す方法はなくなってるんだが、GUIとしてどうなんだろう。
01/17|コンピュータコメント(0)トラックバック(0)TOP↑
まぁ、既知の問題。それもUSBオーディオデバイス全般に言えるらしいが、「プツ」というノイズが乗る。

原因はUSBの通信品質によるものらしく、PC側のコントローラーまで絡むややこしい問題らしい。
ハブを挟んだり、帯域の大きいものを同一コントローラー上につなぐだけで通信が不安定になり、ノイズがのるというもの。

デジタルならノイズが「無条件で絶対に載らない」と思っている人がいるようだがこれは誤りである。
特にUSBはFullSpeed/HighSpeedで帯域保証がなくリアルタイムで再生するとバッファ切れによるノイズ(上記のプツ)が発生し、S/PDIFは一方通行でエラー訂正は元から信号に含まれている分のみだから、不完全になる可能性がある。
いずれにせよ、デジタルのデータ保証ができるのは、送信・検証・再送の仕組みを持つからであり、このサイクルを持たない場合は劣化が起き得る。
なお、一部には単純なデータコピー(TCP/IPでの転送など)でも劣化が起きるとか思い込んでる人がいるが、上記のサイクルを持つ限りは完全性が保証できるので問題はない。最近は早々データが壊れないのでMD5で確認する必要も低い。(普通は転送プロトコルで保証されるので、どこかにバグがない限り手動で見る必要はない)

前置きが長くなったが、ノイズの問題。USBオーディオデバイスではFullSpeedの帯域をほぼ使い切る。こういうことをすると大抵は通信品質の低下に対する問題が表面化しやすい。しかも、リアルタイム性が重視され、大容量のバッファリングが難しいオーディオデバイスでは致命傷になりやすい。
実際、私のPCはマザーボード上にPCI-E x2接続のUSB 3.0(5.0Gbps対応)コントローラーが別途搭載されており、こちらに単独搭載してもノイズが解消しなかった。もう、USBの通信品質が環境により低いとかいう問題ではない。

なお、プロユースの製品ではFireWireが一般的らしい。私のPCにも積んであるので、ぜひ3万~のクラスでFireWire接続の製品が欲しいのだが。

ところで、USBが原因だと切り分けできた理由が気になることだろう。
これは簡単な話で、S/PDIFで接続してみただけである。この状態だとノイズは消え、実に快適である。理屈上S/PDIFではUSBより劣化しうるが(例のサイクルを踏まない)、リアルタイム性でみればUSBより優秀であるため(最初から誤り訂正用のデータを載せている)、視聴上致命的な「プツ」という突発的なノイズが消える。幸い、マザーボードは24bit/192kHzステレオに対応しているので、規格上も劣化なしとなる。
一応、ジッターノイズなどが問題視されてはいるが、より致命的な問題がUSBにある以上、現状ではS/PDIF接続しかないだろう。

なお、SE-U55SXはPCとの接続にはUSBを想定しており、ボイスチャットなどではUSB接続以外の選択肢がない。
ヘッドフォンアンプがわりになるDACを挟んだ状態での運用を簡易化(PC側でデバイスを切り替えないで済ます)つもりで買ったのに、ずいぶん複雑になってしまった。
やはり、SE-U55SXはただの録音装置か、ボイスチャット装置になってもらうか。
01/11|コンピュータコメント(0)トラックバック(0)TOP↑
PCを構築したので紹介する。

CPU: Intel Core i7-860 / 2.8GHz
Memory: DDR3-1333 PC3-10600 2GBx4 / 8GB
M/B: ASUS P7P55D-E Premium
HDD: SEAGATE ST31000528AS / 7200rpm 1TB
CPU Cooler: Thermalright TRue Black 120 Rev.C / Scythe KAZE-JYUNI PWM
G/B: LeadTek WinFast GTX285 / Nvidia GeForce GTX285 / GDDR3 1024MB
Enclosure: Antec P183
Power Supply: Antec CP850 / 850W
OS: Windows 7 Home Premium 64bit Package
Optical Drive: DVD Super Multi

まぁ、こんなもん。
素人には全くおすすめ出きない変な仕様になっている。

今回は以下の要求があった。

メモリ4GB超(4GBではない)であること。
Windows 7 Home Premium 64bitを使用すること。
Core i7であること。
NvidiaのシングルGPUで出来る限り性能が高いこと。
出来る限り静音性を高めつつ、オーバークロック、拡張性をもつこと。

で、上記のようなパーツになった。
静音性とOCを考えると当然CPUクーラーは別途用意。
ケースファンは十分に静かでなかったため、換装(XFAN)。
ついでに、GPUが冷えないので前面吸気ファンも装着(Scythe KAMA-JYUNI 1200rpm)。
電源は余裕をもってCP850。P183と組み合わせた場合に都合がいいというのもある。
グラフィックボードは奮発してGTX285搭載ボードを購入。
CPUはOC時に難があるも、900シリーズよりTDPが低く、定格クロックも高めなCore i7-860を選択。
メインメモリは当初2枚・4GBも考えたが、3枚セットか4枚セットしか在庫がなかったため、奮発して4枚セット。

なお、8GBの使い道が無いと思う方も多いとは思うが、近代的なOSにはファイルキャッシュというものがあり、アプリケーションが使用しない領域に、最近アクセスしたファイルをキャッシュしている。現代のWindowsも当然搭載しており、余っている分もちゃんと有効活用する。
だいたい、2GB程度はOSと常駐プロセスで使いきって、32ビットプログラムの限界値である2GBと合わせると4GBであり、別プログラムやファイルキャッシュを考えると6~8GBが適正値だろう。
もっとも、同一ファイルに複数のアクセスがあるのは、32ビット空間で足りないようなデータを扱うアプリケーション(大抵は64ビット版を用意するだろうが、ゲームとかは32ビット版のみ)か、複数のプロセスで同一ファイルを扱うようなアプリケーション(Webサーバーとか)か、ファイルアクセス時間を無視しているアプリケーション(つまり、同じファイルをなんども開き直す、富豪的プログラミングなやつ)くらいで、普通は必要ないと思うが。

Windows 7はVistaの時と同様、パッケージ版を購入。DSP版の方が一般的だとは思う。しかし、ライセンス上はセット購入したパーツを組み込んでいないとならない。つまり、パーツの交換が自由にできなくなる。それは嫌なのでパッケージ版とした。

こうして、定格使用時に十分なパフォーマンスと、静音性を確保。静音性を犠牲にすれば、OCによるさらなる高速化も可能、そしてSSDをSATA 6GbpsのRAID0で装備可能と、夢が詰まったマシンが完成した。
なお、このマシンのパフォーマンスが最大限に発揮されるのは、今月発売予定の「A列車で行こう9」になるだろう。
なんと推奨スペックが
Windows 7 / Core i7-860 / グラフィックスメモリ1GB
という、かなりぶっ飛んだ設定。
まぁ、見ての通りこれらをすべて満たしている私のマシンにとってはなんの問題もないが。

なお、光学ドライブがDVD Super Multiだが、頃合を見て先代のドライブと交換予定だ。
01/02|コンピュータコメント(0)トラックバック(0)TOP↑
プロフィール

Author:G.U.Nex
職業:プログラマ
趣味:ゲーム(PC、コンシューマ)、ネットサーフィン、ニコニコ動画視聴、プログラミング、鉄道全般
PHP, C, C++, VB(系), Java, JavaScriptを使える。
最近はRubyにはまってる。

最近の記事
最近のコメント
最近のトラックバック
月別アーカイブ
カテゴリー
ブロとも申請フォーム
ブログ内検索
RSSフィード
リンク
フリーエリア
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。