Blog

Rastreador Elástico de Sitio web Abierto como código

Aprende a usar GitHub Actions para gestionar configuraciones de Elastic Open Crawler, de modo que cada vez que enviemos cambios al repositorio, los cambios se apliquen automáticamente a la instancia desplegada del rastreador.

Con Elastic Open Sitio web Crawler y su arquitectura basada en CLI, tener configuraciones de rastreador versionado y una pipeline CI/CD con pruebas locales ahora es bastante sencillo de lograr.

Tradicionalmente, gestionar los rastreadores era un proceso manual y propenso a errores. Implicaba editar configuraciones directamente en la interfaz y luchar con clonar configuraciones de rastreo, retrocesos, versionear y más. Tratar las configuraciones de rastreadores como código resuelve esto al proporcionar los mismos beneficios que esperamos en el desarrollo de software: repetibilidad, trazabilidad y automatización.

Este flujo de trabajo facilita la incorporación del Open Sitio web Crawler a tu pipeline CI/CD para rollbacks, copias de seguridad y migraciones, tareas que eran mucho más complicadas con los Elastic Crawlers anteriores, como el Elastic Sitio web Crawler o el App Search Crawler.

En este artículo, vamos a aprender cómo:

  • Gestiona nuestras configuraciones de rastreo usando GitHub

  • Tener una configuración local para probar pipelines antes de desplegar

  • Crea una configuración de producción para ejecutar el rastreador sitio web con nuevos ajustes cada vez que enviemos cambios a nuestra rama principal

Puedes encontrar el repositorio de proyectos aquí. Según escribo, estoy usando Elasticsearch 9.1.3 y Open Sitio web Crawler 0.4.2.

Prerrequisitos

  • Escritorio Docker

  • Instancia de Elasticsearch

  • Máquina virtual con acceso SSH (por ejemplo, AWS EC2) y Docker instalados

Pasos

  1. Estructura de carpetas

  2. Configuración del orugador

  3. Docker-compose (entorno local)

  4. Acciones en Github

  5. Pruebas locales

  6. Desplegando a la producción

  7. Realización de cambios y re-despliegue

Estructura de carpetas

Para este proyecto, tendremos la siguiente estructura de archivos:

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

Configuración del orugador

Bajo crawler-config.yml, pondremos lo siguiente:

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

Esto se rastreará desde https://sitio web-scraping.dev/products, un sitio simulado de productos. Solo rastrearemos las tres primeras páginas del producto. La configuración max_crawl_depth evitará que el rastreador descubra más páginas de las definidas como seed_urls al no abrir los enlaces que contienen.

Elasticsearch host y api_key se llenarán dinámicamente dependiendo del entorno en el que ejecutemos el script.

La página del producto de "Box of Chocolate Candy" del dominio sitio web-scraping.dev, un sitio simulado para probar sitio web scraping. La página muestra el título del producto, la imagen, la descripción y los elementos HTML para los botones de precio y compra.

Docker-compose (entorno local)

Para la docker-compose.yml, local desplegaremos el rastreador y un único clúster Elasticsearch + Kibana, para poder visualizar fácilmente los resultados del rastreo antes de desplegarlos en producción.

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

Fíjate en cómo el rastreador espera hasta que Elasticsearch esté listo para ejecutar.

Acciones en Github

Ahora necesitamos crear una acción en GitHub que copie la nueva configuración y ejecute el rastreador en nuestra máquina virtual en cada envío a main. Esto garantiza que siempre tengamos la última configuración desplegada, sin tener que entrar manualmente en la máquina virtual para actualizar archivos y ejecutar el rastreador. Vamos a usar AWS EC2 como proveedor de máquinas virtuales.

El primer paso es agregar el host (VM_HOST), el usuario de la máquina (VM_USER), la clave SSH RSA (VM_KEY), el host de Elasticsearch (ES_HOST) y la clave API de Elasticsearch (ES_API_KEY) a los secretos de acción de GitHub:

Una página de configuración de "acciones, secretos y variables", que muestra secretos del repositorio como VM_HOST, VM_KEY y VM_USER dentro de una interfaz sitio web.

De este modo, la acción podrá acceder a nuestro servidor para copiar los archivos nuevos y ejecutar el rastreo.

Ahora, creemos nuestro archivo .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

Esta acción ejecutará los siguientes pasos cada vez que empujemos cambios en el archivo de configuración del rastreador:

  1. Llenar el host y la clave API de Elasticsearch en la configuración de yml

  2. Copia la carpeta config a nuestra máquina virtual

  3. Conéctate vía SSH a nuestra máquina virtual

  4. Ejecuta el rastreo con la configuración que acabamos de copiar del repositorio

Pruebas locales

Para probar nuestro rastreador localmente, creamos un script bash que llena el host de Elasticsearch con el local de Docker y comienza un rastreo. Puedes ejecutar ./local.sh para ejecutarlo.

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

Veamos Kibana DevTools para confirmar que el web-crawler-index se rellenó correctamente:

Kibana DevTools para confirmar que el índice del rastreador sitio web está configurado correctamente.

Desplegando a la producción

Ahora estamos listos para enviar a la rama principal, que desplegará el rastreador en tu máquina virtual y comenzará a enviar registros a tu instancia Serverless Elasticsearch.

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

Esto activará la Acción de GitHub, que ejecutará el script de despliegue dentro de la máquina virtual y comenzará a rastrear.

Puedes confirmar que la acción se ejecutó yendo al repositorio de GitHub y visitando la pestaña "Acciones":

Las acciones se despliegan en la pestaña EC2 de un repositorio de GitHub.

Realización de cambios y re-despliegue

Algo que quizá notaste es que el price de cada producto forma parte del cuerpo del documento. Lo ideal sería almacenar el precio en un campo aparte para poder aplicar filtros sobre él.

Vamos a agregar este cambio al archivo crawler.yml para usar reglas de extracción que extraigan el precio de la clase CSS de 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

También vemos que el precio incluye un signo de dólar ($), que debemos eliminar si queremos hacer consultas por rango. Podemos usar una canalización de ingesta para eso. Ten en cuenta que lo estamos haciendo referencia en nuestro nuevo archivo de configuración del rastreador arriba:

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

Podemos ejecutar ese comando en nuestro clúster de Elasticsearch en producción. Para el desarrollo, al ser efímero, podemos hacer que la creación de pipeline forme parte del archivo docker-compose.yml agregando el siguiente servicio. Ten en cuenta que también agregamos un depends_on al servicio de rastreo para que empiece después de que la tubería se creó con éxito.

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

Ahora vamos a ejecutar `./local.sh` para ver el cambio localmente:

Estoy usando './local.sh' para ver el cambio de precio localmente.

¡Bien! Ahora impulsemos el cambio:

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

Para confirmar que todo funciona, puedes comprobar tu Kibana de producción, que debería reflejar los cambios y mostrar el precio como un nuevo campo sin el signo del dólar.

Conclusión

El Elastic Open Sitio web Crawler te permite gestionar tu rastreador como código, lo que significa que puedes automatizar toda la pipeline —desde el desarrollo hasta el despliegue— y agregar entornos locales efímeros y pruebas programáticas contra los datos rastreados, por nombrar algunos ejemplos.

Se te invita a clonar el repositorio oficial y empezar a indexar tus propios datos usando este flujo de trabajo. También puedes leer este artículo para aprender a realizar búsqueda semántica en índices producidos por el rastreador.

Contenido relacionado

Visualización de campos en un índice de Elasticsearch

Kofi Bartlett

Cómo ingerir datos en Elasticsearch a través de Kafka

Andre Luiz

Guion Ruby en Logstash

Dai Sugimori

Plantillas de índice en Elasticsearch: Cómo usar plantillas componibles

Kofi Bartlett

Cómo mostrar los campos de un índice de Elasticsearch

JD Armada

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo