Category: ruby

read_body の抜け方。

comments read_body の抜け方。 はコメントを受け付けていません。
By , 2010年8月29日 (日) 22:10:46

さっきの続きで次のようなコードを考えてみた。

$lasttime = Time.now
def proc(uri)
  begin
    t = Thread.new {
            while true
                    if(Time.now - $lasttime > TIMEOUT_SECONDS)
                            raise "Read Chunk Timeout"
                    end
                    sleep TIMEOUT_SECONDS
            end
    }
    Net::HTTP.start(uri.host, uri.port) { |http|
            req = Net::HTTP::Post.new(uri.request_uri)
            req.set_form_data({'track' => TRACK, 'follow' => FOLLOW }, '&')
            req.basic_auth(TWITTER_SCREEN_NAME, TWITTER_PASSWORD)
            http.request(req) { |response|
                    response.read_body { |chunk|
                            subproc(chunk)
                            $lasttime = Time.now
                    }
            }
    }
  rescue
    t.kill
    return
  end
end

read_body のブロックの中では、chunk が読まれるたびに $lasttime の値が最新の時刻に更新される。 これとは別に動いているスレッドでは $lasttime と現在時刻との差を常に監視している。 $lasttime が現在時刻と比べて TIMEOUT_SECONDS よりも小さくなっているということは、 read_body がうまくループしてないことを意味する。 そこでこのスレッドは例外を発生させて、 rescue で例外処理に移ることによって read_body ブロックを強制的に抜けて、スレッド自身も kill して後片付けする。 どうよ。

$lasttime をグローバル変数にする必要はないのかもしれん。proc() の中の変数にしても良いのではないかと思うのだが、 スレッド間で共有しているので、念のため。

イテレータのタイムアウト

comments イテレータのタイムアウト はコメントを受け付けていません。
By , 2010年8月29日 (日) 20:25:47

たとえばこういう Ruby のコードがあって、

Net::HTTP.start(host, port) { |http|
  http.request(request) { |response|
    response.read_body { |chunk|
      func(chunk)  
    }
  }
}

read_body {|chunk| … } というイテレータで、「エンティティボディを少しづつ取得して順次ブロックに与え」る、 つまり、ストリーミングで一行ずつ、データを chunk に入れてくれるわけなのだが、 twitter の streaming api が何らかの理由で止まったりして、再接続したいときがある。 その場合に Ruby の timeout というライブラリを使って、

Net::HTTP.start(host, port) { |http|
  timeout(TIMEOUT_SECS){ http.request(request) { |response|
    response.read_body { |chunk|
      func(chunk)  
    }
  }}
}

とやっても駄目で、

Net::HTTP.start(host, port) { |http|
  http.request(request) { |response|
    timeout(TIMEOUT_SECS) { response.read_body { |chunk|
      func(chunk)  
    }}
  }
}

とやっても駄目で、

Net::HTTP.start(host, port) { |http|
  http.request(request) { |response|
    response.read_body { |chunk|
      timeout(TIMEOUT_SECS){ func(chunk) }  
    }
  }
}

とやっても駄目だ。 read_body は一種の永久ループなので、これが timeout することは決してない。 いや、強制的に timeout させても意味がない、というべきか。 ブロック内の実行がある一定時間以上かかっていたらそのブロックを抜けるようなコードを書いてあげなくてはならないのだが、 スレッドを使って

  http.request(request) { |response|
    response.read_body { |chunk|
      $func_result = false
      t = Thread.new {
         sleep(TIMEOUT_SECS)
         if($func_result != true)
            Syslog.log(Syslog::LOG_WARNING, "Timeout")
            return
         end
      }
      $func_result = func(chunk)
      t.kill
    }
  }

こうすると、スレッドの中で sleep した後に、func がまだ true を返していなければ、 スレッドを抜ける前に syslog に警告を残してブロックを抜けてくれる。 funcがスレッドより先に終了すれば、t.kill でスレッドの後始末をする。 のではないかと思ったわけだ。 うまく動いてくれるかどうがわからんが。

ていうか、 a = func() という代入式があったとして、a の値が変更されるのは、 func() から値が返った後だよな。 返るまでは a には元の値が入ってるよな。 マルチスレッドだとそういうタイミングが問題になってくる。

