Elastic Open Web Crawler 作为代码

84 阅读7分钟

学习如何使用 GitHub Actions 管理 Elastic Open Crawler 配置,这样每次我们将更改推送到仓库时,更改都会自动应用到已部署的 crawler 实例。

Elasticsearch 允许你快速并灵活地索引数据。你可以在云端免费试用,也可以在本地运行,看看索引有多么容易。

通过 Elastic Open Web Crawler 及其 CLI 驱动的架构,实现版本化的 crawler 配置和带有本地测试的 CI/CD 流水线现在变得非常简单。

传统上,管理 crawlers 是一个手动且容易出错的过程。它涉及在 UI 中直接编辑配置,并在克隆爬取配置、回滚、版本控制等方面遇到困难。将 crawler 配置当作代码来处理解决了这些问题,因为它提供了我们在软件开发中所期望的相同优势:可重复性、可追溯性和自动化。

这个工作流让你更容易将 Open Web Crawler 引入 CI/CD 流水线,用于回滚、备份和迁移 —— 这些任务在早期的 Elastic Crawlers(如 Elastic Web Crawler 或 App Search Crawler)中要麻烦得多。

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

  • 使用 GitHub 管理我们的爬取配置

  • 搭建本地环境来在部署前测试流水线

  • 创建生产环境,每次将更改推送到主分支时使用新设置运行 web crawler

你可以在这里找到项目仓库。撰写时,我使用的是 Elasticsearch 9.1.3 和 Open Web Crawler 0.4.2。

前提条件

  • Docker Desktop

  • Elasticsearch 实例

  • 带有 SSH 访问权限并安装了 Docker 的虚拟机(例如 AWS EC2)

步骤

  • 文件夹结构

  • Crawler 配置

  • Docker-compose 文件(本地环境)

  • Github Actions

  • 本地测试

  • 部署到生产环境

  • 修改并重新部署

文件夹结构

对于这个项目,我们将有以下文件结构:

`

1.  ├── docker-compose.yml # Local elasticsearch + crawler
2.  ├── config/crawler-config.yml # Crawler config
3.  ├── .github/workflows/deploy.yml # GH Action to deploy changes
4.  ├── local.sh # Script to run our local crawler

`AI写代码

Crawler 配置

在 crawler-config.yml 下,我们将放入以下内容:

`

1.  output_sink: elasticsearch
2.  output_index: web-crawl-index
3.  max_crawl_depth: 1

5.  elasticsearch:
6.    host: ${ES_HOST}
7.    api_key: ${ES_API_KEY}

9.  domains:
10.    - url: https://web-scraping.dev
11.      seed_urls:
12.        - https://web-scraping.dev/product/1
13.        - https://web-scraping.dev/product/2
14.        - https://web-scraping.dev/product/3

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

这将从 web-scraping.dev/products 抓取,这是一个用于产品的模拟网站。我们只会抓取前三个产品页面。max_crawl_depth 设置将阻止 crawler 发现超出 seed_urls 定义的页面,因为它不会打开其中的链接。

Elasticsearch host 和 api_key 将根据我们运行脚本的环境动态填充。

Docker-compose 文件(本地环境)

对于本地的 docker-compose.yml,我们将部署 crawler 和一个单节点的 Elasticsearch 集群 + Kibana,这样我们可以在部署到生产环境之前轻松地可视化爬取结果。

`

1.  services:
2.    es01:
3.      image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
4.      environment:
5.        - discovery.type=single-node
6.        - xpack.security.enabled=false
7.        - ES_JAVA_OPTS=-Xms1g -Xmx1g
8.      ports:
9.        - "9200:9200"
10.      networks: [esnet]
11.      healthcheck:
12.        test: ["CMD", "curl", "-f", "http://localhost:9200"]
13.        interval: 5s
14.        timeout: 5s
15.        retries: 10

