2012/03/23


そう言えば、go言語版を書いてなかったなーと思ったのでエントリ。
いかにしておっぱい画像をダウンロードするか?2012 - ゆーすけべー日記

4年以上前のBlog記事で非常に評判がよく「高校生がプログラミングをはじめるキッカケになった」というエントリーがあります。 題名は「 いかにして効率よく大量のおっぱい画像をダウンロードするか 」。 僕...

http://yusukebe.com/archives/20120229/072808.html
こういう並行処理を簡単に書けてネイティブ実行出来るgo言語はやっぱり素晴らしいなぁと思いました。
# oppai [appid] [outdir] [keyword]
といった感じにお使い下さい。 package main

import (
    "crypto/md5"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "path/filepath"
    "strconv"
    "strings"
)

type response struct {
    SearchResponse struct {
        Image struct {
            Results []struct {
                MediaUrl    string
                ContentType string
            }
        }
    }
}

func main() {
    if len(os.Args) != 4 {
        println("usage: oppai [appid] [outdir] [keyword]")
        os.Exit(1)
    }
    appid, outdir, keyword := os.Args[1], os.Args[2], os.Args[3]

    total := 0
    offset := 0
    outdir, _ = filepath.Abs(outdir)
    param := url.Values{
        "AppId":       {appid},
        "Version":     {"2.2"},
        "Market":      {"ja-JP"},
        "Sources":     {"Image"},
        "Image.Count": {strconv.Itoa(50)},
        "Adult":       {"off"},
        "Query":       {keyword},
    }
    quit := make(chan bool)

    md5hash := md5.New()
    to_filename := func(s, t stringstring {
        md5hash.Reset()
        md5hash.Write([]byte(s))
        token := strings.SplitN(t, "/"2)
        if strings.Index(token[1], "jpeg") != -1 {
            token[1] = "jpg"
        }
        return fmt.Sprintf("%X.%s", md5hash.Sum(nil), token[1])
    }

    for {
        param["Image.Offset"] = []string{strconv.Itoa(offset)}
        res, err := http.Get("http://api.bing.net/json.aspx?" +
            param.Encode())
        count := 0
        if err == nil {
            var result *response
            err = json.NewDecoder(res.Body).Decode(&result)
            res.Body.Close()
            if err != nil {
                println(err.Error())
                break
            }
            if count = len(result.SearchResponse.Image.Results); count ==
                0 {
                total = -1
                break
            }
            for _, r := range result.SearchResponse.Image.Results {
                go func(url, ct string) {
                    filename := filepath.Join(outdir, to_filename(url, ct))
                    if f, derr := os.Create(filename); derr == nil {
                        defer f.Close()
                        dres, derr := http.Get(url)
                        if derr == nil && dres.ContentLength > 0 &&
                            strings.Index(dres.Header.Get("Content-Type"), "image/") == 0 {
                            _, derr = io.CopyN(f, dres.Body, dres.ContentLength)
                            if derr != nil {
                                println(derr.Error())
                            } else {
                                println(filename)
                            }
                        }
                    }
                    quit <- false
                }(r.MediaUrl, r.ContentType)
            }
        } else {
            total = -1
            break
        }
        offset += count
        total += count
    }

    for total > 0 {
        <-quit
        total--
        println(total)
    }
}
けしからんけしからん。
追記1
終了を待ってなかった。 ...orz

追記2
ちょっとエラー処理足した。

Posted at by



2012/03/17


Vimには --startuptime というコマンドラインオプションがあり $ vim --startuptime foo の様に指定するとスクリプトファイル単位で掛かった時間を知る事が出来ます。 times in msec
 clock   self+sourced   self:  sourced script
 clock   elapsed:              other lines

000.000  000.000: --- VIM STARTING ---
000.000  000.000: Allocated generic buffers
000.000  000.000: locale set
000.000  000.000: clipboard setup
000.000  000.000: window checked
000.000  000.000: inits 1
000.000  000.000: parsing arguments
000.000  000.000: expanding arguments
000.000  000.000: shell init
016.000  016.000: Termcap init
016.000  000.000: inits 2
016.000  000.000: init highlight
016.000  000.000  000.000: sourcing c:\vim\runtime\syntax\syncolor.vim
016.000  000.000  000.000: sourcing c:\vim\runtime\syntax\synload.vim
062.000  000.000  000.000: sourcing c:\go\misc\vim\ftdetect\gofiletype.vim
062.000  000.000  000.000: sourcing c:\temp\github\rust\src\etc\vim\ftdetect\rust.vim
062.000  000.000  000.000: sourcing c:\temp\bitbucket\vimclojure\vim\ftdetect\clojure.vim
062.000  046.000  046.000: sourcing c:\vim\runtime\filetype.vim
062.000  046.000  000.000: sourcing c:\vim\runtime\syntax\syntax.vim
062.000  000.000  000.000: sourcing c:\vim\runtime\filetype.vim
062.000  000.000  000.000: sourcing c:\vim\runtime\ftplugin.vim
062.000  000.000  000.000: sourcing c:\vim\runtime\indent.vim
062.000  000.000  000.000: sourcing c:\vim\runtime\ftplugof.vim
しかし幾ら不必要なプラグインを削っても起動が遅い事があります。そんな場合はだいたいvimrcの読み込みに時間が掛かっていたりします。 218.000  202.000  093.000: sourcing $HOME\_vimrc
じゃぁ _vimrc の中のどの部分が時間が掛かってるのよ...となるのですが、--startuptime では実行行単位に計測はしてくれません。これをなんとかしたかったので benchvimrc-vim というプラグインを書きました。
mattn/benchvimrc-vim - GitHub

make benchmark result of your vimrc

https://github.com/mattn/benchvimrc-vim
これをインストールして :BenchVimrc を実行すると、行単位に計測時間が表示されます。 00692           : "**************************************************
00693           : "* Syntax On {{{
00694           : "--------------------------------------------------
00695           : if isdirectory($VIMRUNTIME.'/syntax')
00696           :   if &t_Co > 2 || has("gui_running")
00697   0.025281:     syntax on
00698           :   endif
00699           :   if has("autocmd")
00700   0.013719:     filetype plugin indent on
00701           :   endif
00702   0.000107:   autocmd BufReadPost *
00703           :     \ silentif line("'\"") > 0 && line("'\"") <= line("$") |
00704           :     \   exe "normal g`\"" |
00705           :     \ endif
00706           : endif
00707           : 
00708   0.004691filetype plugin off
先頭に計測時間が付与されています。実行していない行にはもちろん計測時間はついていません。
数値が先頭なので :%sort を実行すると一番下に、重い処理が集まります。私のvimrcの例だと   0.004190 00132let &viewdir=globpath(&rtp, "view")
  0.004294 00796:   silentcall metarw#define_wrapper_commands(1)
  0.004459 00133let &undodir=globpath(&rtp, "undo")
  0.004778 00708filetype plugin off
  0.005199 00464let g:skk_large_jisyo = globpath(&rtp, "dict/SKK-JISYO.L")
  0.013917 00700:     filetype plugin indent on
  0.014531 00731:   silentcolorscheme torte
  0.025083 00697:     syntax on
  0.053559 00716:   call pathogen#infect()
  0.375485 00717:   call pathogen#helptags()
こうなりました。どうやら pathogen#helptags に 0.4秒掛かっているようです。新しいプラグインを入れたとき、自動でヘルプタグを作ってくれるので便利ですが、プラグインの更新時や新しいプラグインを入れた際に自分で実行すればいいだけなので、ひとまずコメントアウトしてみます。
vimrc の読み込みに時間が掛かって困ってる人には便利かもしれません。注意点としては、初回実行という訳ではないので完全な計測では無い事、if文内の式には現状対応出来ていない事です。よろしければどうぞ。

追記1
もう一つ注意点、2度実行してもエラーの出ないvimrcでないとエラーになります。

追記2
vimにはprofileという機能がありますが、FEAT_PROFILEを付けてvimをコンパイルする必要があり、それにより計測を行わない場合においても全てのスクリプト実行が幾分遅くなってしまいます。
Posted at by



2012/03/09


VDBIは一日にして出来上がりその日中にXMLRPCプロトコルからJSONRPCプロトコルに変わってしまったという酷いプロジェクトなんだけど、JSONRPCサーバは色んな言語から扱えるのでやってて楽しい。
mattn/vdbi-vim - GitHub

Database client for Vim

https://github.com/mattn/vdbi-vim
最近clojureに興味はあるんだけど、一向にclojure力があがらないので、勉強がてらVDBIのサーバとおしゃべりしてみた。
使ったのは clj-rpc というJSONRPCライブラリ。
zhuangxm/clj-rpc - GitHub

a simple web rpc using clojure and json protocol

https://github.com/zhuangxm/clj-rpc
最初、エンドポイント指定時に :on-witejson に指定しないとデフォルトで clj プロトコル(通信データがJSONじゃなくclojureのコード)になるという罠にどっぷりハマってしまったけど、なんとか動いた。
(ns vdbi-demo
  (:require [clj-rpc.client :as client]))
(def endp (client/rpc-endpoint :server "localhost" :port 9876 :on-wire "json"))
(client/invoke-rpc endp "connect" ["dbi:SQLite:dbname=c:/temp/foo.db" "" ""])
(client/invoke-rpc endp "prepare" ["select * from foo"])
(client/invoke-rpc endp "execute" [])
(let [rows (client/invoke-rpc endp "fetch" [-1])]
  (reduce (fn [rows row]
    (reduce (fn [cols col]
      (print (str col ","))){}, row)
    (println)){}, rows))
1,あいう,
2,かきく,
3,psgix.harakiri,
ちなみにこの clj-rpc は :on-wire をURLのおケツに引っ付けてしまうので、物によっては動かないサーバもあるかも。
あと vimclojure をいれて let vimclojure#FuzzyIndent=1
let vimclojure#HighlightBuiltins=1
let vimclojure#HighlightContrib=1
let vimclojure#DynamicHighlighting=1
let vimclojure#ParenRainbow=1
という設定をvimrcに書いておくとコードが七色になるのでいれるべし。
vimclojure
Posted at by