ブログ

Elastic Open Web Crawler をコードとして

GitHub Actions を使用して Elastic Open Crawler の構成を管理する方法を学びます。これにより、リポジトリに変更をプッシュするたびに、その変更がクローラーのデプロイされたインスタンスに自動的に適用されます。

Check out the different ways to ingest data into Elasticsearch and dive into practical examples to try something new.

Elasticsearch is packed with new features to help you build the best search solutions for your use case. Start a free cloud trial or try Elastic on your local machine now.

Elastic Open Web Crawlerとその CLI 駆動型アーキテクチャを使用すると、バージョン管理されたクローラー構成とローカル テストを備えた CI/CD パイプラインを実現するのが非常に簡単になります。

従来、クローラーの管理は手動で行われ、エラーが発生しやすいプロセスでした。これには、UI で直接構成を編集することや、クロール構成の複製、ロールバック、バージョン管理などに苦労することが含まれていました。クローラー構成をコードとして扱うことで、ソフトウェア開発で期待されるのと同じ利点(再現性、追跡可能性、自動化)が得られ、この問題が解決されます。

このワークフローにより、ロールバック、バックアップ、移行などのタスクを CI/CD パイプラインに Open Web Crawler を簡単に組み込むことができるようになります。これらのタスクは、Elastic Web Crawler や App Search Crawler などの以前の Elastic Crawler では非常に困難でした。

この記事では、次の方法を学習します。

  • GitHubを使用してクロール設定を管理する

  • デプロイ前にパイプラインをテストするためのローカルセットアップを用意する

  • メインブランチに変更をプッシュするたびに、新しい設定でウェブクローラーを実行するための本番環境設定を作成します。

プロジェクトのリポジトリはこちらです執筆時点では、Elasticsearch 9.1.3とOpen Web Crawler 0.4.2を使用しています。

要件

  • Dockerデスクトップ

  • Elasticsearchインスタンス

  • SSH アクセス(AWS EC2 など)と Docker がインストールされた仮想マシン

ステップ

  1. フォルダ構造

  2. クローラー構成

  3. Docker-compose ファイル (ローカル環境)

  4. Githubアクション

  5. ローカルテスト

  6. 本番環境へのデプロイ

  7. 変更と再展開

フォルダ構造

このプロジェクトでは、次のファイル構造になります。

├── docker-compose.yml # Local elasticsearch + crawler
├── config/crawler-config.yml # Crawler config
├── .github/workflows/deploy.yml # GH Action to deploy changes
├── local.sh # Script to run our local crawler

クローラー構成

crawler-config.yml,の下に次の内容を入力します。

output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3

これは、製品の模擬サイトであるhttps://web-scraping.dev/productsからクロールします。最初の 3 つの製品ページのみをクロールします。max_crawl_depth設定により、クローラーはseed_urlsとして定義されたページよりも多くのページを検出することがなくなり、それらのページ内のリンクを開かなくなります。

Elasticsearch hostapi_keyは、スクリプトを実行している環境に応じて動的に設定されます。

Docker-compose ファイル (ローカル環境)

ローカルdocker-compose.yml,には、クローラーと単一の Elasticsearch クラスター + Kibana をデプロイして、本番環境にデプロイする前にクロール結果を簡単に視覚化できるようにします。

services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports:
      - "9200:9200"
    networks: [esnet]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 5s
      timeout: 5s
      retries: 10

  kibana:
    image: docker.elastic.co/kibana/kibana:9.1.3
    environment:
      - ELASTICSEARCH_HOSTS=http://es01:9200
    ports:
      - "5601:5601"
    networks: [esnet]
    depends_on: [es01]

  crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    stdin_open: true
    tty: true

networks:
  esnet:
    driver: bridge

Elasticsearch の実行準備ができるまでクローラーが待機する方法に注意してください。

Githubアクション

ここで、新しい設定をコピーし、メインにプッシュするたびに仮想マシンでクローラーを実行する GitHub アクションを作成する必要があります。これにより、手動で仮想マシンにアクセスしてファイルを更新し、クローラーを実行する必要がなくなり、常に最新の構成が展開されます。仮想マシンプロバイダーとして AWS EC2 を使用します。

最初のステップは、ホスト ( VM_HOST )、マシン ユーザー ( VM_USER )、SSH RSA キー ( VM_KEY )、Elasticsearch ホスト ( ES_HOST )、Elasticsearch API キー ( ES_API_KEY ) を GitHub Action シークレットに追加することです。

この方法により、アクションはサーバーにアクセスして新しいファイルをコピーし、クロールを実行できるようになります。

それでは、 .github/workflows/deploy.ymlファイルを作成しましょう。

name: Deploy

on:
  push:
    branches: [main]

jobs:
  Deploy:
    name: Deploy to EC2
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5

      - name: Deploy crawler
        env:
          HOSTNAME: ${{ secrets.VM_HOST }}
          USER_NAME: ${{ secrets.VM_USER }}
          PRIVATE_KEY: ${{ secrets.VM_KEY }}
          ES_HOST: ${{ secrets.ES_HOST }}
          ES_API_KEY: ${{ secrets.ES_API_KEY }}
        run: |
          # Save private key
          echo "$PRIVATE_KEY" > private_key
          chmod 600 private_key

          # Generate final config locally
          envsubst < config/crawler-config.yml > config/crawl-config-final.yml

          # Copy the config folder to VM
          scp -o StrictHostKeyChecking=no -i private_key -r config ${USER_NAME}@${HOSTNAME}:~/config

          # SSH into VM and run crawler
          ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} << EOF
            docker run --rm \
              -v ~/config:/config \
              docker.elastic.co/integrations/crawler:latest jruby \
              bin/crawler crawl /config/crawl-config-final.yml
          EOF

