全链路流程梳理:招聘数据自动化抓取
一、系统全景架构
二、启动阶段(定时任务 → 配置加载)
2.1 定时任务脚本
@echo off
echo 正在启动中...
call E:\chu\python310_ve\Scripts\activate.bat ← 激活 Python 3.10 虚拟环境
cd /d E:\chu\qzclawler ← 切换工作目录
git pull ← 拉取最新代码和配置
python main.py -m cp -f 51 ← 启动主程序
三步保障:
- 环境隔离:虚拟环境保证 PaddleOCR、Playwright 等依赖版本正确
- 代码同步:git pull 确保 .ini 配置和爬虫代码是最新的
- 分片执行:
-f 51只负责第 51 号配置文件里的公司,与-f 01到-f 50并行互不干扰
2.2 参数解析与实例化
python main.py -m cp -f 51
│ │ │
│ │ └─ 拼接为 setting_com_51.ini
│ └─ cp = Company 模式,调用 SpiderCom
└─ 主程序入口
实例化三个类:
s = SpiderSch(51) ← 学校爬虫(本次未使用)
cs = SpiderCom(51) ← 公司爬虫(本次主角)
d = SpiderData(s) ← 数据处理器
2.3 配置文件结构(setting_com_51.ini)
[com_00075] ← 松山湖材料实验室
com_name = 松山湖材料实验室
com_logo = https://xxx/logo.png
urls = {"xiaozhao": "https://...", "shezhao": "https://...", "overseas": "https://..."}
template = template_01005
func_name = gen_00005_3
json_domain =
hd_all_location = {"深圳": "深圳", "东莞": "东莞"}
[com_00498] ← 四三九九网络
com_name = 四三九九网络股份有限公司
urls = {"shezhao": "https://...", "xiaozhao": "https://..."}
template = template_50150
json_domain = wx-hr.img4399.com ← 模拟微信端请求获取JSON
func_name = gen_50150
[com_00499] ← 科捷智能(飞书招聘ATS)
com_name = 科捷智能科技股份有限公司
urls = {"shezhao": "https://...feishu..."}
template = template_50151
每个公司节点包含的关键字段:
| 字段 | 作用 |
|---|---|
com_name / com_webname |
公司全称 / 前端展示名 |
com_logo |
Logo 图片链接 |
urls |
按招聘类型分类的 URL(shezhao/xiaozhao/shixi) |
pre_open_url |
预加载链接,用于获取 Cookie/Token |
template |
HTML/JSON 解析模板编号 |
func_name |
自定义解析函数名(gen_xxxxx) |
json_domain |
API 接口域名(前后端分离网站) |
hd_all_location |
城市映射字典 |
max_parent_level |
爬虫钻取层级控制 |
click_type |
模拟点击方式 |
三、数据抓取阶段(四种抓取策略)
3.1 通用 HTML 抓取(大多数公司)
spider_com.py
│
├─ get_page_data()
│ 用 Playwright 打开列表页
│ 根据 template 找到所有职位链接
│
├─ get_page_detail_data()
│ 循环遍历每个职位链接
│
└─ get_page_detail_content()
打开详情页 → 提取 JD 文本
写入 detail_xxx.html
自动化解析函数体系(auto_gen / auto_gen_com):
爬取到页面 HTML
│
▼
func_call.py → 动态加载 gen_000xx.py → extract_table_from_html()
│
├─ 成功 → 输出 JSON(职位名/时间/链接/部门/地点/薪资)
│
└─ 结果为空
│
▼
func_gen_bygpt.py → 调用 GPT-4o
│
└─ 自动生成新的 gen_func_code_xxx_tmp.py
人工审核后重命名为正式文件
三类文件分工(auto_gen_com 目录):
| 文件类型 | 作用 | 示例 |
|---|---|---|
gen_000xx.py |
职位列表 HTML 解析 | 提取职位名、部门、地点、薪资 |
click_500xx.py |
Tab 点击切换 | 点击"博士生招聘"筛选标签 |
page_000xx.py |
翻页控制 | 点击"下一页",检测 disabled 判断末页 |
3.2 API 直连抓取(auto_api)
baidu_data_proc_api.py (总入口路由)
│
├─ com_90001 百度 ─── POST 招聘 API + 代理池(getProxy,递归重试3次)
│ 爬取间隔 30秒,详情页用 requests 拉取
│
├─ com_90002 软通动力 ── POST 两套 API(社招/校招格式不同,自动适配)
│ 详情页用 Playwright 渲染(SPA)+ ThreadPoolExecutor
│ 爬取间隔 1秒
│
├─ com_90003 京东 ──── POST 招聘 API
│
├─ com_90004 金蝶 ──── 下载 jobs.js 静态文件,正则提取 JSON
│
└─ com_90005 中国人保 ── POST 招聘 API
统一落盘逻辑:
获取到职位列表 JSON
│
├─ 文件已存在?→ 只更新 mtime(标记"本次爬到了"),跳过
│
├─ transform_job_json → 私有字段名 → 系统统一字段名
│ 写入 detail_xxx.json
│
├─ 获取/生成 HTML 文件
│ 百度:requests 拉详情页
│ 软通:Playwright 渲染保存
│ 金蝶:generate_html 拼接伪 HTML
│
└─ detail_xxx.html + detail_xxx.json 落盘
文件名策略:hashlib.md5(url.encode()).hexdigest() → 同一 URL 永远映射同一文件,天然去重
3.3 网络响应拦截(auto_on_response)
针对前后端分离 SPA,HTML 里没有职位数据的场景:
main_proc.py → 按 _key 路由
│
└─ com_91000 兴业银行 → xingye_proc.py
│
├─ 第一层 xingye_proc:
│ page.on('response', handler) 注册拦截器
│ 打开招聘页 → 循环点击"下一页"(默认3页 / cp_full全量100页)
│ 每次翻页等待 30秒
│
├─ 第二层 response_handler:
│ 过滤目标 API 响应(job.cib.com.cn/ersApi/...)
│ 确认 message=='成功' → 遍历职位列表
│
└─ 第三层 xingye_json:
原始字段 → 统一字段格式
generate_html → 拼成伪 HTML → 进入后续流水线
3.4 四种策略对比
通用HTML抓取 API直连 响应拦截 JS文件解析
(大多数公司) (百度/京东/软通) (兴业银行) (金蝶)
│ │ │ │
Playwright requests Playwright requests
渲染页面 POST接口 注册监听器 下载JS文件
│ │ │ │
gen_xxx.py 直接拿JSON handler捕获 正则提取
解析HTML API响应 JSON
│ │ │ │
└──────────────────┴──────────────────┴──────────────────┘
│
统一输出 .html + .json
│
进入数据解析流水线
四、数据解析流水线(parsegpt)
4.1 总体流程
原始 HTML 文件
│
▼
┌── ann_md.py ──────────────────────────────┐
│ ① 清除噪音 DOM(导航栏/页脚/广告) │
│ ② 修复链接和图片路径(相对→绝对URL) │
│ ③ 提取硬编码字段(CSS选择器直接抓取) │
│ ④ 图片处理(ann_img.py) │
│ ⑤ HTML → Markdown 转换 │
│ ⑥ 渠道专项修复 │
└──────────────────┬────────────────────────┘
│
▼
┌── ann_model.py ──────────────────────────────────────────┐
│ 第一阶段:公告级解析 │
│ 大模型提取公司名/应聘方式/毕业届/公告类型 │
│ → 过期检测 → 多公司检测 → 三无过滤 → 云端去重 │
│ → 毕业届修复 │
│ │
│ ┌─ 分叉判断 ────────────────────────────────┐ │
│ │ 白名单学校 + 类型="职位" │ │
│ │ → ann_model_job.py(单职位路径) │ │
│ │ 否则 │ │
│ │ → 第二阶段:职位列表解析(多职位提取) │ │
│ └───────────────────────────────────────────┘ │
│ │
│ 第三阶段:Markdown 格式处理 │
│ 大图文章 → 豆包润色 │
│ 外部链接 → 大模型提取 │
│ 复杂表格 → 大模型专项处理 │
│ │
│ 输出:ann + jobs + other + mdfile → JSON 模型文件 │
└──────────────────────────────────────────────────────────┘
4.2 ann_md.py — HTML 预处理(6 步)
① fix_html_div 按配置删除无关 div(导航/页脚/广告)
支持 ^ 语法标记"第N个"div
② fix_html_a 相对路径 → 绝对 URL
fix_html_img_src Base64 内嵌图 → 保存本地 → 上传 OBS → 外链
③ find_hardcode_tag CSS 选择器直接抓取公司名/标题
支持从 .full 文件提取补充字段
④ ann_img.py 调用 图片下载 → MD5/感知哈希去重
→ 二维码检测(Y纯码/H混合/N普通)
→ PaddleOCR 文字识别
→ 多层过滤(尺寸/透明度/黑名单)
→ 上传华为云 OBS 替换 src
⑤ html2md_table 检测合并单元格(colspan/rowspan)
无 → 专用工具转换
有 → html2text 库兜底
⑥ common_process_sch_98534 渠道专项修复(定制正则)
4.3 ann_model.py — 三阶段核心调度
第一阶段:公告级解析
Markdown 全文 → get_template_campus 模板 → 大模型
│
├─ 提取:公司名 / 应聘方式 / 毕业届 / 公告类型
│
├─ 过期检测:截止日期超1个月 → 丢弃
├─ 多公司检测:联合发布 → 公司名改为"校招公告"
├─ 三无过滤:无链接+无文本+无邮箱+无联系方式 → 丢弃
├─ 云端去重:check_cloud API 比对
│ 返回码:301过期 / 302黑名单 / 303全文重复 / 304链接重复
└─ 毕业届修复:正则提取"25届"/"2026" → 默认当前年(8月后+1)
关键分叉:
白名单学校 + 公告类型="职位"
│ │
▼ ▼
ann_model_job.py 第二阶段:普通公告路径
(单职位解析) get_template_job → 大模型
提取所有职位列表
学历规范化 / 去重 / 空职位名过滤
主文本无结果 → tuning_content 补充提取
第三阶段:Markdown 格式处理
四种策略:
├─ 大图文章(微信长图文)→ 读本地 md + 豆包润色
├─ 普通页面 → 读本地 md + 豆包润色
├─ 外部链接页面 → 大模型从全文提取 Markdown
└─ 含复杂表格 → 大模型专项处理表格
最终输出 JSON:
{
"ann": { 公告信息 },
"jobs": [ 职位列表 ],
"other": { 元数据 },
"mdfile": "展示用 Markdown"
}
4.4 两条单职位解析路径对比
| 维度 | ann_model_job.py | cjob_model.py |
|---|---|---|
| 来源 | 公告内的单职位(ann_model 分支调用) | 大公司招聘网站职位详情页(独立入口) |
| 定位方式 | HTML 转纯文本直接处理 | detail_selector CSS 选择器精准定位 |
| 额外字段 | 与公告元数据合并(公司名/链接/联系人) | 从 Header 区域提取类别/地点/时间/人数 |
| 职位类型 | 继承公告类型 | hd_hopeworktype 决定(shixi/xiaozhao/shezhao) |
| 模型调用 | Qwen 普通补全 | 带 system prompt 的 new_call_gpt |
| 复杂度 | 简单 | 复杂(多层备选方案、正则兜底) |
五、AI 模型调用层(api 目录)
5.1 多模型分工
┌─────────────────────────────────────────────────────┐
│ 大模型调用矩阵 │
├─────────────┬───────────────────────────────────────┤
│ 豆包(字节) │ 核心模块,处理职位信息(cjob) │
│ doubao_api │ 提取 20-30 个精细字段 │
│ │ 亮点:Prompt Caching(70h过期) │
│ │ Token 精细统计(input/cache/output) │
├─────────────┼───────────────────────────────────────┤
│ Azure OpenAI │ 处理招聘公告(ann) │
│ openai4o_api │ 提取公司信息/网申时间/岗位列表/投递规则 │
│ │ 保留新旧两版SDK │
├─────────────┼───────────────────────────────────────┤
│ 通义千问 │ 备用模型 / 单职位解析 │
│ qwen_api │ OpenAI 兼容调用方式 │
├─────────────┼───────────────────────────────────────┤
│ DeepSeek │ 备用模型 / 实验性接口 │
│ deepseek_api │ OpenAI 兼容调用方式 │
└─────────────┴───────────────────────────────────────┘
5.2 OCR 模块(ocr_api.py)
输入图片
│
├─ GIF → 转取第一帧
│
├─ 超长图片?
│ └─ img_cut 智能切图算法
│ OpenCV connectedComponents 连通组件检测
│ 切线碰到文字 → 智能上移,保证文字完整性
│
└─ PaddleOCR 识别 → 输出文字
5.3 存储与同步
hwcloud_api.py (华为云 OBS)
│
└─ 上传图片/文档 → quanfile 桶 → 返回公共 URL
quanzhi_api.py (业务系统)
│
├─ fix_upload_cloud:日期格式校验修复
│
├─ check_cloud:入库前拦截
│ 301 = 公告过期
│ 302 = 包含黑名单
│ 303 = 全文重复
│ 304 = 链接重复
│
└─ ann_add:POST 结构化 JSON → 121.36.63.42
六、完整数据流(端到端)
Windows 定时任务
│
▼
① git pull 同步代码
② activate.bat 激活环境
③ python main.py -m cp -f 51
│
▼
④ 读取 setting_com_51.ini(松山湖/4399/科捷智能...)
│
▼
⑤ 遍历每个公司,选择抓取策略:
├─ 通用HTML → Playwright + gen_xxx.py 解析
├─ API直连 → requests POST 接口
├─ 响应拦截 → Playwright page.on('response')
└─ JS文件 → 正则提取 JSON
│
▼
⑥ 统一输出 detail_xxx.html + detail_xxx.json
│
▼
⑦ ann_md.py:HTML清洗 → 图片处理(OCR/去重/上传OBS) → Markdown转换
│
▼
⑧ ann_model.py:大模型结构化提取
├─ 公告级解析(过期/多公司/三无/去重)
├─ 职位列表解析 或 单职位解析
└─ Markdown 格式化
│
▼
⑨ quanzhi_api.py:
├─ check_cloud 云端去重(301/302/303/304)
├─ fix_upload_cloud 日期修复
└─ ann_add POST → 121.36.63.42 正式数据库
│
▼
⑩ 数据入库完成,供前端搜索/展示/推荐使用
💬 评论