17.    kibana:
18.      image: docker.elastic.co/kibana/kibana:9.1.3
19.      environment:
20.        - ELASTICSEARCH_HOSTS=http://es01:9200
21.      ports:
22.        - "5601:5601"
23.      networks: [esnet]
24.      depends_on: [es01]

26.    crawler:
27.      image: docker.elastic.co/integrations/crawler:0.4.2
28.      environment:
29.        - ES_HOST=http://es01:9200
30.        - CRAWLER_JRUBY_OPTS=--server
31.      container_name: crawler
32.      volumes:
33.        - ./config:/home/app/config
34.      networks: [esnet]
35.      entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
36.      stdin_open: true
37.      tty: true

39.  networks:
40.    esnet:
41.      driver: bridge

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

注意 crawler 会等待 Elasticsearch 就绪后才运行。

Github Actions

现在我们需要创建一个 GitHub Action,它会在每次 push 到 main 时将新设置复制并在虚拟机中运行 crawler。这样可以确保我们始终部署的是最新配置,而不需要手动进入虚拟机更新文件并运行 crawler。我们将使用 AWS EC2 作为虚拟机提供商。

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

这样,action 就能访问我们的服务器来复制新文件并运行爬取。

现在,让我们创建 .github/workflows/deploy.yml 文件:

`

1.  name: Deploy

3.  on:
4.    push:
5.      branches: [main]

7.  jobs:
8.    Deploy:
9.      name: Deploy to EC2
10.      runs-on: ubuntu-latest

12.      steps:
13.        - uses: actions/checkout@v5

15.        - name: Deploy crawler
16.          env:
17.            HOSTNAME: ${{ secrets.VM_HOST }}
18.            USER_NAME: ${{ secrets.VM_USER }}
19.            PRIVATE_KEY: ${{ secrets.VM_KEY }}
20.            ES_HOST: ${{ secrets.ES_HOST }}
21.            ES_API_KEY: ${{ secrets.ES_API_KEY }}
22.          run: |
23.            # Save private key
24.            echo "$PRIVATE_KEY" > private_key
25.            chmod 600 private_key

27.            # Generate final config locally
28.            envsubst < config/crawler-config.yml > config/crawl-config-final.yml

30.            # Copy the config folder to VM
31.            scp -o StrictHostKeyChecking=no -i private_key -r config ${USER_NAME}@${HOSTNAME}:~/config

33.            # SSH into VM and run crawler
34.            ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} << EOF
35.              docker run --rm \
36.                -v ~/config:/config \
37.                docker.elastic.co/integrations/crawler:latest jruby \
38.                bin/crawler crawl /config/crawl-config-final.yml
39.            EOF

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

每次我们推送对 crawler 配置文件的更改时,此 action 将执行以下步骤:

  • 在 yml 配置中填充 Elasticsearch host 和 API Key

  • 将 config 文件夹复制到我们的 VM

  • 通过 SSH 连接到 VM

  • 使用刚从仓库复制的配置运行爬取

本地测试

为了在本地测试我们的 crawler,我们创建了一个 bash 脚本,它会用 Docker 中的本地 Elasticsearch host 填充配置并启动爬取。你可以运行 ./local.sh 来执行它。

`

1.  #!/bin/bash

3.  # Exit on any error
4.  set -e

6.  # Load environment variables
7.  export ES_HOST="http://es01:9200"

9.  # Generate final crawler config
10.  envsubst < ./config/crawler-config.yml > ./config/crawl-config-final.yml

12.  # Bring everything up
13.  docker compose up --build

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

让我们查看 Kibana DevTools,以确认 web-crawler-index 是否已正确填充:

部署到生产环境

现在我们准备将更改推送到 main 分支,这将会在你的虚拟机中部署 crawler,并开始将日志发送到你的 Serverless Elasticsearch 实例。

`

1.  git add .
2.  git commit -m "First commit"
3.  git push

`AI写代码

