博客

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

了解如何使用 GitHub Actions 管理 Elastic Open Crawler 配置,以便每次向版本库推送变更时,这些变更都会自动应用到已部署的爬虫实例。

有了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 将根据我们运行脚本的环境动态填充。

"Box of Chocolate Candy 的产品页面" 来自 web-scraping.dev 域,这是一个用于测试 web scraping 的模拟网站。页面显示产品标题、图片、描述以及价格和购买按钮的 HTML 元素。

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 中:

"操作、机密和变量" 配置页面,在基于 Web 的界面中显示存储库机密,如 VM_HOST、VM_KEY 和 VM_USER。

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

现在,让我们创建.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 的填充是否正确:

Kibana DevTools 代码,以确认网络爬虫索引设置正确。

部署到 prod

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

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

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

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

操作部署到 GitHub 存储库中的 EC2 标签。

进行更改和重新部署

您可能已经注意到,每个产品的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` 查看本地的变化:

运行 `./local.sh` 查看本地的价格变化。

太好了!现在让我们推动变革:

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

要确认一切正常,可以检查生产的 Kibana,它应该会反映这些更改,并将价格显示为一个不带美元符号的新字段。

结论

Elastic Open Web Crawler 允许您将爬虫作为代码进行管理,这意味着您可以自动执行从开发到部署的整个流程,并以编程方式添加短暂的本地环境和针对爬虫数据的测试。

我们邀请您克隆官方资源库,并开始使用此工作流程为自己的数据编制索引。您还可以阅读本文,了解如何在爬虫生成的索引上运行语义搜索。

相关内容

显示 Elasticsearch 索引中的字段

Kofi Bartlett

如何通过 Kafka 向 Elasticsearch 摄取数据

Andre Luiz

在 Logstash 中使用 Ruby 脚本

Dai Sugimori

Elasticsearch 中的索引模板:如何使用可组合模板

Kofi Bartlett

如何显示 Elasticsearch 索引的字段

JD Armada

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用