# 爬虫数据上报接口文档 > 适用版本: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 天) ├── 写入 ClickHouse(job_data 库) └── 转发至外部数据平台(qixin.com) ``` 三个平台的爬虫**调用同一套接口**,通过 `platform` 字段区分来源,通过 `data_type` 字段区分数据类型(职位/公司)。 --- ## 2. 认证方式 数据上报接口属于**内部接口,无需鉴权**。 爬虫调用时统一在 Header 中携带: ``` token: dev ``` > 说明:`dev` 是开发模式 Token,后端不验证签名,直接放行。生产部署如需启用鉴权,改用 JWT Token(HS256,有效期 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` 时跳过去重直接写入 | **Response(HTTP 202)** ```json { "code": 202, "message": "批量数据已加入异步处理队列,共 10 条", "platform": "boss", "data_type": "job" } ``` --- ### 3.2 同步批量上报 同步等待全部写入完成后返回,可获得详细的成功/失败统计。 ``` POST /api/v1/universal/data/batch-store ``` **Request Body** — 与 3.1 相同。 **Response(HTTP 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`。 **Response(HTTP 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 项目自动生成,如有疑问联系项目维护者。*