Help us understand the problem. What is going on with this article?

[翻訳] httr vignette: httrはやわかり

More than 3 years have passed since last update.

この文書は Hadley Wickham によるRパッケージhttr (version 1.1.0) のビネット "httr quickstart guide" の翻訳です.
License: MIT


httrはやわかり

この文書の目的は,読者にhttrをできるだけすぐに使い始められるようになってもらうことです.httrは,基礎となっているHTTPプロトコルと密接に対応するように設計されています.この入門編でもHTTPの基本的な事柄は説明するように努めますが,"HTTP: The Protocol Every Web Developer Must Know"や"HTTP made reallyeasy"も読むことを推奨します.

このビネット(とhttr APIの一部)は,Kenneth Reitzによる素晴らしい"Requests quickstart guide"の派生版です.Requestsはhttrと同様の設計思想に基づくPythonライブラリです.

HTTPには2つの重要なパーツがあります.ひとつはリクエストで,サーバに送られるデータのことです.もうひとつはレスポンスで,こちらはサーバから送り返されるデータのことです.最初のセクションでは,リクエストの作成とレスポンスへのアクセスの基本を学びます.2つ目と3つ目のセクションでは,リクエストとレスポンスそれぞれの詳細に踏み込んでいきます.

httrの基本

リクエストを作成するには,まずhttrをロードし,次にGET()にURLを与えて呼び出します:

library(httr)
r <- GET("http://httpbin.org/get")

するとレスポンスオブジェクトが返ってきます.レスポンスオブジェクトを表示するといくつかの有益な情報が得られます.(リダイレクトの後に)実際に使われたURLや,HTTPステータス,ファイル(コンテンツ)のタイプ,サイズなどです.コンテンツがテキストファイルであれば,その最初の数行も表示されます.

r
#> Response [http://httpbin.org/get]
#>   Date: 2016-03-20 18:02
#>   Status: 200
#>   Content-Type: application/json
#>   Size: 300 B
#> No encoding supplied: defaulting to UTF-8.
#> {
#>   "args": {}, 
#>   "headers": {
#>     "Accept": "application/json, text/xml, application/xml, */*", 
#>     "Accept-Encoding": "gzip, deflate", 
#>     "Host": "httpbin.org", 
#>     "User-Agent": "libcurl/7.43.0 r-curl/0.9.6 httr/1.1.0"
#>   }, 
#>   "origin": "119.104.43.128", 
#>   "url": "http://httpbin.org/get"
#> ...

色々なヘルパメソッドを使うことで,レスポンスから重要な箇所を抜き出すことができます.リクエストオブジェクトの中身を直接触ってもかまいません:

status_code(r)
#> [1] 200
headers(r)
#> $server
#> [1] "nginx"
#> 
#> $date
#> [1] "Sun, 20 Mar 2016 18:02:49 GMT"
#> 
#> $`content-type`
#> [1] "application/json"
#> 
#> $`content-length`
#> [1] "300"
#> 
#> $connection
#> [1] "keep-alive"
#> 
#> $`access-control-allow-origin`
#> [1] "*"
#> 
#> $`access-control-allow-credentials`
#> [1] "true"
#> 
#> attr(,"class")
#> [1] "insensitive" "list"
str(content(r))
#> List of 4
#>  $ args   : Named list()
#>  $ headers:List of 4
#>   ..$ Accept         : chr "application/json, text/xml, application/xml, */*"
#>   ..$ Accept-Encoding: chr "gzip, deflate"
#>   ..$ Host           : chr "httpbin.org"
#>   ..$ User-Agent     : chr "libcurl/7.43.0 r-curl/0.9.6 httr/1.1.0"
#>  $ origin : chr "119.104.43.128"
#>  $ url    : chr "http://httpbin.org/get"

この入門編の全体に渡ってhttpbin.orgを利用します.ここは様々なタイプのHTTPリクエストを受け付けて,受け取ったデータを記述するJSONを返してくれるので,httrが何をやっているのかが容易に理解できます.

GET()の他にもHEAD(), POST(), PATCH(), PUT()DELETE()という動詞を使うことができます.おそらくGET()POST()にいちばん馴染みがあるでしょう.GET()はブラウザがページをリクエストするときに使われるもので,POST()は(大抵は)フォームをサーバに投稿するときに使われます.PUT()PATCH()DELETE()はweb APIによって使われることが多いです.

レスポンス