这将触发 GitHub Action,它会在虚拟机中执行部署脚本并开始爬取。

你可以通过访问 GitHub 仓库并进入 “Actions” 标签页来确认 action 是否已执行:

修改并重新部署

你可能已经注意到,每个产品的价格是文档 body 字段的一部分。理想情况下,我们应将价格存储在单独的字段中,以便对其运行过滤器。

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

`

1.  output_sink: elasticsearch
2.  output_index: web-crawl-index
3.  max_crawl_depth: 1

5.  elasticsearch:
6.    host: ${ES_HOST}
7.    api_key: ${ES_API_KEY}

9.    # Index ingest pipeline to process documents before indexing          
10.    pipeline_enabled: true
11.    pipeline: pricing-pipeline

13.  domains:
14.    - url: https://web-scraping.dev
15.      seed_urls:
16.        - https://web-scraping.dev/product/1
17.        - https://web-scraping.dev/product/2
18.        - https://web-scraping.dev/product/3
19.      extraction_rulesets:
20.        - url_filters:
21.            - type: ends
22.              pattern: /product/*
23.          rules:
24.            - action: extract
25.              field_name: price
26.              selector: .product-price
27.              join_as: string
28.              source: html

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

我们还注意到价格包含美元符号 ($),如果我们想运行范围查询,就必须将其去掉。我们可以使用 ingest pipeline 来实现。注意,我们在上面的新 crawler 配置文件中引用了它:

`

1.  PUT _ingest/pipeline/pricing-pipeline
2.  {
3.    "processors": [
4.      {
5.        "script": {
6.          "source": """
7.                  ctx['price'] = ctx['price'].replace("$","")
8.              """
9.        }
10.      }
11.    ]
12.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

我们可以在生产 Elasticsearch 集群中运行该命令。对于开发环境,由于它是临时的,我们可以将 pipeline 创建作为 docker-compose.yml 文件的一部分,通过添加以下服务来实现。注意,我们还在 crawler 服务中添加了 depends_on,以便它在 pipeline 成功创建后再启动。

 `1.   crawler:
2.      image: docker.elastic.co/integrations/crawler:0.4.2
3.      environment:
4.        - ES_HOST=http://es01:9200
5.        - CRAWLER_JRUBY_OPTS=--server
6.      container_name: crawler
7.      volumes:
8.        - ./config:/home/app/config
9.      networks: [esnet]
10.      entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
11.      depends_on:
12.        pipeline-init:
13.          condition: service_completed_successfully
14.      stdin_open: true
15.      tty: true  

18.    pipeline-init:
19.      image: curlimages/curl:latest
20.      depends_on:
21.        es01:
22.          condition: service_healthy
23.      networks: [esnet]
24.      entrypoint: >
25.          sh -c "
26.          echo 'Creating ingest pipeline...';
27.          curl -s -X PUT http://es01:9200/_ingest/pipeline/pricing-pipeline \\
28.            -H 'Content-Type: application/json' \\
29.            -d '{\"processors\":[{\"script\":{\"source\":\"ctx.price = ctx.price.replace(\\\"$\\\", \\\"\\\")\"}}]}';
30.          echo 'Pipeline created!';
31.          "`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

现在让我们运行 ./local.sh 来在本地查看更改:

太好了!现在让我们推送更改:

`

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

`AI写代码

要确认一切正常,你可以查看生产环境的 Kibana,它应反映更改,并显示没有美元符号的价格作为新字段。

结论

Elastic Open Web Crawler 允许你将 crawler 当作代码来管理,这意味着你可以自动化整个流程 —— 从开发到部署 —— 并添加临时本地环境以及对爬取数据进行编程测试,仅举几例。

你可以克隆官方仓库,使用此工作流开始索引自己的数据。你还可以阅读这篇文章,了解如何对 crawler 生成的索引运行语义搜索。

原文:www.elastic.co/search-labs…