Articles de blog

Elastic Open Web Crawler en tant que code

Apprenez à utiliser les actions GitHub pour gérer les configurations d'Elastic Open Crawler, de sorte que chaque fois que nous apportons des modifications au référentiel, celles-ci sont automatiquement appliquées à l'instance déployée du crawler.

Avec Elastic Open Web Crawler et son architecture pilotée par CLI, il est désormais assez simple d'avoir des configurations de crawler versionnées et un pipeline CI/CD avec des tests locaux.

Traditionnellement, la gestion des robots d'indexation était un processus manuel et sujet aux erreurs. Il s'agissait de modifier les configurations directement dans l'interface utilisateur et de se débattre avec le clonage des configurations de crawl, le retour en arrière, la gestion des versions, etc. Traiter les configurations des robots comme du code résout ce problème en offrant les mêmes avantages que ceux que nous attendons du développement de logiciels : répétabilité, traçabilité et automatisation.

Ce flux de travail facilite l'intégration de l'Open Web Crawler dans votre pipeline CI/CD pour les retours en arrière, les sauvegardes et les migrations, tâches qui étaient beaucoup plus délicates avec les Elastic Crawlers précédents, tels que l'Elastic Web Crawler ou l'App Search Crawler.

Dans cet article, nous allons apprendre à.. :

  • Gérer nos configurations de crawl en utilisant GitHub

  • Disposer d'une installation locale pour tester les pipelines avant de les déployer

  • Créer une configuration de production pour exécuter le robot d'exploration avec de nouveaux paramètres à chaque fois que nous apportons des modifications à notre branche principale.

Vous pouvez trouver le dépôt du projet ici. Pour l'instant, j'utilise Elasticsearch 9.1.3 et Open Web Crawler 0.4.2.

Produits requis

  • Bureau Docker

  • Instance Elasticsearch

  • Machine virtuelle avec accès SSH (par exemple, AWS EC2) et Docker installé.

Étapes

  1. Structure des dossiers

  2. Configuration du robot

  3. Fichier Docker-compose (environnement local)

  4. Actions Github

  5. Tests au niveau local

  6. Déploiement vers prod

  7. Modifications et redéploiement

Structure des dossiers

Pour ce projet, nous aurons la structure de fichier suivante :

├── 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

Configuration du robot

Sous crawler-config.yml,, nous mettrons les éléments suivants :

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

Il s'agit d'une recherche à partir de https://web-scraping.dev/products, un site fictif pour les produits. Nous ne parcourrons que les trois premières pages du produit. Le paramètre max_crawl_depth empêchera le robot d'exploration de découvrir d'autres pages que celles définies comme seed_urls en n'ouvrant pas les liens qu'elles contiennent.

Elasticsearch host et api_key seront alimentés dynamiquement en fonction de l'environnement dans lequel nous exécutons le script.

La page produit pour "Boîte de bonbons au chocolat" provient du domaine web-scraping.dev, un site fictif pour tester le web scraping. La page affiche le titre du produit, l'image, la description et les éléments HTML pour les boutons de prix et d'achat.

Fichier Docker-compose (environnement local)

Pour le site local docker-compose.yml,, nous allons déployer le crawler et un seul cluster Elasticsearch + Kibana, de sorte que nous puissions facilement visualiser nos résultats de crawling avant de les déployer en production.

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

Notez que le crawler attend qu'Elasticsearch soit prêt à fonctionner.

Actions Github

Nous devons maintenant créer une action GitHub qui copiera les nouveaux paramètres et exécutera le crawler dans notre machine virtuelle à chaque poussée vers main. Ainsi, nous disposons toujours de la dernière configuration déployée, sans avoir à entrer manuellement dans la machine virtuelle pour mettre à jour les fichiers et exécuter le crawler. Nous allons utiliser AWS EC2 comme fournisseur de machines virtuelles.

La première étape consiste à ajouter l'hôte (VM_HOST), l'utilisateur de la machine (VM_USER), la clé RSA SSH (VM_KEY), l'hôte Elasticsearch (ES_HOST) et la clé API Elasticsearch (ES_API_KEY) aux secrets d'action GitHub :

Une page de configuration "actions, secrets et variables", affichant les secrets du référentiel tels que VM_HOST, VM_KEY et VM_USER dans une interface web.

