JobData/docs/爬虫数据上报接口文档.md
2026-03-22 23:22:30 +08:00

594 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 爬虫数据上报接口文档
> 适用版本JobData v1.x
> 更新日期2026-03-20
> 目标读者:接手爬虫开发或后端对接的工程师
---
## 目录
1. [整体架构](#1-整体架构)
2. [认证方式](#2-认证方式)
3. [核心上报接口](#3-核心上报接口)
- 3.1 [异步批量上报(推荐)](#31-异步批量上报推荐)
- 3.2 [同步批量上报](#32-同步批量上报)
- 3.3 [同步单条上报](#33-同步单条上报)
- 3.4 [平台专属便捷接口](#34-平台专属便捷接口)
4. [各平台数据结构](#4-各平台数据结构)
- 4.1 [BOSS直聘](#41-boss直聘-platformboss)
- 4.2 [前程无忧](#42-前程无忧-platformqcwy)
- 4.3 [智联招聘](#43-智联招聘-platformzhilian)
5. [去重规则](#5-去重规则)
6. [爬虫调用示例](#6-爬虫调用示例)
7. [辅助接口](#7-辅助接口)
8. [数据存储说明](#8-数据存储说明)
9. [常见问题](#9-常见问题)
---
## 1. 整体架构
```
爬虫Boss / 前程无忧 / 智联)
│ POST /api/v1/universal/data/batch-store-async
FastAPI 后端app/
├── 去重检查ClickHouse 查最近 90 天)
├── 写入 ClickHousejob_data 库)
└── 转发至外部数据平台qixin.com
```
三个平台的爬虫**调用同一套接口**,通过 `platform` 字段区分来源,通过 `data_type` 字段区分数据类型(职位/公司)。
---
## 2. 认证方式
数据上报接口属于**内部接口,无需鉴权**。
爬虫调用时统一在 Header 中携带:
```
token: dev
```
> 说明:`dev` 是开发模式 Token后端不验证签名直接放行。生产部署如需启用鉴权改用 JWT TokenHS256有效期 7 天)。
---
## 3. 核心上报接口
**Base URL**(本地开发):`http://localhost:8000`
两个路径前缀完全等价,行为相同:
- `/api/v1/universal`
- `/api/v1/job`
---
### 3.1 异步批量上报(推荐)
**三个平台爬虫均使用此接口**,立即返回 202后台异步写入。
```
POST /api/v1/universal/data/batch-store-async
```
**Request Headers**
```
Content-Type: application/json
token: dev
```
**Request Body**
```json
{
"data_list": [
{ ...原始职位或公司 JSON... }
],
"data_type": "job",
"platform": "boss",
"check_duplicate": true
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `data_list` | `List[Dict]` | ✅ | 原始数据列表,结构见第 4 节 |
| `data_type` | `string` | ✅ | `job`(职位)或 `company`(公司) |
| `platform` | `string` | ✅ | `boss` / `qcwy` / `zhilian` |
| `check_duplicate` | `bool` | ❌ | 默认 `true``false` 时跳过去重直接写入 |
**ResponseHTTP 202**
```json
{
"code": 202,
"message": "批量数据已加入异步处理队列,共 10 条",
"platform": "boss",
"data_type": "job"
}
```
---
### 3.2 同步批量上报
同步等待全部写入完成后返回,可获得详细的成功/失败统计。
```
POST /api/v1/universal/data/batch-store
```
**Request Body** — 与 3.1 相同。
**ResponseHTTP 200**
```json
{
"code": 200,
"message": "批量处理完成: 成功 8 条,失败 0 条,重复 2 条",
"data": {
"total": 10,
"success": 8,
"failed": 0,
"duplicate": 2,
"errors": []
},
"platform": "boss",
"data_type": "job"
}
```
---
### 3.3 同步单条上报
```
POST /api/v1/universal/data/store
```
**Request Body**
```json
{
"data": { ...单条原始 JSON... },
"data_type": "job",
"platform": "boss",
"check_duplicate": true
}
```
注意:字段名是 `data`(单条),不是 `data_list`
**ResponseHTTP 200**
```json
{
"code": 200,
"message": "JSON数据存储成功",
"data": {
"success": true,
"message": "JSON数据存储成功",
"duplicate": false,
"table_name": "boss_job",
"storage_type": "json"
},
"platform": "boss",
"data_type": "job"
}
```
数据重复时 `duplicate``true``message``"数据重复,跳过插入"`HTTP 仍返回 200。
---
### 3.4 平台专属便捷接口
Request Body 直接传原始 JSON 对象(无需包装 `platform`/`data_type`),等价于 3.3 的 `data` 字段:
| URL | 平台 | 类型 |
|-----|------|------|
| `POST /api/v1/job/boss/job` | BOSS直聘 | 职位 |
| `POST /api/v1/job/boss/company` | BOSS直聘 | 公司 |
| `POST /api/v1/job/qcwy/job` | 前程无忧 | 职位 |
| `POST /api/v1/job/qcwy/company` | 前程无忧 | 公司 |
| `POST /api/v1/job/zhilian/job` | 智联招聘 | 职位 |
| `POST /api/v1/job/zhilian/company` | 智联招聘 | 公司 |
---
## 4. 各平台数据结构
> 以下为 `data_list` 中每个元素的结构,即各平台原始 API 响应体(直接透传,无需转换)。
---
### 4.1 BOSS直聘platform=boss
#### 职位data_type=job
数据来源BOSS 微信小程序接口 `/wapi/zpgeek/miniapp/job/detail.json`
```json
{
"jobBaseInfoVO": {
"jobId": "123456",
"encryptJobId": "abc123",
"positionName": "Python 工程师",
"locationName": "上海",
"locationDesc": "上海市浦东新区XX路XX号",
"jobDesc": "负责数据采集与处理...",
"degreeName": "本科",
"experienceName": "3-5年",
"lowSalary": 15,
"highSalary": 25,
"requiredSkills": ["Python", "爬虫", "ClickHouse"],
"salaryWelfareInfo": ["五险一金", "弹性工作"]
},
"brandComInfoVO": {
"encryptBrandId": "brand_abc",
"brandName": "某科技有限公司",
"industryName": "互联网",
"scaleName": "100-499人",
"introduce": "公司简介..."
},
"bossBaseInfoVO": {
"brandName": "张HR"
}
}
```
**关键去重字段**`jobBaseInfoVO.jobId`
#### 公司data_type=company
数据来源BOSS 微信小程序接口 `/wapi/zpgeek/miniapp/brand/detail.json`
```json
{
"name": "某科技有限公司",
"companyFullInfoVO": {
"name": "某科技有限公司(全称)"
}
}
```
**关键去重字段**`name``companyFullInfoVO.name`(取 company_name
---
### 4.2 前程无忧platform=qcwy
#### 职位data_type=job
数据来源:前程无忧 APP 接口
```json
{
"jobId": "JL123456789",
"updateDateTime": "2026-03-20 10:00:00",
"jobName": "数据工程师",
"companyName": "某公司",
"fullCompanyName": "某公司全称有限公司",
"coId": "CO123456",
"jobDescribe": "岗位职责:...",
"degreeString": "本科",
"workYearString": "3-5年",
"jobSalaryMax": 20000,
"jobSalaryMin": 15000,
"provideSalaryString": "15k-20k",
"termStr": "全职",
"companySizeString": "500-999人",
"companyTypeString": "民营企业",
"major1Str": "互联网/电子商务",
"major2Str": "数据服务",
"jobWelfareCodeDataList": [
{ "chineseTitle": "五险一金", "typeTitle": "社保", "code": "001" }
],
"jobTagsForOrder": ["Python", "Spark", "Hive"],
"location": "上海",
"workLocation": {
"workAddress": "浦东新区",
"address": "上海市浦东新区XX路"
},
"jobAreaString": "上海",
"jobAreaLevelDetail": {
"cityString": "上海",
"landMarkString": "陆家嘴"
},
"confirmDateString": "2026-03-20",
"jobHref": "https://www.51job.com/...",
"companyHref": "https://www.51job.com/..."
}
```
**关键去重字段**`jobId` + `updateDateTime`(两字段联合唯一)
#### 公司data_type=company
```json
{
"companyName": "某公司",
"fullCompanyName": "某公司全称有限公司"
}
```
**关键去重字段**`companyName`
---
### 4.3 智联招聘platform=zhilian
#### 职位data_type=job
数据来源:智联招聘 PC 搜索接口 `https://fe-api.zhaopin.com/c/i/search/positions`
```json
{
"number": "ZL20260320001",
"firstPublishTime": "2026-03-20T10:00:00",
"name": "后端开发工程师",
"jobId": "J001",
"companyName": "某公司",
"companyId": "C001",
"salary60": "15k-25k",
"jobSummary": "职位描述:负责后端服务开发...",
"education": "本科",
"workingExp": "3-5年",
"workType": "全职",
"workCity": "上海",
"cityDistrict": "浦东新区",
"companySize": "500-999人",
"propertyName": "民营企业",
"industryName": "互联网",
"skillLabel": [
{ "value": "Go" },
{ "value": "Python" }
],
"recruitNumber": 3,
"positionURL": "https://www.zhaopin.com/...",
"companyUrl": "https://www.zhaopin.com/...",
"companyDesc": "公司描述(从额外接口补充)"
}
```
**关键去重字段**`number` + `firstPublishTime`(两字段联合唯一)
#### 公司data_type=company
```json
{
"companyName": "某公司",
"name": "某公司"
}
```
**关键去重字段**`companyName``name`
---
## 5. 去重规则
| 平台 | 数据类型 | 去重字段 | ClickHouse 表 |
|------|----------|----------|---------------|
| boss | job | `jobBaseInfoVO.jobId` | `boss_job` |
| boss | company | `name` / `companyFullInfoVO.name` | `boss_company` |
| qcwy | job | `jobId` + `updateDateTime` | `qcwy_job` |
| qcwy | company | `companyName` | `qcwy_company` |
| zhilian | job | `number` + `firstPublishTime` | `zhilian_job` |
| zhilian | company | `companyName` / `name` | `zhilian_company` |
- 去重检查范围:**最近 90 天**内已入库的记录。
- 重复数据不报错,正常返回 200`duplicate: true`
-`check_duplicate: false` 可跳过去重,强制写入(测试时使用)。
---
## 6. 爬虫调用示例
### BOSS直聘`jobs_spider/boss/boos_api.py`
```python
import requests
API_BASE_URL = "http://localhost:8000"
def push_job(zp_data: dict):
"""推送职位数据"""
payload = {
"data_list": [zp_data],
"data_type": "job",
"platform": "boss"
}
resp = requests.post(
f"{API_BASE_URL}/api/v1/universal/data/batch-store-async",
headers={
"accept": "application/json",
"token": "dev",
"Content-Type": "application/json"
},
json=payload,
timeout=30
)
return resp.json()
def push_company(zp_data: dict):
"""推送公司数据"""
payload = {
"data_list": [zp_data],
"data_type": "company",
"platform": "boss"
}
resp = requests.post(
f"{API_BASE_URL}/api/v1/universal/data/batch-store-async",
headers={
"accept": "application/json",
"token": "dev",
"Content-Type": "application/json"
},
json=payload,
timeout=30
)
return resp.json()
```
---
### 前程无忧(`jobs_spider/qcwy/qcwy.py`
```python
import requests
import socket
API_BASE_URL = "http://localhost:8000"
local_ip = socket.gethostbyname(socket.gethostname())
def report_data(data: list, data_type: str = "job"):
"""批量上报数据"""
payload = {
"data_list": data,
"data_type": data_type, # "job" 或 "company"
"platform": "qcwy"
}
resp = requests.post(
f"{API_BASE_URL}/api/v1/universal/data/batch-store-async",
json=payload,
headers={
"accept": "application/json",
"Content-Type": "application/json",
"X-Forwarded-For": local_ip # 传递真实 IP用于日志溯源
},
timeout=300
)
return resp.json()
```
---
### 智联招聘(`jobs_spider/zhilian/zhilian_single.py`
```python
import requests
API_BASE_URL = "http://localhost:8000"
def report_data(data_list: list, data_type: str = "job"):
"""批量上报数据"""
payload = {
"data_list": data_list,
"data_type": data_type, # "job" 或 "company"
"platform": "zhilian"
}
resp = requests.post(
f"{API_BASE_URL}/api/v1/universal/data/batch-store-async",
json=payload,
headers={
"accept": "application/json",
"Content-Type": "application/json"
},
timeout=300
)
return resp.json()
```
---
## 7. 辅助接口
爬虫运行过程中还会调用以下辅助接口:
| 接口 | 说明 | 主要使用方 |
|------|------|-----------|
| `GET /api/v1/token/tokens?page=1&page_size=10` | 获取可用的 MPT Token 列表 | BOSS爬虫 |
| `GET /api/v1/keyword/available?source=boss&limit=1&reserve=True` | 获取下一个未使用的关键词(城市+职位组合) | BOSS爬虫 |
| `POST /api/v1/keyword/mark-used` | 标记关键词已使用 | BOSS爬虫 |
| `GET /api/v1/stats` | 查询各平台已入库数据量 | 监控/运营 |
| `GET /api/v1/platforms` | 查询支持的平台列表及去重字段配置 | 调试 |
| `GET /api/v1/universal/data?platform=boss&data_type=job&page=1&page_size=20` | 分页查询已入库数据 | 调试 |
### 标记关键词已使用 Request Body
```json
{
"source": "boss",
"ids": [1, 2, 3]
}
```
---
## 8. 数据存储说明
### ClickHouse 表结构
所有表均在 `job_data` 数据库下ENGINE = `MergeTree()`
**通用列(每张表都有):**
| 列名 | 类型 | 说明 |
|------|------|------|
| `id` | UInt64 | 自增 ID |
| `json_data` | String | 原始 JSON 字符串(完整保存) |
| `created_at` | DateTime | 入库时间 |
| `updated_at` | DateTime | 更新时间 |
**各表额外列(用于去重查询):**
| 表名 | 额外列 |
|------|--------|
| `boss_job` | `job_id String` |
| `boss_company` | `company_name String` |
| `qcwy_job` | `job_id String`, `update_date_time String` |
| `qcwy_company` | `company_name String` |
| `zhilian_job` | `number String`, `first_publish_time String` |
| `zhilian_company` | `company_name String` |
### 统一查询视图
`job_analytics` 视图 UNION ALL 三张职位表,提供统一查询入口:
| 列名 | 说明 |
|------|------|
| `source` | 平台来源boss/qcwy/zhilian |
| `job_id` | 职位唯一标识 |
| `position_name` | 职位名称 |
| `company_name` | 公司名称 |
| `salary_text` | 薪资描述 |
| `city` | 城市 |
| `experience_required` | 经验要求 |
| `education` | 学历要求 |
| `created_at` | 入库时间 |
---
## 9. 常见问题
**Q上报返回 202但数据库里查不到数据**
A异步接口的写入有延迟通常 1-5 秒)。改用同步接口 `batch-store` 可立即确认写入结果。
**Q如何判断某条数据是否已存在**
A调用同步单条上报接口响应中 `duplicate: true` 表示已存在。
**Q`check_duplicate: false` 会导致重复数据吗?**
A会。仅在测试/调试时使用,生产环境保持默认 `true`
**Q三个平台的数据结构差异大如何统一分析**
A使用 `job_analytics` 视图,已将三张表的字段映射为统一列名。
**Q爬虫报超时错误怎么处理**
A异步接口 timeout 建议设 30s同步接口因要等待写入完成建议设 300s。若仍超时检查 ClickHouse 连接状态。
**Q`token: dev` 在生产环境安全吗?**
A不安全。生产环境应替换为 JWT Token并在接口上挂载鉴权中间件。
---
*文档由 JobData 项目自动生成,如有疑问联系项目维护者。*