サーバから送り返されてくるデータは3つのパーツで構成されています.ステータス行とヘッダとボディです.ステータス行でいちばん重要な部分はHTTPステータスコードです.これによってリクエストが成功したかどうかがわかります.以下ではステータスコードにアクセスする方法を示し,それからボディとヘッダにアクセスする方法を示します.

ステータスコード

ステータスコードはリクエストが成功したかどうかを要約した3桁の番号です(通信しているサーバによって定義されます).ステータスコードと,その内容を記述したメッセージには,http_status()関数でアクセスできます:

r <- GET("http://httpbin.org/get")
# 役に立つ情報を取得する:
http_status(r)
#> $category
#> [1] "Success"
#> 
#> $reason
#> [1] "OK"
#> 
#> $message
#> [1] "Success: (200) OK"

# または単なるコード値を取得する:
r$status_code
#> [1] 200

成功したリクエストは常にステータス200を返します.よくあるエラーは404(file not found)と403(permission denied)です.web APIを利用している場合は,コード500を見ることもあるかもしれません.このコードは失敗一般を表しています(なので,それほど役立つ情報ではありません).ステータスコードについてもっと知りたい場合,最も覚えやすいガイドはhttp status catsです.

リクエストが成功しなかった場合には自動的にエラーや警告を出すようにできます:

warn_for_status(r)
stop_for_status(r)

httrを関数の中で使用する場合(つまり,インタラクティブに使うのではない場合)には,可能な限り早くエラーに気付けるようにするために,常にこれらの関数のいずれかを使うことを強く推奨します

ボディ

リクエストのボディにアクセスする方法は3種類ありますが,いずれもcontent()関数を使用します:

  • content(r, "text") はボディに文字型ベクトルとしてアクセスします:

    r <- GET("http://httpbin.org/get")
    content(r, "text")
    #> No encoding supplied: defaulting to UTF-8.
    #> [1] "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"application/json, text/xml, application/xml, */*\", \n    \"Accept-Encoding\": \"gzip, deflate\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"libcurl/7.43.0 r-curl/0.9.6 httr/1.1.0\"\n  }, \n  \"origin\": \"119.104.43.128\", \n  \"url\": \"http://httpbin.org/get\"\n}\n"
    

    httrはcontent-type HTTPヘッダで与えられたエンコーディングを使用して,サーバから返されたコンテンツを自動的にデコードします.残念なことに,サーバの言うことが常に信頼できるとは限らないので,必要であればエンコーディングは上書きすることができます:

    content(r, "text", encoding = "ISO-8859-1")
    

    正しいエンコーディングが何なのか見当が付かない場合は,stringi::stri_enc_detect(content(r, "raw"))を試してみてください.

  • テキスト以外のリクエストに対しては,バイト型ベクトルとしてボディにアクセスすることができます:

    content(r, "raw")
    #>   [1] 7b 0a 20 20 22 61 72 67 73 22 3a 20 7b 7d 2c 20 0a 20 20 22 68 65 61
    #>  [24] 64 65 72 73 22 3a 20 7b 0a 20 20 20 20 22 41 63 63 65 70 74 22 3a 20
    #>  [47] 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f 6e 2c 20 74 65 78 74
    #>  [70] 2f 78 6d 6c 2c 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 6d 6c 2c 20
    #>  [93] 2a 2f 2a 22 2c 20 0a 20 20 20 20 22 41 63 63 65 70 74 2d 45 6e 63 6f
    #> [116] 64 69 6e 67 22 3a 20 22 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 22 2c
    #> [139] 20 0a 20 20 20 20 22 48 6f 73 74 22 3a 20 22 68 74 74 70 62 69 6e 2e
    #> [162] 6f 72 67 22 2c 20 0a 20 20 20 20 22 55 73 65 72 2d 41 67 65 6e 74 22
    #> [185] 3a 20 22 6c 69 62 63 75 72 6c 2f 37 2e 34 33 2e 30 20 72 2d 63 75 72
    #> [208] 6c 2f 30 2e 39 2e 36 20 68 74 74 72 2f 31 2e 31 2e 30 22 0a 20 20 7d
    #> [231] 2c 20 0a 20 20 22 6f 72 69 67 69 6e 22 3a 20 22 31 31 39 2e 31 30 34
    #> [254] 2e 34 33 2e 31 32 38 22 2c 20 0a 20 20 22 75 72 6c 22 3a 20 22 68 74
    #> [277] 74 70 3a 2f 2f 68 74 74 70 62 69 6e 2e 6f 72 67 2f 67 65 74 22 0a 7d
    #> [300] 0a
    

    これはまさにwebサーバが送信したバイト列そのものなので,ディスクにファイルを保存する場合には最も正確なやり方です:

    bin <- content(r, "raw")
    writeBin(bin, "myfile.txt")
    
  • httrは,一般的なファイル形式に対するデフォルトのパーサを数多く提供しています:

    # JSONが自動的に名前付きリストにパースされる
    str(content(r, "parsed"))
    #> List of 4
    #>  $ args   : Named list()
    #>  $ headers:List of 4
    #>   ..$ Accept         : chr "application/json, text/xml, application/xml, */*"
    #>   ..$ Accept-Encoding: chr "gzip, deflate"
    #>   ..$ Host           : chr "httpbin.org"
    #>   ..$ User-Agent     : chr "libcurl/7.43.0 r-curl/0.9.6 httr/1.1.0"
    #>  $ origin : chr "119.104.43.128"
    #>  $ url    : chr "http://httpbin.org/get"
    

    パーサの完全な一覧については?contentを参照してください.

    これらのパーサはインタラクティブな用途では便利ですが,APIラッパを書いている場合にはテキストやバイト列のコンテンツを自分でパースして,内容が期待どおりのものか確認するのがベストです.詳細についてはAPIラッパのビネットを参照してください.