このアクションは、クローラー構成ファイルに変更をプッシュするたびに、次の手順を実行します。

  1. yml config に Elasticsearch ホストと API キーを入力します。

  2. configフォルダをVMにコピーします

  3. SSH経由でVMに接続します

  4. リポジトリからコピーした設定でクロールを実行します

ローカルテスト

クローラーをローカルでテストするために、Docker からローカルのものを Elasticsearch ホストに入力してクロールを開始する bash スクリプトを作成しました。./local.shを実行して実行できます。

#!/bin/bash

# Exit on any error
set -e

# Load environment variables
export ES_HOST="http://es01:9200"

# Generate final crawler config
envsubst < ./config/crawler-config.yml > ./config/crawl-config-final.yml

# Bring everything up
docker compose up --build

Kibana DevTools を見て、 web-crawler-indexが正しく入力されていることを確認しましょう。

本番環境へのデプロイ

これで、メイン ブランチにプッシュする準備が整いました。これにより、仮想マシンにクローラーがデプロイされ、Serverless Elasticsearch インスタンスにログの送信が開始されます。

git add .
git commit -m "First commit"
git push

これにより、GitHub アクションがトリガーされ、仮想マシン内でデプロイ スクリプトが実行され、クロールが開始されます。

アクションが実行されたかどうかを確認するには、GitHub リポジトリにアクセスして「アクション」タブにアクセスします。

変更と再展開

お気づきかもしれませんが、各製品のpriceはドキュメントの本文フィールドの一部です。価格を別のフィールドに保存して、それに対してフィルターを実行できるようにするのが理想的です。

抽出ルールを使用してproduct-price CSS クラスから価格を抽出するために、 crawler.ymlファイルに次の変更を追加してみましょう。

output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
  # Index ingest pipeline to process documents before indexing          
  pipeline_enabled: true
  pipeline: pricing-pipeline

domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3
    extraction_rulesets:
      - url_filters:
          - type: ends
            pattern: /product/*
        rules:
          - action: extract
            field_name: price
            selector: .product-price
            join_as: string
            source: html

また、価格にはドル記号 ( $ ) が含まれていますが、範囲クエリを実行する場合はこれを削除する必要があります。そのために、取り込みパイプラインを使用できます。上記の新しいクローラー構成ファイルでこれを参照していることに注意してください。

PUT _ingest/pipeline/pricing-pipeline
{
  "processors": [
    {
      "script": {
        "source": """
                ctx['price'] = ctx['price'].replace("$","")
            """
      }
    }
  ]
}

このコマンドは本番環境の Elasticsearch クラスターで実行できます。開発用の場合は一時的なものなので、次のサービスを追加することで、パイプライン作成部分をdocker-compose.ymlファイルにすることができます。パイプラインが正常に作成された後にクローラー サービスが起動するように、 depends_onもクローラー サービスに追加されていることに注意してください。

 crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    depends_on:
      pipeline-init:
        condition: service_completed_successfully
    stdin_open: true
    tty: true  


  pipeline-init:
    image: curlimages/curl:latest
    depends_on:
      es01:
        condition: service_healthy
    networks: [esnet]
    entrypoint: >
        sh -c "
        echo 'Creating ingest pipeline...';
        curl -s -X PUT http://es01:9200/_ingest/pipeline/pricing-pipeline \\
          -H 'Content-Type: application/json' \\
          -d '{\"processors\":[{\"script\":{\"source\":\"ctx.price = ctx.price.replace(\\\"$\\\", \\\"\\\")\"}}]}';
        echo 'Pipeline created!';
        "

次に、 `./local.sh`を実行してローカルで変更を確認してみましょう。

素晴らしい!変更をプッシュしてみましょう。

git add crawler-config.yml
git commit -m "added price CSS selector"
git push

すべてが機能していることを確認するには、本番環境の Kibana をチェックします。変更が反映され、価格がドル記号なしの新しいフィールドとして表示されるはずです。

まとめ

Elastic Open Web Crawler を使用すると、クローラーをコードとして管理できるため、開発からデプロイメントまでのパイプライン全体を自動化したり、一時的なローカル環境を追加したり、クロールされたデータに対してプログラムでテストを実行したりすることができます。

公式リポジトリのクローンを作成し、このワークフローを使用して独自のデータのインデックス作成を開始してください。クローラーによって生成されたインデックスに対してセマンティック検索を実行する方法については、この記事を読むこともできます。

関連記事

TSDSとILMが出会うとき:遅延データを拒否しない時系列データストリームの設計

Bret Wortman

Elasticsearchインデックスのフィールドを表示する方法

JD Armada

LogstashでのRubyスクリプト

Dai Sugimori

Elasticsearchインデックスのフィールドを表示する

Kofi Bartlett

Elasticsearch フィールドをインデックスから除外する

Kofi Bartlett