スレッドって面白いけど難しいよな。どうやって動作確認すりゃいいのか、よくわからん。

ていうか、chunk に値が渡されるまでブロックの中身は実行されなさそうだよな。 だとするとこういうこと書いても無意味かもしれん。 じゃあどうすりゃいいんだという。 なんか Ruby を C言語のレベルでいじる(ライブラリを書く、read_bodyの実装をいじる)とかしないと根本的解決にはならんような気がしてきた。

Twitter Streaming API + Ruby

comments Twitter Streaming API + Ruby はコメントを受け付けていません。
By , 2010年8月20日 (金) 12:55:13

いろいろうだうだはまったがなんとか動き出した。 ソースが長くなったんで、 wordpress の plugin では限界があるんで、 wiki の方に貼っておいた。 Twitter Streaming API + Ruby

いろんなことが関わってくる。 MySQL/Ruby、Syslog、Daemon、Twitter Streaming API。

ここで最初やりたかったことは、 或る一定時間以上読み込みがとぎれたときには、コネクションが死んでる可能性があるので、 一旦切って再接続したいということだった。 しかしほんとにそれが成功しているかはわからん。

MySQLサーバとの接続が失われたときにも再接続するようにしたいが、まあ、 そこまでしなくていいか。

daemon + mysql

comments daemon + mysql はコメントを受け付けていません。
By , 2010年8月20日 (金) 04:59:34

とりあえず、GETメソッドならこれでよし。

[ruby] require ‘net/http’ require ‘uri’ require ‘rubygems’ require ‘json’ require ‘mysql’ TWITTER_SCREEN_NAME = ‘xxxxxx’ TWITTER_PASSWORD = ‘xxxxx’ MYSQL_HOST = ‘xxxxx’ MYSQL_USER = ‘xxxxx’ MYSQL_PASSWORD = ‘xxxxx’ MYSQL_DATABASE = ‘xxxxx’ MYSQL_TABLE = ‘xxxxx’ uri = URI.parse(‘http://stream.twitter.com/1/statuses/sample.json’) mysql = Mysql.new(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE) Process.daemon Net::HTTP.start(uri.host, uri.port) do |http| request = Net::HTTP::Get.new(uri.request_uri) request.basic_auth(TWITTER_SCREEN_NAME, TWITTER_PASSWORD) http.request(request) do |response| response.read_body do |chunk| status = JSON.parse(chunk) rescue next next unless status[‘text’] next if ‘ja’ != status[‘user’][‘lang’] screen_name = Mysql.quote(status[‘user’][‘screen_name’]) text = Mysql.quote(status[‘text’]) q = "insert #{MYSQL_TABLE} (screen_name,text) values (‘#{screen_name}’,’#{text}’)" mysql.query(q) end end end [/ruby]

POSTメソッドもこんな感じでいける。

[ruby] uri = URI.parse(‘http://stream.twitter.com/1/statuses/filter.json’) mysql = Mysql.new(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE) Process.daemon Net::HTTP.start(uri.host, uri.port) do |http| request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data({‘track’ => ‘nhk,etv,ntv,tbs,fujitv,tvasahi,tvtokyo’, ‘follow’ => ‘112686015,117441019,105529019’}, ‘&’) request.basic_auth(TWITTER_SCREEN_NAME, TWITTER_PASSWORD) http.request(request) do |response| response.read_body do |chunk| status = JSON.parse(chunk) rescue next next unless status[‘text’] next if ‘ja’ != status[‘user’][‘lang’] screen_name = Mysql.quote(status[‘user’][‘screen_name’]) text = Mysql.quote(status[‘text’]) q = "insert #{MYSQL_TABLE} (screen_name,text) values (‘#{screen_name}’,’#{text}’)" mysql.query(q) end end end [/ruby]

levenshtein-0.2.0-1.9

comments levenshtein-0.2.0-1.9 はコメントを受け付けていません。
By , 2010年6月27日 (日) 15:04:43

Erik Veenstra氏によるlevenshtein-0.2.0wrsに使っていたのだが、 ruby が 1.9 になってからうまくコンパイルできなくなっていた。 いや、もともとうまくコンパイルできないんで、工夫が要ったのだけど。 それでなんかいつも授業準備ばかりで煮詰まったので、 ソースをつらつら眺めてみると、 ruby-1.8 の ruby.h では

struct RArray {
    struct RBasic basic;
    long len;
    union {
        long capa;
        VALUE shared;
    } aux;
    VALUE *ptr;
};

となっているのが、ruby-1.9 では

struct RArray {
    struct RBasic basic;
    union {
        struct {
            long len;
            union {
                long capa;
                VALUE shared;
            } aux;
            VALUE *ptr;
        } heap;
        VALUE ary[RARRAY_EMBED_LEN_MAX];
    } as;
};

などとなっており、構造体や共用体の階層が深くなっていることがわかる。 で、

 l1    = RARRAY(rb_o1)->len;

となっているようなところを、

 l1    = RARRAY(rb_o1)->as.heap.len;

のように書き換えてやればコンパイルが通ることがわかった。 RString 構造体関係も同様に書き換えてやる必要がある。

このように、Ruby 1.9 は Array や String などの基本的な型の内部構造がかなり変更されているので、 外部ライブラリがそのままではコンパイルできないようになってしまったのだと思われる。

ま、ともかくやっとこれでうちも Ruby 1.9 に移行できる。やれやれ。 Rubyのパッケージの作り方とかよくわからんのだが、とりあえず levenshtein-0.2.0-1.9.tar.gzというものを置いておく。 展開して

ruby extconf.rb

して

make install

すればインストールされるはず。

Hash clear

comments Hash clear はコメントを受け付けていません。
By , 2010年1月24日 (日) 00:12:54

だいぶ速くなってきたような気がするが、それでもまだ遅いのでいろいろいじる。

OptimizingRubyProgramによれば Hash は new するより clear する方が速いらしいので、そうしてみる。 Array は new するより clear するより replace([]) する方が速いらしいのでそうしてみる。

Array、Hash ともに concat が速いらしいので、使ってみる。

大量に Hash とか Array 使っているだけなので、 一部 Cで書き直すというのも良い手かもしれんとは思う。

というか、以前1週間くらいかかっていたのが3日くらいですめば十分な気もする。

jawiki-20100107-pages-meta-history.xml.7z

comments jawiki-20100107-pages-meta-history.xml.7z はコメントを受け付けていません。
By , 2010年1月10日 (日) 19:37:20

さっそく処理開始。 さて、少しは速くなってくれたかな。

だめだ。 やはり GC.start は必要。

rbprof

comments rbprof はコメントを受け付けていません。
By , 2010年1月10日 (日) 13:44:26

RbProfを使ってみる。

Standard profiler (profile.rb) is slow (typically slows down your program by a factor of 60-200).

Ruby 標準の profiler だとだいたい 60倍から 200倍遅くなる、と言っている。

Fast (typically 5-10 times faster than Ruby’s standard profiler)

でそれが 5倍から 10倍速くなるわけだから、 実質 5倍くらい遅くなるわけね。 遅いよ遅いっ(笑)。 まあでもたまには使ってみるか。

rbprof を使うには aspectr というものもインストールしなくてはならない。 その辺少しとまどう。

なんか warning がぼろぼろ出る。 しかもプロファイルの意味(?) がさっぱりわからん。 これはどうも使い物にならんね。 2002年で開発止まってるし。 やれやれ。

ruby profile

comments ruby profile はコメントを受け付けていません。
By , 2010年1月10日 (日) 06:27:18

Ruby Profileって それ自体がかなり遅いようだな。 全然終わらんよ。

profile はそれ自身がオーバーヘッドになるためメソッド呼び出しあたりの処理時間がかなり遅くなります。

RAA – rbprof

Profiler which is faster and more “to the point” than profile.rb.
Fast (typically 5-10 times faster than Ruby’s standard profiler)

なななんだってー。

MySQL/Rubyが遅い→Ruby-DBIで対抗 か。 まあ、今回ここで時間かかってるわけではないと思うのだが。 ともかく、profile が遅いってことだけはわかった。 あと、やはりリハビリは重要。 いつもやってなきゃ駄目ってことだな。

あと、メモリ増やしたので GC.start は外してみた。

ruby 周り

comments ruby 周り はコメントを受け付けていません。
By , 2010年1月8日 (金) 15:38:40

比較的ヒマなのと、リハビリを兼ねて Ruby をいじってみる。 fedora もいつのまにか 12 になってる。 fedora 11 では update しても ruby は 1.8.6 のまま。 1.8.7 または 1.9.1 では高速化しているというが、 1.9.1 使うと古いパッケージが動かない。 1.8.7 ならなんとかいけるか。 rails は使ってないので私には関係ないもんね。 ていうかもう、jawiki の解析に1週間かかるのをどげんかせんといかんと思う。 最適化して、一番内側のループちょっといじっただけで倍くらい速くなるならしたい。 ruby のバージョンアップもね。

fedora も upgrade したくないわけではないのだが。

ま、とりあえず fedora 11 の update から。

まだ少し予算があるから一台 poweredge 安いの買って fedora 12 入れてみるか。 fedora のバージョンアップに合わせて poweredge をこつこつ買うってのも良いよね。

ruby 1.9.1 にするのはいろんなパッケージが対応してからで良いよね。

メモリはやはり4GBあらまほしけれ。 fedora の update にかかる時間が全然違うっぽい。

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 66.62  3919.32   3919.32        2 1959660.00 2562660.00  Kernel.open
  8.48  4418.15    498.83 44022878     0.01     0.01  IO#gets
  6.31  4789.16    371.01  2814690     0.13     0.17  Object#xmldecode
  4.67  5064.00    274.84    30847     8.91    24.42  Array#each
  2.82  5230.01    166.01     3469    47.86    72.67  Array#sort
  2.04  5350.00    119.99 11258760     0.01     0.01  String#gsub!
  1.09  5413.92     63.92  3873461     0.02     0.02  Array#[]
  0.99  5471.94     58.02     3470    16.72   243.02  Hash#each_pair
  0.93  5526.88     54.94  4056750     0.01     0.01  Time#gm
  0.76  5571.55     44.67  4056026     0.01     0.01  Fixnum#+
  0.72  5614.10     42.55  4056750     0.01     0.01  Float#<
  0.72  5656.65     42.55  4056750     0.01     0.01  Time#-
  0.69  5697.22     40.57  2512900     0.02     0.02  Fixnum#==
  0.68  5737.30     40.08  1747299     0.02     0.02  Array#<<
  0.64  5775.08     37.78  1676033     0.02     0.02  Hash#[]=
  0.51  5805.31     30.23  1672479     0.02     0.02  Fixnum#<=>
  0.45  5831.56     26.25  1564378     0.02     0.02  Hash#include?
  0.27  5847.69     16.13    16922     0.95     1.44  Array#include?
  0.26  5862.75     15.06    12786     1.18     1.18  Array#join
  0.16  5871.98      9.23   513294     0.02     0.02  Hash#[]
  0.06  5875.40      3.42   349011     0.01     0.01  Fixnum#<
  0.03  5877.33      1.93       69    27.97    27.97  GC.start
  0.03  5879.21      1.88    39393     0.05     0.05  String#+
  0.03  5880.94      1.73    11267     0.15     0.15  Mysql#quote
  0.01  5881.47      0.53    14169     0.04     0.06  Class#new
  0.01  5881.89      0.42    24355     0.02     0.02  Fixnum#to_s
  0.00  5882.14      0.25     3764     0.07     0.07  Mysql#query
  0.00  5882.37      0.23    10407     0.02     0.02  Array#initialize
  0.00  5882.58      0.21    12786     0.02     0.02  Array#empty?
  0.00  5882.74      0.16     3759     0.04     7.44  Object#userfunc
  0.00  5882.85      0.11     3762     0.03     0.03  Hash#initialize
  0.00  5882.96      0.11     3759     0.03     0.03  Array#size
  0.00  5883.06      0.10     7518     0.01     0.01  Fixnum#%
  0.00  5883.12      0.06     3759     0.02     0.02  Array#uniq!
  0.00  5883.16      0.04     3759     0.01     0.01  Hash#empty?
  0.00  5883.17      0.01        1    10.00    10.00  Kernel.require
  0.00  5883.17      0.00        7     0.00     0.00  Class#inherited
  0.00  5883.17      0.00        1     0.00     0.00  Hash#size
  0.00  5883.17      0.00       11     0.00     0.00  Kernel.singleton_method_added
  0.00  5883.17      0.00        2     0.00     0.00  IO#close
  0.00  5883.17      0.00        1     0.00     0.00  Fixnum#/
  0.00  5883.17      0.00        6     0.00     0.00  IO#write
  0.00  5883.17      0.00        1     0.00     0.00  Mysql#initialize
  0.00  5883.17      0.00        3     0.00     0.00  IO#puts
  0.00  5883.17      0.00        2     0.00     0.00  Time#now
  0.00  5883.17      0.00        1     0.00     0.00  Time#strftime
  0.00  5883.17      0.00        1     0.00     0.00  File#initialize
  0.00  5883.17      0.00        2     0.00     0.00  Time#initialize
  0.00  5883.17      0.00       10     0.00     0.00  Hash#default
  0.00  5883.17      0.00        1     0.00     0.00  Mysql#close
  0.00  5883.17      0.00        2     0.00     0.00  Fixnum#*
  0.00  5883.17      0.00        1     0.00     0.00  Mysql#new
  0.00  5883.17      0.00      131     0.00     0.00  Module#method_added
  0.00  5883.17      0.00        1     0.00 5883170.00  #toplevel

ruby -r profile の結果。 これはどう解釈すればいいんだ。 Kernel.openとgetsはファイル読み込みだから、これが時間かかるのは仕方ないよな。 どうしろと。

ふむ。gsubにけっこう時間がかかっているようだ。 Rubyじか書きよりパッケージを使った方が良いらしいから、

require 'cgi'

して、CGI.unescapeHTML を代わりに使ってみる。

ruby をソースからビルドしたせいで mysql-ruby も入れ直し。

http://rubyforge.org/frs/download.php/51087/mysql-ruby-2.8.1.tar.gz

を拾ってくる。

ruby extconf.rb --with-mysql-config=/usr/lib/mysql/mysql_config

とか。mysql-devel 必要。

levenshtein がうまくビルドできなくていらいら。

ruby build.rb

とかやってもなんかだめ。 build.rb 読んで system とかでやってる辺りを手作業でうだうだやったらできた。

ダンプ全体を読ませるのは無茶だが、 page要素ごとに分解して xml パーザに読ませちゃどうかと思った。 nokogiri とか libxml-ruby などが速いらしい。 nokogiri tutorials とか読んでだいたい使い方はわかったが、 ちと思案中。

ruby 1.9.1をfedora11に入れてみる。

comments ruby 1.9.1をfedora11に入れてみる。 はコメントを受け付けていません。
By , 2009年11月4日 (水) 04:36:37

けっこうむずい。

MySQL/Ruby もコンパイル。 yum なら yum install mysql-ruby一発なのだが。 yum で zlib-devel、mysql-devel なども入れる必要があるかも。 –with-mysql-config オプションつけるといける。

が、しかし、Levenshteinがうまくコンパイルできない。

UTF-8で書いているのだが、invalid multibyte char (US-ASCII) と怒られる。 rubyスクリプトの1行目に

# -*- encoding: utf-8 -*-

と書く必要があるらしい。

速くなったかどうかはよくわからない。

invalid byte sequence in UTF-8 (ArgumentError)

と言われて途中で止まってしまった。うーん、こりゃダメかもしれんね。

ruby-1.9.2-previewでもやだめやった。1.8に戻すか。 levenshteinもバージョンアップに期待。 他力本願。

gem版levenshteinを使ってみる。

comments gem版levenshteinを使ってみる。 はコメントを受け付けていません。
By , 2009年11月1日 (日) 04:38:47

なんか、またしても無限ループから帰ってこないのでへこたれた。 原因不明だがまたlevenshteinのせいだろうか。 で、いっそのことCで書かれてて速いというライブラリ levenshtein0.2.0 を試してみることにする。 RubyForge levenshtein0.2.0 を拾ってくる。 .gemでもよかったのかもしれないが、.tar.gzをとってくる。

展開する。 rubygemsが無いよと言われたので、

# yum install rubygems

gccも入ってなかったので(汗)

# yum install gcc

それからやっと

$ ruby build.rb

が動いてなんちゃら .gem、.gemspecなどのファイルができた。 ここでも一度ネット検索して、

# gem install levenshteinxxxx.gem

まだ動かない。

require 'levenshtein'

ではなく

require 'rubygems'
require 'levenshtein'

やっと動いた。

distance = Levenshtein.normalized_distance(title_tmp, title)
if(distance < DISTANCE_MAX)
      sorted_similar << [title_tmp, distance]
end

とか。

ああ、やっと結果が出だしたぞ。 上の例でDISTANCE_MAX = 0.3にしたら、 「のなかみのる」と「すがやみつる」、「2月11日」と「2月21日」、「黒岩よしひろ」と「高橋よしひろ」、 「たがみよしひさ」と「あさりよしとお」などが似てると判定された。 ええっと。最後の例は一見似てないが、「よし」が一致している罠。 「のなかみのる」と「すがやみつる」は「み」と「る」が一致してる。 微妙(笑)。

やはり止まる。

comments やはり止まる。 はコメントを受け付けていません。
By , 2009年10月27日 (火) 08:27:07

またまたRubyが無言でお亡くなりになっていた。 800万件くらいすぎたところで。 やあ困ったな。 ウィキペディア英語版は今600万件あるらしいから、 特別ページや単なるリダイレクトを含めると実際の項目数はその倍くらいあるだろう。 ゴールは2000万件くらいか。 遠い。遠すぎるよ。 そのうち一部を抽出しようとしてたのだが。 まあしかし、2GBメモリで800万件で死亡したのなら、 8GBにすれば2000万件くらいいけちゃうのかもね。さて。

限界まで来て死んでることも、途中で勝手に死んでることもあって、原因もよくわからん。 これは困った。 仮にメモリ増やしても解決しなけりゃどうなる。

GCの改善について うーむ。 Javaで一から書き直してみるとかはやりたくない。 とりあえず -v つけるか。

ていうか、項目数が増えると当然編集したユーザ数も増える罠。 推薦システムというのは項目と編集者のマトリクスで決まるわけだから、 仮に項目数と編集者数が比例関係にあるとして、 計算量やメモリ使用量は項目数の自乗に比例しそうだ罠 (computer scienceの人だと「しそうだ罠」みたいないい加減なことは言ってはならんのだろうが。 computer scienceの端っこの人?ていうか推薦システムの計算量についてなんてどこかにもう論文あるだろうね)。 いやー、困ったなあ。 ていうかね、割と、重要な項目でも早期収束してしまって、 最近はあまり編集されてないページというのもあるようなのだよね。 となると、 やはり全数検査したくなる罠。 少なくとも、最近三ヶ月分とかじゃうまくいかん気がしてきた。 いや、実は早期収束している重要なページというのはみなリダイレクトで、 重要な項目は常にIPユーザによるスパムにさらされていてそれをいちいちリバートするので、 三ヶ月以上更新なしということはあり得ないのかもしれん。 てか半保護ページなら更新なしもあり得るわけだが。 いやーまだまだ勉強が足りないね。

nobel prizeのページのnobelのところを自分の名前らしき単語に置き換えるIPスパム野郎が居て、 それをリバートされてまた書き換えみたいな履歴が延々残ってて笑ろた。

ウィキペディア英語版だが、北米人が3億人いて一番話者は多いとして、 インド人とかも多そうだな。 ヒンディ語版もあるようだが、項目数が5万とかって誤差のレベルだし。 日本人はせいぜい1億人で勝ち目はない罠。 しかも英語版は古くからあってその蓄積もすごいし。 インドは鉄道王国らしいし、インド人に鉄オタがたくさん居れば、 英語版はものすごい勢いでインドの鉄道駅とか車両の項目が増えそうだが。日本語版と同じことが起きればの話だが。 いや、やはり日本固有のことかもしれん。 ヨーロッパもフランス、ドイツ語圏以外は英語版にさくっと書いちゃう気もするし。

アリストテレスとかアインシュタインとかは日本で言えば織田信長とか紫式部的なもので、 英語圏の人間なら一度は編集してみたくなる項目なのかもしれん。

まだまだ予断は許されんが(笑)、このまま英語版で一番編集されているのが「ブリトニー」で逃げ切ると面白いのだが。

あら、ブリトニーはジーザスに追い抜かれた。 playstation3はwiiに追い抜かれた。

中国語版を編集しているのが台湾人なのか華僑なのかそれとも本国人なのかもすごく気になる。 項目数はまだまだ少なく、しかもオタク系のページは日本語のページのコピーみたいなのが多いわけだが。

Panorama Theme by Themocracy