ヘッダ

レスポンスヘッダにはheaders()でアクセスします:

headers(r)
#> $server
#> [1] "nginx"
#> 
#> $date
#> [1] "Sun, 20 Mar 2016 18:02:50 GMT"
#> 
#> $`content-type`
#> [1] "application/json"
#> 
#> $`content-length`
#> [1] "300"
#> 
#> $connection
#> [1] "keep-alive"
#> 
#> $`access-control-allow-origin`
#> [1] "*"
#> 
#> $`access-control-allow-credentials`
#> [1] "true"
#> 
#> attr(,"class")
#> [1] "insensitive" "list"

これは基本的には名前付きリストですが,HTTPヘッダには大文字と小文字の区別がないので,
このオブジェクトの要素にアクセスする場合も大文字と小文字の違いは無視されます:

headers(r)$date
#> [1] "Sun, 20 Mar 2016 18:02:50 GMT"
headers(r)$DATE
#> [1] "Sun, 20 Mar 2016 18:02:50 GMT"

クッキー

クッキーにも同様のやり方でアクセスできます:

r <- GET("http://httpbin.org/cookies/set", query = list(a = 1))
cookies(r)
#>        domain  flag path secure expiration name value
#> 1 httpbin.org FALSE    /  FALSE       <NA>    a     1

同じドメインへのリクエストでは,クッキーは自動的に永続化されます:

r <- GET("http://httpbin.org/cookies/set", query = list(b = 1))
cookies(r)
#>        domain  flag path secure expiration name value
#> 1 httpbin.org FALSE    /  FALSE       <NA>    a     1
#> 2 httpbin.org FALSE    /  FALSE       <NA>    b     1

リクエスト

レスポンスと同様,リクエストも3つの部分から成っています.ステータス行,ヘッダ,ボディです.ステータス行はHTTPメソッド(GET,POST,DELETE等)とURLを定義します.(クエリ文字列を使用した)URLや(クッキーを含む)ヘッダ,あるいはPOST()PUT()PATCH()のボディを利用することで,付加的なデータをサーバに送ることができます.

URLクエリ文字列

単純なキーと値のペアをサーバに送る一般的な方法は,クエリ文字列を使うことです.たとえばhttp://httpbin.org/get?key=valといった風にします.httrでは,これらの引数を名前付きリストとしてquery引数で与えることができます.たとえばkey1=value1key2=value2http://httpbin.org/getに渡したければ以下のようにできるでしょう:

r <- GET("http://httpbin.org/get", 
  query = list(key1 = "value1", key2 = "value2")
)
content(r)$args
#> $key1
#> [1] "value1"
#> 
#> $key2
#> [1] "value2"

NULL要素は自動的にリストから落とされます.またキーと値はどちらも自動的にエスケープされます.

r <- GET("http://httpbin.org/get", 
  query = list(key1 = "value 1", "key 2" = "value2", key2 = NULL))
content(r)$args
#> $`key 2`
#> [1] "value2"
#> 
#> $key1
#> [1] "value 1"

カスタムヘッダ

add_headers()を使ってカスタムヘッダをリクエストに加えることができます:

r <- GET("http://httpbin.org/get", add_headers(Name = "Hadley"))
str(content(r)$headers)
#> List of 6
#>  $ Accept         : chr "application/json, text/xml, application/xml, */*"
#>  $ Accept-Encoding: chr "gzip, deflate"
#>  $ Cookie         : chr "a=1; b=1"
#>  $ Host           : chr "httpbin.org"
#>  $ Name           : chr "Hadley"
#>  $ User-Agent     : chr "libcurl/7.43.0 r-curl/0.9.6 httr/1.1.0"

content(r)$headerにはhttpbinが受けとったヘッダが入っていることに注意してください.headers(r)はhttpbinが送り返してきたレスポンスのヘッダを与えます.)

クッキー

クッキーはクエリ文字列と同様に単純なキーと値のペアですが,セッション中の複数のリクエストに渡って永続化されています(なぜなら毎回のリクエストで送受信がなされるからです).自分で作ったクッキーをサーバに送るにはset_cookies()を使用します:

r <- GET("http://httpbin.org/cookies", set_cookies("MeWant" = "cookies"))
content(r)$cookies
#> $MeWant
#> [1] "cookies"
#> 
#> $a
#> [1] "1"
#> 
#> $b
#> [1] "1"

このレスポンスには以前サーバによって加えられたクッキーabが含まれていることに注意してください.

リクエストボディ

POST()するときにデータをリクエストのボディに含めることができます.httrでは様々な異なったやり方でデータを与えることができます.最も一般的な方法は名前付きリストを使うことです:

r <- POST("http://httpbin.org/post", body = list(a = 1, b = 2, c = 3))

encode引数を使って,このデータがどのようにサーバに送られるかを指定できます:

url <- "http://httpbin.org/post"
body <- list(a = 1, b = 2, c = 3)

# Formエンコード
r <- POST(url, body = body, encode = "form")
# Multipartエンコード
r <- POST(url, body = body, encode = "multipart")
# JSONエンコード
r <- POST(url, body = body, encode = "json")

何がサーバに送られているか正確に知りたければ,verbose()を使ってください.残念なことに,verbose()の仕様によりknitrがメッセージをキャプチャできないので,何が起こっているのか知りたければ,インタラクティブコンソールでこれらのコードを実行する必要があります.

POST(url, body = body, encode = "multipart", verbose()) # the default
POST(url, body = body, encode = "form", verbose())
POST(url, body = body, encode = "json", verbose())

PUT()PATCH()POST()と同じくリクエストボディを持つことができます.

ディスク上のファイルを送信することもできます:

POST(url, body = upload_file("mypath.txt"))
POST(url, body = list(x = upload_file("mypath.txt")))

upload_file()はmime-typeを拡張子から推測しますが,type引数によって自分で与えたり上書きすることもできます.)

これらのアップロードにおいては,データはサーバにストリーミングで送信されます.データがRにチャンクとしてロードされ,リモートサーバに送られるのです.したがってメモリに収まらない大きなファイルもアップロードすることができます.

他に送信できるもののタイプについて,詳しくはPOST()を参照してください.ボディなし,空のボディ,文字列型とバイト型のベクトルがあります.


この文書は以下のR環境でビルドされました[訳注:訳者の環境です]

sessionInfo()
#> R version 3.2.3 (2015-12-10)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 7 x64 (build 7601) Service Pack 1
#> 
#> locale:
#> [1] LC_COLLATE=Japanese_Japan.932  LC_CTYPE=Japanese_Japan.932   
#> [3] LC_MONETARY=Japanese_Japan.932 LC_NUMERIC=C                  
#> [5] LC_TIME=Japanese_Japan.932    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] httr_1.1.0          RevoUtilsMath_3.2.3
#> 
#> loaded via a namespace (and not attached):
#>  [1] R6_2.1.2        magrittr_1.5    formatR_1.2.1   tools_3.2.3    
#>  [5] htmltools_0.3   curl_0.9.6      yaml_2.1.13     stringi_1.0-1  
#>  [9] rmarkdown_0.9.2 knitr_1.12.3    jsonlite_0.9.19 stringr_1.0.0  
#> [13] digest_0.6.9    evaluate_0.8
nakamichi
R / Python / data analysis / machine learning
abeja
「ディープラーニング」を活用し、多様な業界、シーンにおけるビジネスの効率化・自動化を促進するベンチャー企業です。
https://abejainc.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした