博客

作为代码的弹性开放式网络爬虫

了解如何使用 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 管道。

传统上,管理爬虫是一个手动且容易出错的过程。这涉及到直接在用户界面上编辑配置,以及克隆抓取配置、回滚、版本控制等问题。将爬虫配置视为代码可以解决这个问题,因为它提供了我们在软件开发中期待的相同优势:可重复性、可追溯性和自动化。

这种工作流程可以更轻松地将开放式网络爬虫带入您的 CI/CD 流水线,以进行回滚、备份和迁移--这些任务在使用早期的弹性爬虫(如弹性网络爬虫或应用程序搜索爬虫)时要棘手得多。

在本文中,我们将学习如何:

  • 使用 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. 部署到 prod

  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 抓取,这是一个产品模拟网站。我们只会抓取前三个产品页面。max_crawl_depth 设置将通过不打开其中的链接,防止爬虫发现比定义为seed_urls 的页面更多的页面。

Elasticsearchhostapi_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 Action,它将复制新设置,并在每次推送到 main 时在虚拟机中运行爬虫。这样就能确保我们始终部署有最新的配置,而无需手动进入虚拟机更新文件和运行爬虫。我们将使用 AWS EC2 作为虚拟机提供商。

第一步是将主机 (VM_HOST) 、机器用户 (VM_USER) 、SSH RSA 密钥 (VM_KEY) 、Elasticsearch 主机 (ES_HOST) 和 Elasticsearch API 密钥 (ES_API_KEY) 添加到 GitHub Action secrets 中:

这样,操作就能访问我们的服务器,将新文件复制过来并运行抓取。

现在,让我们创建.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 配置中填入 Elasticsearch 主机和 API 密钥

  2. 将配置文件夹复制到我们的虚拟机

  3. 通过 SSH 连接到我们的虚拟机

  4. 使用我们刚从 repo 复制的配置运行抓取程序

本地测试

为了在本地测试爬虫,我们创建了一个 bash 脚本,将 Elasticsearch 主机与 Docker 中的本地主机进行填充,然后开始爬行。您可以运行./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 的填充是否正确:

部署到 prod

现在,我们准备推送到主分支,这将在虚拟机中部署爬虫,并开始向无服务器 Elasticsearch 实例发送日志。

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

这将触发 GitHub 操作,在虚拟机中执行部署脚本并开始抓取。

您可以访问 GitHub 仓库并访问 "操作 "选项卡,确认操作已执行:

进行更改和重新部署

您可能已经注意到,每个产品的price 都是文档正文字段的一部分。如果能将价格存储在一个单独的字段中,我们就可以根据它运行筛选器,这将是最理想的。

让我们在crawler.yml 文件中添加这一更改,使用提取规则product-price CSS 类中提取价格:

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