--- title: "02-全链路流程梳理" created: 2026-04-02 tags: - 项目 aliases: - 全链路流程梳理 --- # 全链路流程梳理:招聘数据自动化抓取 ## **一、系统全景架构** ![[image-62607bc7.png]] ## **二、启动阶段(定时任务 → 配置加载)** ### **2.1 定时任务脚本** ```bat @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 ← 启动主程序 ``` **三步保障**: 1. **环境隔离**:虚拟环境保证 PaddleOCR、Playwright 等依赖版本正确 2. **代码同步**:git pull 确保 .ini 配置和爬虫代码是最新的 3. **分片执行**:`-f 51` 只负责第 51 号配置文件里的公司,与 `-f 01` 到 `-f 50` 并行互不干扰 ### **2.2 参数解析与实例化** ```bash 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)** ```text [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 抓取(大多数公司)** ```text 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)**: ```text 爬取到页面 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)** ```text 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 ``` **统一落盘逻辑**: ```text 获取到职位列表 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 里没有职位数据的场景: ```text 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 四种策略对比** ```text 通用HTML抓取 API直连 响应拦截 JS文件解析 (大多数公司) (百度/京东/软通) (兴业银行) (金蝶) │ │ │ │ Playwright requests Playwright requests 渲染页面 POST接口 注册监听器 下载JS文件 │ │ │ │ gen_xxx.py 直接拿JSON handler捕获 正则提取 解析HTML API响应 JSON │ │ │ │ └──────────────────┴──────────────────┴──────────────────┘ │ 统一输出 .html + .json │ 进入数据解析流水线 ``` ## **四、数据解析流水线(parsegpt)** ### **4.1 总体流程** ```text 原始 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 步)** ```text ① 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 — 三阶段核心调度** **第一阶段:公告级解析** ```text Markdown 全文 → get_template_campus 模板 → 大模型 │ ├─ 提取:公司名 / 应聘方式 / 毕业届 / 公告类型 │ ├─ 过期检测:截止日期超1个月 → 丢弃 ├─ 多公司检测:联合发布 → 公司名改为"校招公告" ├─ 三无过滤:无链接+无文本+无邮箱+无联系方式 → 丢弃 ├─ 云端去重:check_cloud API 比对 │ 返回码:301过期 / 302黑名单 / 303全文重复 / 304链接重复 └─ 毕业届修复:正则提取"25届"/"2026" → 默认当前年(8月后+1) ``` **关键分叉**: ```text 白名单学校 + 公告类型="职位" │ │ ▼ ▼ ann_model_job.py 第二阶段:普通公告路径 (单职位解析) get_template_job → 大模型 提取所有职位列表 学历规范化 / 去重 / 空职位名过滤 主文本无结果 → tuning_content 补充提取 ``` **第三阶段:Markdown 格式处理** ```text 四种策略: ├─ 大图文章(微信长图文)→ 读本地 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 多模型分工** ```text ┌─────────────────────────────────────────────────────┐ │ 大模型调用矩阵 │ ├─────────────┬───────────────────────────────────────┤ │ 豆包(字节) │ 核心模块,处理职位信息(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)** ```text 输入图片 │ ├─ GIF → 转取第一帧 │ ├─ 超长图片? │ └─ img_cut 智能切图算法 │ OpenCV connectedComponents 连通组件检测 │ 切线碰到文字 → 智能上移,保证文字完整性 │ └─ PaddleOCR 识别 → 输出文字 ``` ### **5.3 存储与同步** ```text 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 ``` ## **六、完整数据流(端到端)** ```text 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 正式数据库 │ ▼ ⑩ 数据入库完成,供前端搜索/展示/推荐使用 ``` --- **项目分区导航**:[[01-链路追踪|链路追踪]] ⬅️ | 02-全链路流程梳理 | ➡️ [[01-main.py|main.py]]