Chapter 02

ウェブページを取得する

Shotaro Tsuji
Shotaro Tsuji
2021.03.02に更新

まずはreqwestクレートを使ってウェブページを取得するコードを書いてみましょう。最初は使い勝手を確かめるためにsrc/main.rsに直に書いていくことにします。reqwestクレートにどんな機能があるかを知るために、 Docs.rs にあるドキュメントを見てみましょう。

最初に目に入ってくる文章をざっと読むと、reqwestクレートは非同期なHTTPクライアントとブロックする(つまり送信や受信を行う関数を呼び出すと、処理が完了するまで戻ってこない)HTTPクライアントの機能を提供していることが分かります。今回は非同期プログラミングは必要ないので、ブロックするクライアントを使うことにしましょう。

サンプルコードを動かす

reqwest::blockingモジュールのドキュメントを開きます。ドキュメントの"§Making a GET request"に書かれたサンプルコードをひとまずコンパイルできるようにしてしまいます。

Cargo.tomldependenciesセクションにreqwestへの依存を書きます。ドキュメントにはblocking featureを有効にする必要があると書かれているので以下の記述を追加します。また、main関数で?演算子を使いたいのでエラーハンドリングのためにeyreクレートへの依存も宣言します。

Cargo.toml
[dependencies]
reqwest = { version = "0.10.8", features = ["blocking"] }
eyre = "0.6"

reqwest::blockingモジュールのサンプルコードをmain関数の中に貼り付けて、プログラムが動作するようにコードを書き足したものが以下のソースコードになります。main関数の戻り値をResult型にすることで?演算子を使ってエラーを返すことができるようになります。std::result::Result<T, E>を使うとEに適切な型を入れる必要があるので、ここでは簡単に済ませるためにeyre::Result<T>を使いました。eyre::Result<T>を使うことでエラーがBox<dyn std::error::Error>のように振る舞う型に自動で変換されます。

src/main.rs
fn main() -> eyre::Result<()> {
    let body = reqwest::blocking::get("https://www.rust-lang.org")?
        .text()?;

    println!("body = {:?}", body);

    Ok(())
}

これでcargo runを実行すればコンパイルされた後にプログラムが実行されて、HTMLが出力される様子が見られるでしょう。(ちなみにWiFiをオフにするかEthernetケーブルを抜くかして、インターネットへの疎通がない状態にしてからcargo runを実行してみましょう。エラーメッセージが表示されるはずです。)

ソースコードの読解

ソースコードを読んでいきましょう。まず、reqwest::blocking::get関数を呼び出しています。この関数はreqwest::IntoUrlトレイトを実装した型を引数とする関数です。Url自身と&str型と&String型がこのトレイトを実装します[1]。URLではない文字列が与えられるとエラーを返します。

このサンプルコードを読むと、プロトコルがHTTPなのか、あるいはHTTPSなのかはプログラマは気にしなくていいことがわかります。HTTPとHTTPSで通信をするのに必要な処理は異なりますが、そのあたりはreqwestが適切にやってくれるようです。

さて、get関数の戻り値の型はResult<Response>です。このResultreqwest::Resultのことです。エラー型にはreqwestが定義しているreqwest::Errorが入っています。エラーについては飛ばしてResponseについて調べることにしましょう。

Responseのドキュメントを開きましょう。Responseにはいろいろとメソッドが定義されていますが、今回呼び出しているtextメソッドのドキュメントを読んでみます。textメソッドの戻り値の型はResult<String>です。説明を読むとレスポンスボディをヘッダで指定されたエンコーディングに従ってデコードして返すようです。

最後にeyre::Result<T>について説明しておきましょう。この型はResult<T, eyre::Report>として定義されています。eyre::ReportError + Send + Sync + 'staticを実装したオブジェクトから作ることができます。さまざまな型のエラーを一つの型に変換することで、最終的にプログラムが報告するエラーの取り扱いを容易にすることと、バックトレースを提供するのがeyre::Reportの役目です。

この章で作成したファイル

演習

  1. インターネットへの疎通がない状態でプログラムを実行してエラーが出るか確かめてみましょう。
  2. get関数に渡すURLをUTF-8以外でエンコーディングされているウェブページにしてプログラムを動かしてみましょう。例えば https://www.hmv.co.jp/ はShift-JISでエンコードされていて、Content-Typeヘッダで文字コードが指定されています。
    reqwestContent-Typeヘッダで文字コードが指定されていない場合はUTF-8と仮定してボディを読み込みます。http://abehiroshi.la.coocan.jp/ はShift-JISでエンコードされたページですが、Content-Typeヘッダがありません。)
  3. get関数に渡すURLを存在しないページを指すURLにしてみましょう。このとき、レスポンスのステータスコードも表示するようにしてみましょう。(ステータスコードはResponse構造体のstatusメソッドで取得することができます。)
脚注
  1. このことはドキュメントを読むだけではわかりません。公開されていないトレイトPolyfillTryIntoに実装があります。これはソースコードを読めばわかります。 ↩︎