构建简单数据 API 与使用 Docker 容器化爬虫服务
1. 创建简单数据 API
在数据处理中,我们常常需要从各种数据源获取数据。这里我们以获取职位列表信息为例,通过简单的 API 来实现数据的获取和存储。
1.1 获取特定技能信息
我们可以使用 curl 命令来获取特定职位的技能信息,示例如下:
curl localhost:5000/joblisting/122517/skills
执行上述命令后,会得到如下结果:
["c#", "sql", "javascript", "asp.net", "angularjs"]
1.2 将抓取数据存储到 Elasticsearch
为了优化数据请求,避免重复抓取已处理过的职位列表,我们将抓取到的数据存储到 Elasticsearch 中。
准备工作 :
- 确保 Elasticsearch 在本地运行,代码将访问 localhost:9200 。可以参考 Elasticsearch 安装指南 进行安装。安装完成后,可以使用以下 curl 命令检查安装是否正确:
curl 127.0.0.1:9200?pretty
如果安装正确,会得到类似如下的输出:
{
"name": "KHhxNlz",
"cluster_name": "elasticsearch",
"cluster_uuid": "fA1qyp78TB623C8IKXgT4g",
"version": {
"number": "6.1.1",
"build_hash": "bd92e7f",
"build_date": "2017-12-17T20:23:25.338Z",
"build_snapshot": false,
"lucene_version": "7.1.0",
"minimum_wire_compatibility_version": "5.6.0",
"minimum_index_compatibility_version": "5.0.0"
},
"tagline": "You Know, for Search"
}
- 安装
elasticsearch-py库,可以使用以下命令进行安装:
pip install elasticsearch
具体操作步骤 :
1. 在 API 代码中添加 elasticsearch-py 的导入:
from elasticsearch import Elasticsearch
- 修改
JobListing类的get方法:
class JobListing(Resource):
def get(self, job_listing_id):
print("Request for job listing with id: " + job_listing_id)
listing = get_job_listing_info(job_listing_id)
es = Elasticsearch()
es.index(index='joblistings', doc_type='job-listing', id=job_listing_id, body=listing)
print("Got the following listing as a response: " + listing)
return listing
在调用 API 之前,使用以下 curl 命令检查是否存在 joblistings 索引:
curl localhost:9200/joblistings
由于刚刚安装 Elasticsearch,会得到如下错误:
{"error":{"root_cause":[{"type":"index_not_found_exception","reason":"no such index","resource.type":"index_or_alias","resource.id":"joblistings","index_uuid":"_na_","index":"joblistings"}],"type":"index_not_found_exception","reason":"no such index","resource.type":"index_or_alias","resource.id":"joblistings","index_uuid":"_na_","index":"joblistings"},"status":404}
- 启动 API:
python api.py
- 发送
curl请求获取职位列表:
curl localhost:5000/joblisting/122517
此时,该文档将被存储到 Elasticsearch 中。再次使用以下 curl 命令检查 joblistings 索引:
curl localhost:9200/joblistings
会得到类似如下结果(仅显示前几行):
{
"joblistings": {
"aliases": {},
"mappings": {
"job-listing": {
"properties": {
"CleanedWords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"ID": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
这表明已经创建了名为 joblistings 的索引,并且 Elasticsearch 通过检查文档识别出了索引结构。
- 检索特定文档:
curl localhost:9200/joblistings/job-listing/122517
会得到类似如下结果(仅显示内容开头部分):
{
"_index": "joblistings",
"_type": "job-listing",
"_id": "122517",
"_version": 1,
"found": true,
"_source": {
"ID": "122517",
"JSON": {
"@context": "http://schema.org",
"@type": "JobPosting",
"title": "SpaceX Enterprise Software Engineer, Full Stack",
"skills": [
"c#",
"sql",
"javascript",
"asp.net",
"angularjs"
],
"description": "<h2>About this job</h2>\r\n<p><span>Location options: <strong>Paid relocation</strong></span><br/><span>Job type: <strong>Permanent</strong></span><br/><span>Experience level: <strong>Mid-Level,"
}
}
}
1.3 存储文档的参数说明
在将文档存储到 Elasticsearch 时,使用了 es.index() 方法,其参数说明如下:
| 参数 | 说明 |
| ---- | ---- |
| index | 指定要存储文档的 Elasticsearch 索引名称,这里是 joblistings ,也是检索文档时 URL 的第一部分。 |
| doc_type | 每个 Elasticsearch 索引可以有多个文档类型,这里使用 job-listing ,它也是检索特定文档时 URL 的第二部分。 |
| id | 文档的标识符,如果提供该参数,可以直接查找特定文档而无需搜索。这里使用职位列表的 ID。 |
| body | 指定文档的实际内容,这里直接传递了从爬虫获取的结果。 |
1.4 Elasticsearch 存储结果分析
通过检索文档的结果,我们可以看到以下信息:
- 索引、文档类型和 ID :在结果的前几行可以看到 _index 、 _type 和 _id ,使用这三个值进行文档检索非常高效。
- 版本信息 :每个文档都有一个版本号,初始版本为 1。如果再次执行相同的查询,文档会被再次存储,版本号会递增。
- 结构化数据 :结果中的 JSON 属性包含了职位描述的结构化数据,如 @context 、 @type 、 title 和 skills 等。这些结构化数据使得我们可以方便地进行各种查询。
- 提取的高价值词汇 :API 返回的 CleanedWords 属性是经过 NLP 处理提取的高价值词汇和术语,我们可以利用这些词汇进行丰富的查询。
1.5 抓取前检查 Elasticsearch
为了进一步优化数据请求,我们可以在抓取职位列表之前先检查 Elasticsearch 中是否已经存在该文档。
代码实现 :
class JobListing(Resource):
def get(self, job_listing_id):
print("Request for job listing with id: " + job_listing_id)
es = Elasticsearch()
if (es.exists(index='joblistings', doc_type='job-listing', id=job_listing_id)):
print('Found the document in ElasticSearch')
doc = es.get(index='joblistings', doc_type='job-listing', id=job_listing_id)
return doc['_source']
listing = get_job_listing_info(job_listing_id)
es.index(index='joblistings', doc_type='job-listing', id=job_listing_id, body=listing)
print("Got the following listing as a response: " + listing)
return listing
上述代码在调用爬虫代码之前,先使用 exists 方法检查文档是否存在于 Elasticsearch 中。如果存在,则直接从 Elasticsearch 中获取文档;否则,抓取文档并存储到 Elasticsearch 中。
JobListingSkills 类的实现 :
class JobListingSkills(Resource):
def get(self, job_listing_id):
print("Request for job listing's skills with id: " + job_listing_id)
es = Elasticsearch()
if (es.exists(index='joblistings', doc_type='job-listing', id=job_listing_id)):
print('Found the document in ElasticSearch')
doc = es.get(index='joblistings', doc_type='job-listing', id=job_listing_id)
return doc['_source']['JSON']['skills']
skills = get_job_listing_skills(job_listing_id)
print("Got the following skills as a response: " + skills)
return skills
该实现仅使用 Elasticsearch 检查文档是否存在,不尝试保存新抓取的文档。因为 get_job_listing_skills 方法只返回技能列表,而不是整个文档。一种潜在的解决方案是调用 get_job_listing_info 方法,保存整个文档,然后只返回特定的子集(如技能信息)。
2. 使用 Docker 容器化爬虫服务
为了将爬虫服务部署到实际环境中,我们可以使用 Docker 进行容器化。Docker 提供了一种方便的方式来打包和部署应用程序,使得应用程序可以在不同的环境中一致运行。
2.1 安装 Docker
Docker 支持 Linux、macOS 和 Windows 等主流操作系统,不同操作系统的安装过程有所不同。以下以 macOS 为例,介绍 Docker 的安装步骤。
准备工作 :
可以参考 Docker 安装指南 进行安装。
具体操作步骤 :
1. 访问 Docker 下载页面,选择 Docker Community Edition 的稳定版进行下载。
2. 下载完成后,打开 Docker.dmg 文件,将 Moby 鲸鱼图标拖到应用程序文件夹中。
3. 打开 Docker.app ,输入密码进行安装。安装完成后,会在状态栏看到 Moby 图标。
4. 打开终端,输入以下命令验证 Docker 是否正常工作:
docker info
2.2 从 Docker Hub 安装 RabbitMQ 容器
RabbitMQ 是一个消息队列中间件,我们将使用它作为爬虫微服务的消息总线。
准备工作 :
通常,安装 RabbitMQ 需要安装 Erlang 和 RabbitMQ 本身,还可能需要安装管理工具。使用 Docker 可以方便地获取预配置好的容器。
具体操作步骤 :
1. 使用 docker pull 命令获取容器,先尝试使用 --help 查看命令帮助:
docker pull --help
输出如下:
Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
Pull an image or a repository from a registry
Options:
-a, --all-tags Download all tagged images in the repository
--disable-content-trust Skip image verification (default true)
--help Print usage
- 我们要拉取
rabbitmq:3-management容器,该容器包含版本 3 的 RabbitMQ 以及管理工具。该容器来自 Docker Hub 的 RabbitMQ 仓库,仓库页面为 https://hub.docker.com/_/rabbitmq/ 。 - 在终端中执行以下命令拉取容器:
docker pull rabbitmq:3-management
通过以上步骤,我们完成了简单数据 API 的创建和数据存储,以及 Docker 的安装和 RabbitMQ 容器的拉取。后续可以继续进行容器化爬虫服务的其他操作,如创建 API 容器、运行 Docker 容器等。
构建简单数据 API 与使用 Docker 容器化爬虫服务
2.3 运行 Docker 容器(RabbitMQ)
在成功拉取 RabbitMQ 容器后,我们可以运行该容器。以下是运行 RabbitMQ 容器的具体步骤:
1. 使用以下命令运行容器:
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
此命令的参数说明如下:
| 参数 | 说明 |
| ---- | ---- |
| -d | 以守护进程模式运行容器,即容器在后台运行。 |
| --name rabbitmq | 为容器指定名称为 rabbitmq 。 |
| -p 5672:5672 | 将容器内部的 5672 端口映射到主机的 5672 端口,5672 是 RabbitMQ 的 AMQP 协议端口。 |
| -p 15672:15672 | 将容器内部的 15672 端口映射到主机的 15672 端口,15672 是 RabbitMQ 管理界面的端口。 |
| rabbitmq:3-management | 指定要运行的容器镜像。 |
2. 运行成功后,可以使用以下命令查看容器的运行状态:
docker ps
如果看到 rabbitmq 容器处于运行状态,则说明容器已成功启动。
2.4 停止和移除容器及镜像
在某些情况下,我们可能需要停止和移除容器及镜像。以下是具体操作步骤:
1. 停止容器 :
docker stop rabbitmq
此命令将停止名为 rabbitmq 的容器。
2. 移除容器 :
docker rm rabbitmq
此命令将移除名为 rabbitmq 的容器。
3. 移除镜像 :
docker rmi rabbitmq:3-management
此命令将移除 rabbitmq:3-management 镜像。
2.5 创建 API 容器
为了将 API 服务容器化,我们需要创建一个 Dockerfile 来定义容器的构建规则。以下是一个简单的 Dockerfile 示例:
# 使用 Python 基础镜像
FROM python:3.8-slim
# 设置工作目录
WORKDIR /app
# 复制当前目录下的所有文件到工作目录
COPY . /app
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 暴露端口
EXPOSE 5000
# 运行 API
CMD ["python", "api.py"]
创建 Dockerfile 后,使用以下命令构建 API 容器:
docker build -t sojobs-api .
此命令将构建一个名为 sojobs-api 的容器镜像。
2.6 创建通用微服务与 Nameko
Nameko 是一个用于创建微服务的 Python 框架。以下是创建一个简单 Nameko 微服务的示例:
from nameko.rpc import rpc
class MyService:
name = "my_service"
@rpc
def hello(self, name):
return f"Hello, {name}!"
将上述代码保存为 service.py ,然后使用以下命令启动 Nameko 服务:
nameko run service
2.7 创建爬虫微服务
我们可以将爬虫功能封装成一个微服务。以下是一个简单的爬虫微服务示例:
from nameko.rpc import rpc
import requests
class ScraperService:
name = "scraper_service"
@rpc
def scrape_job_listing(self, job_listing_id):
url = f"https://example.com/joblisting/{job_listing_id}"
response = requests.get(url)
return response.text
将上述代码保存为 scraper_service.py ,然后使用以下命令启动爬虫微服务:
nameko run scraper_service
2.8 创建爬虫容器
为了将爬虫微服务容器化,同样需要创建一个 Dockerfile。以下是一个简单的 Dockerfile 示例:
# 使用 Python 基础镜像
FROM python:3.8-slim
# 设置工作目录
WORKDIR /app
# 复制当前目录下的所有文件到工作目录
COPY . /app
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 暴露端口
EXPOSE 8000
# 运行爬虫微服务
CMD ["nameko", "run", "scraper_service"]
创建 Dockerfile 后,使用以下命令构建爬虫容器:
docker build -t sojobs-scraper .
此命令将构建一个名为 sojobs-scraper 的容器镜像。
2.9 创建后端(ElasticCache)容器
如果需要使用 ElasticCache 作为后端存储,可以使用 Docker 拉取 ElasticCache 容器。以下是具体操作步骤:
1. 拉取 ElasticCache 容器:
docker pull amazon/aws-elasticache-redis
- 运行 ElasticCache 容器:
docker run -d --name elasticcache -p 6379:6379 amazon/aws-elasticache-redis
2.10 使用 Docker Compose 组合和运行爬虫容器
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。以下是一个 docker-compose.yml 文件示例:
version: '3'
services:
api:
build: .
ports:
- "5000:5000"
depends_on:
- rabbitmq
- elasticcache
scraper:
build: .
ports:
- "8000:8000"
depends_on:
- rabbitmq
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
elasticcache:
image: amazon/aws-elasticache-redis
ports:
- "6379:6379"
使用以下命令启动所有容器:
docker-compose up -d
此命令将以守护进程模式启动所有容器。
通过以上步骤,我们完成了使用 Docker 容器化爬虫服务的整个过程,包括安装 Docker、拉取和运行容器、创建 API 容器、微服务以及使用 Docker Compose 组合和运行多个容器。这些操作使得爬虫服务可以方便地部署和管理,并且可以在不同的环境中一致运行。
超级会员免费看

18

被折叠的 条评论
为什么被折叠?