De cette manière, l'action pourra accéder à notre serveur pour copier les nouveaux fichiers et exécuter le crawl.

Maintenant, créons notre fichier .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

Cette action exécutera les étapes suivantes à chaque fois que des modifications seront apportées au fichier de configuration du crawler :

  1. Renseigner l'hôte Elasticsearch et la clé API dans la configuration yml

  2. Copier le dossier config sur notre VM

  3. Se connecter via SSH à notre VM

  4. Exécuter le crawl avec la configuration que nous venons de copier depuis le repo

Tests au niveau local

Pour tester notre crawler localement, nous avons créé un script bash qui remplit l'hôte Elasticsearch avec l'hôte local de Docker et démarre un crawl. Vous pouvez lancer ./local.sh pour l'exécuter.

#!/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

Regardons Kibana DevTools pour confirmer que le site web-crawler-index a été correctement renseigné :

Code Kibana DevTools pour confirmer que le web-crawler-index est correctement configuré.

Déploiement vers prod

Nous sommes maintenant prêts à pousser vers la branche principale, ce qui déploiera le crawler dans votre machine virtuelle et commencera à envoyer des logs à votre instance Serverless Elasticsearch.

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

Cela déclenchera l'action GitHub, qui exécutera le script de déploiement dans la machine virtuelle et commencera l'exploration.

Vous pouvez confirmer que l'action a été exécutée en allant sur le dépôt GitHub et en visitant l'onglet "Actions" :

Les actions se déploient sur l'onglet EC2 dans un dépôt GitHub.

Modifications et redéploiement

Vous avez peut-être remarqué que le site price de chaque produit fait partie du champ "body" du document. L'idéal serait de stocker le prix dans un champ distinct afin de pouvoir utiliser des filtres.

Ajoutons cette modification au fichier crawler.yml afin d'utiliser des règles d'extraction pour extraire le prix de la classe CSS product-price:

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

Nous constatons également que le prix comprend un signe de dollar ($), que nous devons supprimer si nous voulons exécuter des requêtes de plage. Nous pouvons utiliser un pipeline d'acquisition pour cela. Notez que nous y faisons référence dans notre nouveau fichier de configuration du crawler ci-dessus :

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

Nous pouvons exécuter cette commande dans notre cluster Elasticsearch de production. Pour celui de développement, comme il est éphémère, nous pouvons intégrer la création du pipeline dans le fichier docker-compose.yml en ajoutant le service suivant. Notez que nous avons également ajouté un depends_on au service crawler afin qu'il démarre après la création réussie du pipeline.

 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!';
        "

Exécutons maintenant `./local.sh` pour voir les changements localement :

Exécution de `./local.sh` pour voir le prix changer localement.

C'est très bien ! Poussons maintenant la modification :

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

Pour confirmer que tout fonctionne, vous pouvez vérifier votre Kibana de production, qui devrait refléter les changements et afficher le prix comme un nouveau champ sans le signe du dollar.

Conclusion

Elastic Open Web Crawler vous permet de gérer votre crawler en tant que code, ce qui signifie que vous pouvez automatiser l'ensemble du pipeline - du développement au déploiement - et ajouter des environnements locaux éphémères et des tests sur les données explorées de manière programmatique, pour ne citer que quelques exemples.

Vous êtes invités à cloner le dépôt officiel et à commencer à indexer vos propres données à l'aide de ce flux de travail. Vous pouvez également lire cet article pour apprendre comment effectuer une recherche sémantique sur les index produits par le crawler.

Pour aller plus loin

Affichage des champs dans un index Elasticsearch

Kofi Bartlett

Comment ingérer des données dans Elasticsearch via Kafka ?

Andre Luiz

Scripting Ruby dans Logstash

Dai Sugimori

Modèles d'index dans Elasticsearch : Comment utiliser les modèles composables

Kofi Bartlett

Comment afficher les champs d'un index Elasticsearch ?

JD Armada

Prêt à créer des expériences de recherche d'exception ?

Une recherche suffisamment avancée ne se fait pas avec les efforts d'une seule personne. Elasticsearch est alimenté par des data scientists, des ML ops, des ingénieurs et bien d'autres qui sont tout aussi passionnés par la recherche que vous. Mettons-nous en relation et travaillons ensemble pour construire l'expérience de recherche magique qui vous permettra d'obtenir les résultats que vous souhaitez.

Jugez-en par vous-même