【第2回】「Guzzle」でAPI通信やスクレイピングを使いやすく

【第2回】Composerを使ってお手軽アプリケーション開発

本連載ではComposerで公開されているパッケージの中から、フレームワークを問わず汎用的に使えるライブラリをサンプルコードと共に紹介します。
今回は「Guzzle」というAPI通信やスクレイピングに便利なパッケージを紹介します。

Guzzleの概要

GuzzleはHTTPクライアントのパッケージで、一言で説明すると「高性能なcurl関数のラッパー」です。みなさんはAPI通信やスクレイピングを行う時に、どのようにして外部サーバからHTTPで通信する方法があるでしょうか。

ひとつはfile_get_contents関数を使用する方法です。
URLを引数に入れるだけで、相手サーバのコンテンツを取得することが出来るので、初心者でも簡単に外部サーバと通信することができます。しかし、POSTデータを送りたい時やエラー時の例外処理、一定時間経ってもレスポンスが返ってこないときのタイムアウト処理などを考慮する必要がある場合は簡単に実装できません。

ふたつめはcurl系関数を使用する方法です。
curl系関数を使用すると、上記のような問題はオプションを設定することによって実装が可能になりますが、こちらもcurlハンドラを操作することに慣れていない人にとっては簡単に実装することはできません。

Guzzleを使うと初心者でも簡単に高度なHTTP通信を行うことができます。

公式サイト:http://docs.guzzlephp.org
GitHub:https://github.com/guzzle/guzzle

Guzzleの導入方法

Composerのインストールコマンドは下記です。

composer require guzzlehttp/guzzle

または、composer.jsonに下記を記載して、composer updateを実行します。

{
    "require": {
        "guzzlehttp/guzzle": "^6.3"
    }
}
2018/10/22時点の最新バージョンは6.3.3です。

Guzzleの基本的な使い方

基本的な使い方はクライアントとなるインスタンスを作成してリクエストを送ると、レスポンスが含まれるインスタンスを受け取ることができるというシンプルな作りです。

まずは簡単なサンプルを見てみましょう。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

// インスタンス作成
$client = new Client();

// リクエスト送信
$response = $client->request('GET', 'https://technoledge.net');

// レスポンス本文の取得
echo $response->getBody()->getContents();

ソースを見ただけでも何をしているのか大体分かると思うのですが、テクナレジのトップページにリクエストを送り、レスポンスのHTMLを出力している処理です。

リクエスト送信の部分は

$response = $client->get('https://technoledge.net')

と記述することもできます。

受け取ったレスポンスからHTML部分(レスポンス本文)を取得するために

$response->getBody()->getContents()

と少し遠回りな処理になっているように見えますが、これはPSR-7のレスポンスに従った仕様に従っているためで、ストリームからHTMLを取得するという形式になっています。

これくらいの処理であれば、Guzzleを使わずでも容易に実装ができる処理なので、次はPOSTするときの処理を見てみましょう。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

// インスタンス作成
$client = new Client();

// リクエスト送信
$options = [
    // POSTデータは"form_params"に記載
    'form_params' => [
        'name' => 'tak-solder',
        'job' => 'engineer',
        'language' => [
            'php',
            'javascript'
        ]
    ]
];
$response = $client->request('POST', 'https://technoledge.net', $options);

// レスポンスボディの取得
echo $response->getBody()->getContents();

先ほどのGETの部分をPOSTにするのは予想通りだと思うのですが、ここでrequestメソッドの第3引数の$optionsが出てきました。$optionsの連想配列の中にPOSTデータを入れるのですが、$optionsでは様々な設定を行うことができるので詳しくは後述します。

POSTと同様にPUTやDELETEといったメソッドも、もちろん使用可能です。

ヘッダー情報の設定・取得

リクエストを送るときには、ユーザーエージェントやリファラーのようなヘッダー情報を付加させてリクエストを送りたいこともあると思います。そういった場合も、$optionsのheadersに書くだけです。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

// インスタンス作成
$client = new Client();

// オプション設定
$options = [
    // リクエストヘッダー
    'headers' => [
        // ユーザーエージェント
        'user-agent' => 'technoledge/1.0',
        // リファラー
        'referer' => 'https://www.mfro.net',
    ],
];

// リクエスト送信
$response = $client->request('GET', 'https://technoledge.net', $options);

レスポンス時もレスポンスコードや何かのレスポンスヘッダーを取得したいことがあると思います。これらはレスポンスのインスタンスから取得することができます。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

// インスタンス作成
$client = new Client();

// リクエスト送信
$response = $client->request('GET', 'https://technoledge.net', $options);

// ステータスコード
echo $response->getStatusCode(); // 200

// ヘッダー情報
echo $response->getHeaderLine('content-type'); // text/html; charset=utf-8

オプション設定

上記以外にもGuzzleではリクエスト時の様々なオプションが存在します。その中から、比較的使いそうなものだけいくつかピックアップして紹介します。

<?php

require_once __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

// インスタンス作成
$client = new Client();

$options = [
    // Basic認証
    'auth' => ['user', 'password'],
    
    // プロキシ設定
    'proxy' => 'tcp://localhost:8080',

    // SSL証明書を検証しない
    'verify' => false,

    // タイムアウト
    'connect_timeout' => 0.5, // コネクション時のタイムアウト
    'timeout' => 3.5,         // リクエストのタイムアウト
];

// リクエスト送信
$response = $client->request('GET', 'https://technoledge.net', $options);

Basic認証はauthの中が連想配列ではなく、単純な添字配列でユーザー名、パスワードの順に入力するという点に注意してください。SSL証明書を検証しない設定はサーバ側でオレオレ証明書を使用している際に有効ですが、プライベートネットワーク以外ではLet'sEncryptを利用することをお勧めします。

まとめ

GuzzleにはGETやPOSTなどのHTTPリクエストを多彩なオプションとともに生成し、レスポンスを取得できることが分かりました。今回紹介した機能以外にも非同期で複数のリクエストを送信することもできるので、別の機会に書きたいと思います。