(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

MediaWikiのAPIをGaucheで叩くとなんか変。

変というか、思った通りの結果+αになってしまって困っている感じ。
Wikipediaのページをランダムに取得して、適当に呟くようにしようとしてMediaWikiAPIを調べて以下のようなURLを作った。

http://ja.wikipedia.org/w/api.php?action=query&list=random&rnnamespace=0&format=json

一先ずこれで何かしら取得出来るのは、適当なブラウザに突っ込んでみたら分かると思う。
MediaWikiAPIに関してはこっち => API:Main page - MediaWiki

ちなみにこんな感じのデータがjson形式で返ってくる。formatをxmlと書き換えればxmlが返ってくる。

{"query":{"random":[{"id":419952,"ns":0,"title":"\u6728\u4e4b\u672c\u8b66\u5bdf\u7f72"}]}}

それでGaucheだと何がおかしいかというと、以下のコード。

(receive (status head body)
    (http-get "jp.wikipedia.org"
	      "/w/api.php?action=query&list=random&rnnamespace=0&format=json")
  (print body)) ;;デバッグするのにprintしてる

いつもの要領でこういうコード書いてみた。いつもなら、結果のjson(もしくはxml)だけが取得出来てそれをパースするだけで済む…。はずだったんだけど、なんかパースでコケるからprintしてみたら、下のような結果が返ってくる。

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://ja.wikipedia.org/w/api.php?action=query&amp;list=random&amp;rnnamespace=0&amp;format=json">here</a>.</p>
</body></html>
{"query":{"random":[{"id":2186129,"ns":0,"title":"\u62db\u9060\u770c (\u9577\u6cbb\u5e02)"}]}}
#<undef>

前半HTML、後半は結果のjson。コケるわけです。ちなみにprintしているから、普通に改行されているけど改行コード含む文字列が返ってきています。
苦肉の策で(string-scan body "" 'after)みたいなことしたけど、なんか腑に落ちない。

ちなみにこれ、Rubyでこういうコードをirbで試してみると。

require 'open-uri'
require 'pp'
a = 'http://ja.wikipedia.org/w/api.php?action=query&list=random&rnnamespace=0&format=json'
b = open a
b.read
pp b

こんな感じになる。

irb(main)> b.read
=> "{\"query\":{\"random\":[{\"id\":192347,\"ns\":0,\"title\":\"\\u592a\\u5e73\\u6d0b\\u30d5\\u30a7\\u30ea\\u30fc\"}]}}"
irb(main)> pp b
#<StringIO:0x00000001e3f2a0
 @base_uri=
  #<URI::HTTP:0x00000001e3f7a0 URL:http://ja.wikipedia.org/w/api.php?action=query&list=random&rnnamespace=0&format=json>,
 @meta=
  {"date"=>"Tue, 03 Sep 2013 21:54:07 GMT",
   "server"=>"Apache",
   "x-content-type-options"=>"nosniff",
   "cache-control"=>"private",
   "x-frame-options"=>"DENY",
   "vary"=>"Accept-Encoding,X-Forwarded-Proto",
   "content-length"=>"96",
   "content-type"=>"application/json; charset=utf-8",
   "x-cache"=>"MISS from cp1001.eqiad.wmnet, MISS from cp1002.eqiad.wmnet",
   "x-cache-lookup"=>
    "MISS from cp1001.eqiad.wmnet:3128, MISS from cp1002.eqiad.wmnet:80",
   "connection"=>"close"},
 @status=["200", "OK"]>
=> #<StringIO:0x00000001e3f2a0>

wgetあたりでも前半のHTML部分は取得されていない。ので、Gaucheの何かがおかしい?

追記。

落ち着いて返ってきているHTML部分をよく見ると301 Moved Permanentlyと書いてあって、これってつまりリダイレクトされている?
Gaucheのドキュメントを調べてみると。

Function: http-get server request-uri :key sink flusher redirect-handler secure …
Function: http-head server request-uri :key redirect-handler secure …
Function: http-post server request-uri body :key sink flusher redirect-handler secure …
Function: http-put server request-uri body :key sink flusher redirect-handler secure …
Function: http-delete server request-uri :key sink flusher redirect-handler secure …
serverに、それぞれHTTPのGET、HEAD、POST、PUT、DELETEリクエストを送り、 サーバの応答を返します。

デフォルトでは、 サーバがステータスコード 300, 301, 302, 303, 305 の応答を返し、 RFC2616による自動リダイレクトが許されている場合は、 これらの手続きは自動的に応答のメッセージヘッダの "location" で返されるURIに対して リクエストを再送します。 この動作はredirect-handlerキーワード引数でカスタマイズしたり 抑制したりできます。下の"キーワード引数"を参照してください。
11.23 rfc.http - HTTP

戻り値: 全ての手続きは3つの値を返します。

1つ目は、RFC2616で定義されているステータスコードの文字列値(例えば、成功時の 200、"Not found"の404など)です。

2つ目は、パーズされたヘッダのリストで、リストの要素は(header-name value …)です。header-nameはヘッダの文字列名(例えば、 "content-type"や"location"など)で、valueは対応する値の文字列値です。 ヘッダ名は小文字に変換されます。値は、RFC2822で定義されている無指定行区切 (ソフト・ライン・ブレイク)が除かれる以外はそのままです。 サーバが同じ名前のヘッダを1つ以上返した場合は、 1つのリストに統合されます。それ以外では、2つ目の戻り値に おけるヘッダのリストの順番は、サーバの応答での順番と同じです。

3つ目の戻り値は、サーバの応答におけるメッセージボディです。 デフォルトでは、文字列で表現されたメッセージボディそのものです。 サーバの応答がボディを持たない場合、3つ目の戻り値は#fです。 キーワード引数によって、メッセージボディがどのように扱われるかを制御できます。 例えば、中間的な文字列を作らずに、返されたメッセージボディを直接ファイルに 格納することが出来ます。詳細は以下で説明しています。
11.23 rfc.http - HTTP

あーあー。ということは中間の結果がメッセージボディにばっちり格納されているという認識でOKなのかな。

で、書いているままを受け取ればsink,flusherあたりのキーワード引数で最後のリクエストに対するボディだけを取得することも可能そう。
とりあえず、いっか。後でもう少し色々試してみよう。