🐄
study laravel #1
Scraping JOBを実装する
背景
とあるサイトの膨大なページをスクレイピングして必要な情報だけDBに保存したい。その後、必要な情報に対してRDBのクエリで検索したい。システムを置くサーバでは最小の処理時間を要求される。計算量の追求のみでは限界があるので、処理単位を細分化し、具体的には非同期処理で行いたい。
勉強の目的
- どのような状態をキューに入れることができるか
- 最上の期待値:実行待ちのJOBオブジェクトそのもの
- 次点の期待値:JOBの実行に必要なパラメタとそれを必要とするJOB名のセット
- 最低の期待値:単独のJOBの実行に必要なパラメタ
実装の細分化
- JOB稼働環境のセットアップ(JOB, RDB, migrate)
- ジョブ実行結果のModel作成(SampleJOBModel: url,content)
- サンプルジョブ作成(SampleJOB: getURIContents())
- ジョブ実行コントローラ作成
JOB稼働環境のセットアップ
laravelチュートリアル参照。あるいはChatGPTに教えてもらってください。
ジョブ実行結果のModel作成
SampleJobModelという適当な名前をつけたモデルを作成。
url :String, content :String とする。
php artisan make:model SampleJobModel
php artisan make:migration create_sample_job_models_table
public function up(): void
{
Schema::create('sample_job_models', function (Blueprint $table) {
$table->id();
$table->string('uri'); // uri列(文字列)
$table->text('content'); // content列(テキスト)
$table->timestamps();
});
}
php artisan migrate
自分用メモ:
LaravelはModelクラスに手を加える必要がない。コード補完がしたければIDEで工夫するのみ
サンプルジョブ作成
最も原始的なScrapingを実装するProcessSampleJob。
handle()の仕様は、指定URLに対してcurl GETした結果をすべて文字列としてSampleJobModelへ保存。
php artisan make:job ProcessSampleJob
public function handle(): void
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->uri);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPGET, true);
$response = curl_exec($ch);
curl_close($ch);
$model = new SampleJobModel;
$model->uri = $this->uri;
$model->content = $response;
$model->save();
}
ジョブ実行コントローラ作成
webのcontrollerでなくて良い。app/Console/Commandsでいい。Laravelのジョブを動かせれば何でもいい。
php artisan make:command DoSampleJob
public function handle()
{
$uri = "https://httpbin.org/delay/5";
ProcessSampleJob::dispatch($uri);
ProcessSampleJob::dispatch($uri);
ProcessSampleJob::dispatch($uri);
ProcessSampleJob::dispatch($uri);
ProcessSampleJob::dispatch($uri);
}
php artisan app:do-sample-job
# JOBSテーブルの1行分
{
"uuid": "7653dc8a-27cd-46ba-889a-0e5895776b3d",
"displayName": "App\\Jobs\\ProcessSampleJob",
"job": "Illuminate\\Queue\\CallQueuedHandler@call",
"maxTries": null,
"maxExceptions": null,
"failOnTimeout": false,
"backoff": null,
"timeout": null,
"retryUntil": null,
"data": {
"commandName": "App\\Jobs\\ProcessSampleJob",
"command": "O:25:\"App\\Jobs\\ProcessSampleJob\":1:{s:6:\"\u0000*\u0000uri\";s:27:\"https://httpbin.org/delay/5\";}"
}
}
php artisan queue:work
INFO Processing jobs from the [default] queue.
2024-01-04 06:32:44 App\Jobs\ProcessSampleJob ...... RUNNING
2024-01-04 06:32:50 App\Jobs\ProcessSampleJob ...... 6s DONE
2024-01-04 06:32:50 App\Jobs\ProcessSampleJob ...... RUNNING
2024-01-04 06:32:56 App\Jobs\ProcessSampleJob ...... 6s DONE
2024-01-04 06:32:56 App\Jobs\ProcessSampleJob ...... RUNNING
2024-01-04 06:33:03 App\Jobs\ProcessSampleJob ...... 6s DONE
2024-01-04 06:33:03 App\Jobs\ProcessSampleJob ...... RUNNING
2024-01-04 06:33:09 App\Jobs\ProcessSampleJob ...... 6s DONE
2024-01-04 06:33:09 App\Jobs\ProcessSampleJob ...... RUNNING
2024-01-04 06:33:15 App\Jobs\ProcessSampleJob ...... 5s DONE
# SampleJobModelテーブルの一行分
uri:
https://httpbin.org/delay/5
content:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"X-Amzn-Trace-Id": "Root=1-6596510d-3a03259e4bdf41e73f6c7109"
},
"origin": "211.2.76.67",
"url": "https://httpbin.org/delay/5"
}
最上の期待値を満たしたと思う。
次の課題は
- scraping結果からURLを抽出して、非同期の多段scraping
- php artisan queue:work をコマンドライン実行なしに定期実行する方法
Discussion