Blog

Elastic Open Web Crawler como código

Aprenda a usar o GitHub Actions para gerenciar as configurações do Elastic Open Crawler, de forma que, sempre que enviarmos alterações para o repositório, elas sejam aplicadas automaticamente à instância implantada do crawler.

Com o Elastic Open Web Crawler e sua arquitetura orientada por linha de comando, ter configurações de crawler versionadas e um pipeline de CI/CD com testes locais agora é bastante simples de se obter.

Tradicionalmente, o gerenciamento de rastreadores era um processo manual e propenso a erros. Isso envolvia editar configurações diretamente na interface do usuário e ter dificuldades com a clonagem de configurações de rastreamento, reversão, controle de versão e muito mais. Tratar as configurações do rastreador como código resolve isso, proporcionando os mesmos benefícios que esperamos no desenvolvimento de software: repetibilidade, rastreabilidade e automação.

Esse fluxo de trabalho facilita a integração do Open Web Crawler ao seu pipeline de CI/CD para reversões, backups e migrações — tarefas que eram muito mais complicadas com versões anteriores do Elastic Crawler, como o Elastic Web Crawler ou o App Search Crawler.

Neste artigo, vamos aprender como:

  • Gerencie nossas configurações de rastreamento usando o GitHub.

  • Ter um ambiente local para testar pipelines antes da implantação.

  • Criar um ambiente de produção para executar o rastreador web com novas configurações sempre que enviarmos alterações para nossa branch principal.

Você pode encontrar o repositório do projeto aqui. No momento em que escrevo, estou usando o Elasticsearch 9.1.3 e o Open Web Crawler 0.4.2.

Pré-requisitos

  • Docker Desktop

  • instância do Elasticsearch

  • Máquina virtual com acesso SSH (por exemplo, AWS EC2) e Docker instalado.

Etapas

  1. Estrutura de pastas

  2. Configuração do rastreador

  3. Arquivo Docker-compose (ambiente local)

  4. Ações do GitHub

  5. Testando localmente

  6. Implantação em produção

  7. Realizar alterações e redistribuir

Estrutura de pastas

Para este projeto, teremos a seguinte estrutura de arquivos:

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

Configuração do rastreador

Em crawler-config.yml, colocaremos o seguinte:

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

Este script irá extrair dados de https://web-scraping.dev/products, um site fictício para produtos. Iremos rastrear apenas as três primeiras páginas de produtos. A configuração max_crawl_depth impedirá que o rastreador descubra mais páginas do que as definidas como seed_urls , não abrindo os links dentro delas.

Elasticsearch host e api_key serão preenchidos dinamicamente dependendo do ambiente em que estamos executando o script.

Página do produto "Caixa de Balas de Chocolate" do domínio web-scraping.dev, um site simulado para testes de web scraping. A página exibe o título do produto, a imagem, a descrição e os elementos HTML para o preço e os botões de compra.

Arquivo Docker-compose (ambiente local)

Para o ambiente local docker-compose.yml, implantaremos o rastreador e um único cluster Elasticsearch + Kibana, para que possamos visualizar facilmente os resultados do rastreamento antes da implantação em produção.

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

Observe como o rastreador aguarda até que o Elasticsearch esteja pronto para ser executado.

Ações do GitHub

Agora precisamos criar uma ação do GitHub que copie as novas configurações e execute o rastreador em nossa máquina virtual a cada push para o repositório principal. Isso garante que sempre tenhamos a configuração mais recente implantada, sem precisar entrar manualmente na máquina virtual para atualizar arquivos e executar o rastreador. Vamos usar o AWS EC2 como provedor de máquinas virtuais.

O primeiro passo é adicionar o host (VM_HOST), o usuário da máquina (VM_USER), a chave SSH RSA (VM_KEY), o host do Elasticsearch (ES_HOST) e a chave da API do Elasticsearch (ES_API_KEY) aos segredos da ação do GitHub:

Uma página de configuração de "ações, segredos e variáveis", que exibe segredos do repositório, como VM_HOST, VM_KEY e VM_USER, em uma interface baseada na web.

Dessa forma, a ação poderá acessar nosso servidor para copiar os novos arquivos e executar a indexação.

