几乎可以抓取和解析在线上可用的任何数据。本文示例将展示从 JSON 中提取数据,这可能是最简单的。有些软件包可帮助解析其他格式,例如 HTML、XML、PDF、CSV、Excel 甚至是借助 AI 技术的图像。
在荷兰,有一个网站 (https://www.verlorenofgevonden.nl) 用于跟踪丢失和扣押的自行车。通过这个项目,我们将获取在该网站上发现的相关数据,以对原始数据进行一些分析。
该网站 的屏幕截图
入门指南
如果你已经熟悉此部分,则可以跳过。我们将遵循官方文档创建一个带有 Docker 的 Laravel 项目。我正在 MacOS 上进行此操作,因此请参阅文档,因为你的说明可能针对 Linux 或 Windows 有所不同。
创建项目
导航到你的项目目录并创建一个新项目。由于它将在本地拉取和构建容器,所以这可能需要一些时间。
curl -s "https://laravel.build/laravel-scraper" | bash
启动环境
导航到新目录并启动环境。建议创建别名,请参见 文档。
cd laravel-scraper && sail up -d --build
# if you get a response like "command not found" you should
# learn to do as advised
一旦应用程序的 Docker 容器已启动,你可以在 Web 浏览器中访问应用程序,网址为 http://localhost。你应该会看到默认的起始页面。
默认的起始页面
依赖项
我们将使用一些基本的依赖项来帮助我们入门。
Laravel Actions
对于这个项目,我决定尝试使用 Laravel Actions。这不是必需的,但看起来是正确的工具。
sail composer require lorisleiva/laravel-actions
Guzzle HTTP 客户端
Laravel 提供了一个简洁的 API,围绕 Guzzle HTTP 客户端,允许你快速地进行出站 HTTP 请求,以与其他 Web 应用程序进行通信。
sail composer require guzzlehttp/guzzle
Laravel Pint (可选)
Laravel Pint 是一个面向极简主义者的 PHP 代码风格修复程序。Pint 建立在 PHP-CS-Fixer 之上,使得确保你的代码风格保持干净和一致变得简单。
sail composer require laravel/pint --dev
核心逻辑
从网站获取数据非常简单。你可以按照一些基本步骤来应用此方法到你的用例中。
- 手动浏览网站。
- 检查网络日志以确定网站如何获取数据,最理想的情况是找到一些可直接定位的 API 请求。
- 使用类似 Postman 的工具手动复制请求,以了解数据的流程、参数和形状。
模式
为了本指南的简单起见,我们将仅创建一个 Bicycle
模型来存储数据。使用以下命令在终端中生成模型和迁移。
sail artisan make:model Bicycle --migration
class Bicycle extends Model
{
protected $fillable = [
'object_number',
'type',
'sub_type',
'brand',
'color',
'description',
'city',
'storage_location',
'registered_at',
];
protected $casts = [
'registered_at' => 'datetime',
];
}
文件: database/migrations/create_bicycles_table.php
return new class extends Migration
{
public function up(): void
{
Schema::create('bicycles', function (Blueprint $table) {
$table->id();
$table->string('object_number', 32)->unique();
$table->string('type', 32);
$table->string('sub_type', 32);
$table->string('brand', 32);
$table->string('color', 32);
$table->text('description');
$table->string('city', 32);
$table->string('storage_location', 64);
$table->dateTime('registered_at');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('bicycles');
}
};
运行迁移
sail artisan migrate
爬虫
我们将创建一个单一类,使用 Laravel Actions 来包含所需的逻辑。示例命令将专注于获取最基本的数据。你可以随时扩展此功能并深入挖掘。
例如,你可以从描述中推断出唯一的“车架号”,以检查单辆自行车被扣押了多少次。
文件: app/Actions/Scrapers/FetchBicycles.php
class FetchBicycles
{
use AsAction;
public string $commandSignature = 'scraper:fetch-bicycles';
public string $commandDescription = 'Fetch impounded bicycles from www.verlorenofgevonden.nl';
public function asCommand(Command $command): void
{
$this->handle($command);
}
public function handle(Command $command): void
{
$this->fetch($command, now()->subMonth(), now());
}
private function fetch(
Command $command,
Carbon $dateFrom,
Carbon $dateTo,
int $from = 0
): void {
$response = Http::acceptJson()->get('https://verlorenofgevonden.nl/scripts/ez.php', [
'site' => 'nl',
'q' => 'fietsendepot',
'date_from' => $dateFrom->format('d-m-Y'),
'date_to' => $dateTo->format('d-m-Y'),
'timestamp' => now()->timestamp,
'from' => $from,
]);
$hits = collect($response->json('hits.hits'));
if ($hits->isEmpty()) {
$command->info('Done processing');
return;
}
$upserts = collect();
foreach ($hits as $hit) {
$registeredAt = Carbon::parse(data_get($hit, '_source.RegistrationDate'));
$upserts->push([
'object_number' => data_get($hit, '_source.ObjectNumber'),
'type' => data_get($hit, '_source.Category'),
'sub_type' => data_get($hit, '_source.SubCategory'),
'brand' => data_get($hit, '_source.Brand'),
'color' => data_get($hit, '_source.Color'),
'description' => data_get($hit, '_source.Description'),
'city' => data_get($hit, '_source.City'),
'storage_location' => data_get($hit, '_source.StorageLocation.Name'),
'registered_at' => $registeredAt,
]);
}
Bicycle::upsert($upserts->toArray(), ['object_number'], [
'type',
'sub_type',
'brand',
'color',
'description',
'city',
'storage_location',
'registered_at',
]);
$total = $from + $upserts->count();
$command->info(sprintf('Processed %d results', $total));
$this->fetch($command, $dateFrom, $dateTo, $total);
}
}
class Kernel extends ConsoleKernel
{
protected $commands = [
FetchBicycles::class,
];
// ...
}
运行命令
sail artisan scraper:fetch-bicycles
运行命令的示例
数据样例
对历史数据进行深入分析
由于我自行车被没收了两次,所以我受到启发从该网站中抓取数据并检查统计数据。事实上,我两次都是非法停车。
从抓取到的数据中发现了一些有趣的结果,我从中生成了以下图表。请注意,这些图表是在2022年10月1日生成的。
自2018年以来每个季度城市的没收情况
自2021年以来阿姆斯特丹与总数的比较
每个月城市的没收情况
自2018年以来没收罚款的估计金额
想法:自行车拍卖应用
我相信在荷兰没收自行车市场上存在一些空白。该网站帮助社区找回他们丢失或被没收的自行车,并允许你通过支付罚款和运费来获得它们。
阿姆斯特丹的自行车库 — 请查看源
荷兰的自行车库里堆满了成千上万的自行车。许多人从未取回他们没收的自行车,导致了不断增长的废弃自行车。
有人应该创建一个简单的应用程序,将这些废弃自行车拍卖出去。获取数据非常简单,正如本指南所证明的那样。为了闭环,允许所有者设定一个最低竞价和/或截止日期。所有者可以在竞价成功后通过邮寄将钥匙发送给买家,而标准的交付过程可以用来将自行车交付给新所有者。
GitHub - HendrikPrinsZA/laravel-scraper: Simple web scraper built with Laravel Actions
评论(0)