4.9 KiB
4.9 KiB
Phase 3: 前程无忧 & 智联重写 — 技术研究
研究日期: 2026-03-21
阶段目标: 前程无忧和智联招聘爬虫完全基于 crawler_core 运行,三平台统一使用新基类
1. 现状分析
1.1 crawler_core 现有基础(Phase 1 完成)
| 文件 | 内容 |
|---|---|
crawler_core/qcwy/sign.py |
Job51Sign.build_sign_path() — HMAC-SHA256 签名 |
crawler_core/zhilian/sign.py |
ZhilianSign.sign_headers()/sign_params() — 智联多类型签名 |
crawler_core/http_client.py |
HTTPClient — TLS 伪装 + 代理 + tenacity 重试 |
crawler_core/base.py |
Result[T], BaseFetcher, BaseSearcher |
1.2 前程无忧(job51)待迁移层
spiderJobs/platforms/job51/ 下已有全部文件:
| 文件 | 旧依赖 | 迁移目标 |
|---|---|---|
client.py |
spiderJobs.core.http_client.HTTPClient + spiderJobs.platforms.job51.sign.Job51Sign |
→ crawler_core.http_client.HTTPClient + crawler_core.qcwy.sign.Job51Sign |
api.py |
spiderJobs.core.base.ApiResult/BaseFetcher/BaseSearcher |
→ crawler_core.base.Result/BaseFetcher/BaseSearcher |
main.py |
spiderJobs.core.base.BaseFetcher/BaseSearcher |
→ crawler_core.base.BaseFetcher/BaseSearcher |
sign.py |
独立实现(与 crawler_core/qcwy/sign.py 相同) | → 向后兼容桩,重新导出 Job51Sign |
job51/api.py 具体变更:
- 第 14 行 import 替换
ApiResult全量替换为Result(共 11 处)- 第 164 行:
self._http.get(endpoint)→self.http_client.get(endpoint) - 第 208 行:
self._http.get(self.ENDPOINT, ...)→self.http_client.get(self.ENDPOINT, ...)
1.3 智联招聘(zhilian)待迁移层
spiderJobs/platforms/zhilian/ 下已有全部文件:
| 文件 | 旧依赖 | 迁移目标 |
|---|---|---|
client.py |
spiderJobs.core.http_client.HTTPClient + spiderJobs.platforms.zhilian.sign.ZhilianSign |
→ crawler_core.http_client.HTTPClient + crawler_core.zhilian.sign.ZhilianSign |
api.py |
spiderJobs.core.base.BaseFetcher/BaseSearcher |
→ crawler_core.base.BaseFetcher/BaseSearcher |
main.py |
spiderJobs.core.base.BaseFetcher/BaseSearcher |
→ crawler_core.base.BaseFetcher/BaseSearcher |
sign.py |
独立实现(与 crawler_core/zhilian/sign.py 相同) | → 向后兼容桩,重新导出 ZhilianSign |
zhilian/api.py 具体变更:
- 第 10 行 import 替换(无 ApiResult,zhilian 使用 crawler_core 的默认解析器,无需自定义 _parse_response)
- 第 200 行:
return self._http.get(→return self.http_client.get(
重要差异: 智联 api.py 中 SearchCompanyPositions._build_params() 第 184 行使用了 self._client.signer.sign_params(),这是通过 self._client(设为传入的 ZhilianClient)间接访问 signer 的,迁移后不受影响(属性名不变)。
2. 对比 Phase 2(Boss)的工作量
| 维度 | Phase 2 Boss | Phase 3 job51 | Phase 3 zhilian |
|---|---|---|---|
ApiResult 替换 |
11 处 | 11 处 | 0 处(无自定义解析器) |
self._http 替换 |
3 处 | 2 处 | 1 处 |
| sign.py → 桩 | ✓ | ✓ | ✓ |
| client.py import | ✓ | ✓ | ✓ |
| api.py import | ✓ | ✓ | ✓ |
| main.py import | ✓ | ✓ | ✓ |
3. mock 测试策略
3.1 job51 测试(tests/job51/test_job51_client.py)
job51 的 mock 策略与 Boss 完全一致:用 MagicMock() mock http_client,测试:
_parse_job51_response(纯函数,覆盖 status/1 成功、非 1 失败、HTTP 错误)SearchRecommendJobs.search()(正常、HTTP 错误)GetJobDetail.fetch()(成功,异常捕获)GetCompanyDetail.fetch()(成功)Job51Client._job51_headers()(sign 注入)
3.2 zhilian 测试(tests/zhilian/test_zhilian_client.py)
智联的请求走 POST(cgate)或 GET(capi),mock 方式相同:
SearchPositions.search()(正常、错误)GetPositionDetail.fetch()(成功)SearchCompanyPositions.search()(成功,特别验证 sign_params 被调用)ZhilianClient.post/get(验证签名头注入)
4. 成功标准验证
| 标准 | 验证方式 |
|---|---|
| job51 继承 BaseFetcher/BaseSearcher | issubclass() 断言 |
| zhilian 继承 BaseFetcher/BaseSearcher | issubclass() 断言 |
| 两平台无内联签名或 HTTP 样板 | grep 无 requests import,无 hmac 在 client/api 中 |
| mock 测试通过 | pytest tests/job51/ tests/zhilian/ |
| 三平台代码结构一致 | 代码审查:client/api/sign/main 四文件结构 |
RESEARCH COMPLETE
Phase 3 可以规划,分 2 个 PLAN:
- Plan 01:迁移前程无忧(job51)层 + mock 测试
- Plan 02:迁移智联招聘(zhilian)层 + mock 测试
两个 Plan 无依赖关系,理论上可并行,但顺序执行更稳妥。