Agora, vamos criar nosso arquivo .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

Essa ação executará os seguintes passos sempre que enviarmos alterações para o arquivo de configuração do rastreador:

  1. Preencha o arquivo de configuração YAML com o host e a chave da API do Elasticsearch.

  2. Copie a pasta de configuração para a nossa máquina virtual.

  3. Conecte-se à nossa VM via SSH.

  4. Execute o rastreamento com a configuração que acabamos de copiar do repositório.

Testando localmente

Para testar nosso rastreador localmente, criamos um script bash que popula o host do Elasticsearch com a versão local do Docker e inicia uma busca. Você pode executar ./local.sh para executá-lo.

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

Vamos verificar as Ferramentas de Desenvolvedor do Kibana para confirmar se o campo web-crawler-index foi preenchido corretamente:

Código do Kibana DevTools para confirmar se o índice do rastreador web está configurado corretamente.

Implantação em produção

Agora estamos prontos para enviar a alteração para a branch principal, o que implantará o crawler em sua máquina virtual e começará a enviar logs para sua instância do Elasticsearch Serverless.

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

Isso acionará a ação do GitHub, que executará o script de implantação na máquina virtual e iniciará a indexação.

Você pode confirmar se a ação foi executada acessando o repositório do GitHub e visitando a aba “Ações”:

As ações são implantadas na aba EC2 de um repositório GitHub.

Realizar alterações e redistribuir

Algo que você pode ter notado é que o price de cada produto faz parte do corpo do documento. O ideal seria armazenar o preço em um campo separado para que pudéssemos aplicar filtros a ele.

Vamos adicionar essa alteração ao arquivo crawler.yml para usar regras de extração para extrair o preço da 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

Também vemos que o preço inclui um sinal de dólar ($), que devemos remover se quisermos executar consultas de intervalo. Podemos usar um pipeline de ingestão para isso. Observe que estamos fazendo referência a ele em nosso novo arquivo de configuração do rastreador acima:

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

Podemos executar esse comando em nosso cluster Elasticsearch de produção. Para o desenvolvimento, como é efêmero, podemos fazer com que a criação do pipeline faça parte do arquivo docker-compose.yml adicionando o seguinte serviço. Observe que também adicionamos um depends_on ao serviço de rastreamento para que ele seja iniciado após a criação bem-sucedida do 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!';
        "

Agora vamos executar `./local.sh` para ver a alteração localmente:

Executando `./local.sh` para ver a alteração de preço localmente.

Ótimo! Agora vamos impulsionar a mudança:

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

Para confirmar se tudo funciona corretamente, você pode verificar seu Kibana de produção, que deverá refletir as alterações e mostrar o preço como um novo campo sem o símbolo de dólar.

Conclusão

O Elastic Open Web Crawler permite que você gerencie seu crawler como código, o que significa que você pode automatizar todo o pipeline — do desenvolvimento à implantação — e adicionar ambientes locais efêmeros e testes com os dados rastreados programaticamente, para citar alguns exemplos.

Você está convidado a clonar o repositório oficial e começar a indexar seus próprios dados usando este fluxo de trabalho. Você também pode ler este artigo para aprender como executar uma pesquisa semântica em índices produzidos pelo rastreador.

Conteúdo relacionado

Exibindo campos em um índice do Elasticsearch

Kofi Bartlett

Como ingerir dados no Elasticsearch através do Kafka

Andre Luiz

Scripting em Ruby no Logstash

Dai Sugimori

Criação de modelos de índice no Elasticsearch: Como usar modelos compostos

Kofi Bartlett

Como exibir os campos de um índice do Elasticsearch

JD Armada

Pronto para criar buscas de última geração?

Uma pesquisa suficientemente avançada não se consegue apenas com o esforço de uma só pessoa. O Elasticsearch é impulsionado por cientistas de dados, especialistas em operações de aprendizado de máquina, engenheiros e muitos outros que são tão apaixonados por buscas quanto você. Vamos nos conectar e trabalhar juntos para construir a experiência de busca mágica que lhe trará os resultados desejados.

Experimente você mesmo(a)