🫥

Laravel(&Docker)でシンプルにクローラー処理を作ってみる※Mac M1

2023/01/24に公開

なぜ作ろうと思ったか?

Laravel使ったクローラー処理であんまり参考になるものがないなぁ(特にDockerまわりで)と思っていたのでやってみる。正直PHPでクローリング処理を実装するのもどうなんだろうなぁとおもいつつ、自社でPHP使うことも多いのでまぁいいか。

あとはmac m1 dockerでchromeが動かないのでchromiumでどうにかしたかったってのもある。

とりあえずまずはLaravelのインストール

※ mac m1で動くことを前提にしてます。

Laravel9を使うため、mac上にphp(8.2以上)、composerをインストール

brew install php
brew install composer

PHPへのパスを追加しておくこと(.zshrcとか)

export PATH="/opt/homebrew/opt/php/bin:$PATH"
export PATH="/opt/homebrew/opt/php/sbin:$PATH"

次にLaravelをインストール

composer create-project "laravel/laravel=9.*" crawler

次にdocker環境を構築していく

※ 自社のディレクトリ構成になっているので適宜読み替えてください。

app
docker
  └ dev
      └ app
       └ Dockerfile
    └ chromium
        └ Dockerfile
    └ docker-compose.yml

dev/app/Dockerfile

FROM php:8.2-fpm
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

COPY --from=composer:2.4 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git wget vim libicu-dev libzip-dev libonig-dev libcurl4-openssl-dev pkg-config procps && \
  apt-get -y install locales locales-all task-japanese && \
  apt-get -y install fonts-ipafont fonts-ipaexfont && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  mkdir /var/run/php-fpm && \
  mkdir /var/log/php && \
  docker-php-ext-install intl pdo_mysql zip && \
  docker-php-ext-install curl && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://repo.packagist.org

# timezone environment
RUN echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen ja_JP.UTF-8 && \
    dpkg-reconfigure locales && \
    /usr/sbin/update-locale LANG=ja_JP.UTF-8
ENV TZ Asia/Tokyo
ENV LANG ja_JP.UTF-8
ENV LANGUAGE "ja_JP:jp"
ENV LC_ALL ja_JP.UTF-8

# composer environment
# ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_NO_INTERACTION 1

COPY ./php.ini /usr/local/etc/php/php.ini

WORKDIR /project

dev/chromium/Dockerfile

FROM seleniarm/standalone-chromium

dev/docker-compose.yml

version: '3.9'

services:
  app:
    image: crawler
    container_name: crawler
    build:
      context: ./app
    volumes:
      - ../../.:/project
    networks:
      - crawler-network
  chromium:
    image: crawler.chromium
    container_name: crawler.chromium
    hostname: selenium
    build:
      context: ./chromium
    ports:
      - 4444:4444
      - 5900:5900
      - 7900:7900
    networks:
      - crawler-network

networks:
  crawler-network:
    name: crawler-network

ビルドと立ち上げ

cd crawler/docker/dev

docker compose build

docker compose up -d

立ち上がったらサンプルでスクリーンショットが取れるかどうか確認

まずphp-webdriverをインストール

docker compose exec -it app bash
composer require php-webdriver/webdriver

サンプルバッチを作成

php artisan make:command SeleniumBatch
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Chrome\ChromeDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Interactions\WebDriverActions;
use Facebook\WebDriver\WebDriverDimension;

class SeleniumBatch extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'batch:selenium-sample';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $driverPath = "http://selenium:4444/wd/hub";

        $options = new ChromeOptions();
        $options->addArguments([
            "disable-infobars",
            "--headless",
            "window-size=1920,1600",
        ]);

        $capabilities = DesiredCapabilities::chrome();
        $capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
        $driver = RemoteWebDriver::create($driverPath, $capabilities);

        $driver->get("https://www.google.co.jp/");
        $driver->wait(2)->until(WebDriverExpectedCondition::presenceOfElementLocated(WebDriverBy::name("q")));

        $element = $driver->findElement(WebDriverBy::name("q"))->sendKeys("うっかりさん 困った時の備忘録")->submit();
        $driver->wait(3)->until(WebDriverExpectedCondition::titleContains("うっかりさん"));

        $file = __DIR__."/sample.png";
        $driver->takeScreenshot($file);

        $driver->quit();

        return Command::SUCCESS;
    }
}

実行してみる

php artisan batch:selenium-sample

sample.pngができてGoogleの検索画面が取得できれいればOK!

さいごに

ほぼコピペでクローラー処理ができるかと思います。
RubyとかPythonは結構でてくるのですが、PHP系がほとんどなかった(古かったり、LaraDuskしかなかったり)なのでちょっと書いてみました。
ECSもこれで行けると思うので本番化してみてまた悩んだら記事にしてみようと思います。

参考サイト)
https://www.ukkari-san.net/seleniumlaravel8php-webdriverを動かす/

Discussion