【Laravel】はじめてのAPI構築(第3回) 情報を取得してみる編
前回、非常に基礎的な形ではありましたが、URLを叩いてJSONデータが取得できるAPIが作成できました。
ただ初心者の方は、実践的なデータ投入であったり、DBからの取得であったりの具体的なイメージがある方が理解が進むと思うので、今回は実際にデータを入れてみます。
実は、気象庁のWEBサイトから天気情報を取得したり、Yahoo!さんのリアルタイムトレンドワードを取得したりしようかと思っていたのですが、他サービスに迷惑がかかるといけないので、本ブログ上で情報を取得することにしました。
題材としてはつまらないものですが、ご了承ください><
目的
ということで今回は、このsupilog(本ブログ)の右ナビゲーションにある、最近の投稿3件の情報(タイトルとリンクURL)を取得し、さらにAPIとして出力するというところまでやってみたいと思います。
インストール
composer require fabpot/goutte
HTTPクライアントのライブラリとして、Goutteを利用します。
コマンド生成
より実践的にということで、後々crontabにも設定しやすいようにコマンドを作成しようと思います。
php artisan make:command GetBlog
実行すると、api/app/Console/Commands/GetBlog.phpが生成されます。生成されたファイルを開いて編集します。
signature
このコマンドは、artisanコマンドとして実行できるようになります。このファイルを生成するときに使ったのもartisanコマンドです。
signatureを設定することで実行するコマンドを指定することが出来ます。やってみましょう。
protected $signature = 'command:name';
↓ 編集
protected $signature = 'blog:get';
これで、「php artisan blog:get」コマンドを実行すると、このクラスが実行されるようになります。
description
コマンドの説明文です。自分用に好きな説明を入力します。日本語でOK。
protected $description = '最近のブログ投稿を取得する';
__construct()
今回は修正なし
handle()
コマンドを実行したときに、行いたい処理をここに記述します。まずは簡単な命令を書いて実行できるか確かめてみましょう。
public function handle()
{
print "Command blog:get done.\n";
}
動作確認
artisanコマンドリスト
php artisan list
---------------------------------------------
...
blog
blog:get 最近のブログ投稿を取得する
...
---------------------------------------------
↑先程設定したコマンドと説明文が表示されていますね。
実行
php artisan blog:get
---------------------------------------------
Command blog:get done.
---------------------------------------------
print文で指定した文字列が出力されていれば成功です。
コマンド実装
DB準備
取得したデータを保管しておくテーブルを用意します。
php artisan make:migration create_blogs_table --create=blogs
migrationファイルが作成されるので、下記のように編集します。(api/database/migrations/YYYY_MM_DD_XXXXXX_create_blogs_table.php)
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTrendsTable extends Migration
{
public function up()
{
Schema::create('blogs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('blogtitle1');
$table->string('blogurl1');
$table->string('blogtitle2');
$table->string('blogurl2');
$table->string('blogtitle3');
$table->string('blogurl3');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('blogs');
}
}
修正したら下記コマンドを実行し適用させます。これでテーブルが作成されます。
php artisan migrate
---------------------------------------------
Migrating: 2021_12_08_133726_create_blogs_table
Migrated: 2021_12_08_133726_create_blogs_table (0.06 seconds)
---------------------------------------------
モデルも作成しておきます。
php artisan make:model Blog
データ取り込み
GetBlogコマンドの中身を実装します。難しことはやっていませんが、HTML上の要素を指定して値を取得して、最終的に先程作成したblogsテーブルに保存しています。
<?php
namespace App\Console\Commands;
use App\Blog;
use Goutte\Client;
use Illuminate\Console\Command;
use Symfony\Component\HttpClient\HttpClient;
class GetBlog extends Command
{
protected $signature = 'blog:get';
protected $description = '最近のブログ投稿を取得する';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$client = new Client(HttpClient::create([
'timeout' => 10
]));
$crawler = $client->request('GET', 'https://supilog.supisupi.com/');
$blog = new Blog();
// 3番目までのブログ情報取得
$blogdata = array();
for ($i = 0; $i < 3; $i ++) {
$tmp = array();
$article = $crawler->filter('.recent-list')
->filter('a')
->eq($i);
$tmp['url'] = $article->attr("href");
// タイトルのみ文字列の前後にあるスペースを除去
$tmp['title'] = preg_replace('/\A[\p{C}\p{Z}]++|[\p{C}\p{Z}]++\z/u', '', $article->text());
array_push($blogdata, $tmp);
}
$blog->blogtitle1 = $blogdata[0]['title'];
$blog->blogtitle2 = $blogdata[1]['title'];
$blog->blogtitle3 = $blogdata[2]['title'];
$blog->blogurl1 = $blogdata[0]['url'];
$blog->blogurl2 = $blogdata[1]['url'];
$blog->blogurl3 = $blogdata[2]['url'];
// DBに格納
$blog->save();
}
}
php artisan blog:get を実行して、エラーが出なければ処理は成功しています。
それではいよいよ、このデータを使って、APIの出力を行いましょう。
データ出力(API)
routeを修正
前回、/api/testというルートで、testアクションを実行していましたが、testという名前も忍びないので、更新しておきます。
<?php
use Illuminate\Http\Request;
Route::get ( '/blog', 'ApiController@blog' );
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ApiController extends Controller
{
public function blog(Request $request) {
// $ret = array();
// $ret['version'] = '1.0.0';
// $ret['result'] = 'success';
// return $ret;
}
}
trendアクション実装
ApiControllerのblogアクションを以下のように修正してみましょう。
<?php
namespace App\Http\Controllers;
use App\Blog;
use Carbon\Carbon;
use Illuminate\Http\Request;
class ApiController extends Controller
{
public function blog(Request $request)
{
// DBから最新データを取得
$blog = Blog::latest()->first();
// 値をセットする
$ret = array();
$ret['version'] = '1.0.0';
$dt = new Carbon($blog->created_at);
$ret['update_date'] = $dt->format('Y/m/d H:i:s');
$ret['blogs'] = array();
$ret['blogs'][0]['title'] = $blog->blogtitle1;
$ret['blogs'][0]['url'] = $blog->blogurl1;
$ret['blogs'][1]['title'] = $blog->blogtitle2;
$ret['blogs'][1]['url'] = $blog->blogurl2;
$ret['blogs'][2]['title'] = $blog->blogtitle3;
$ret['blogs'][2]['url'] = $blog->blogurl3;
return $ret;
}
}
確認
起動をして、http://127.0.0.1:8000/api/blogにアクセスしてみましょう。
なにやら記号が出ましたが、こちらは文字化けではなくエンコードされているだけなので、ちゃんと出力されています。
試しにこの文字列(JSON)を整形してみました。
{
"version": "1.0.0",
"update_date": "2021/12/09 02:59:46",
"blogs": [
{
"title": "【Laravel】はじめてのAPI構築(第2回) 環境構築+はじめてのAPI編",
"url": "https://supilog.supisupi.com/blog/is4aifa8ffpd/"
},
{
"title": "【Laravel】はじめてのAPI構築(第1回) 座学編",
"url": "https://supilog.supisupi.com/blog/ltuopp8vmr99/"
},
{
"title": "ダミー画像作成サービス – Dynamic Dummy Image Generator",
"url": "https://supilog.supisupi.com/blog/9ranotb1e4jb/"
}
]
}
しっかり出力されてます。今回の作業はこれで終了です。
情報を取得してみる まとめ
いかがでしたか。簡単ではありますが、「データの収集」から「APIでDBに保管されている最新データを出力する」ところまで、しっかり行ってみました。
今回データの取得は、artisanコマンドを利用しましたので、そのままcrontabに記述してコマンド実行させることが容易です。リアルタイムにデータが変化する場合などは短い間隔で定期実行してあげることで、定期的にデータベースが更新されていくことになり、それによってAPIが出力する内容も新しくなります。
【注意1】スクレイピングによって、他サービスからデータを利用する場合には、対象サービスの規約をよく読み、規約違反をすることのないように注意してください。
【注意2】スクレイピングは対象のHTMLが更新されることによりデータが取得できなくなることは、容易にあり得ることなので、実運用する場合にはエラーが検知できるようにする工夫も必要です。
次回は、APIの認証部分に触れていきたいと思います。←実はここが一番やりたかったところ(私も復習したくて。。