6.5 KiB
Phase 2: Boss 直聘重写 — 技术研究
研究日期: 2026-03-21
阶段目标: Boss 直聘爬虫完全基于 crawler_core 运行,旧实现可安全停用
1. 现状分析
1.1 crawler_core 现有基础(Phase 1 完成)
| 文件 | 内容 |
|---|---|
crawler_core/http_client.py |
HTTPClient — requests_go + TLS 伪装 + tenacity 重试(min=10s) + 代理池/隧道代理 |
crawler_core/base.py |
Result[T](泛型)、BaseFetcher、BaseSearcher、parse_response |
crawler_core/boss/sign.py |
BossSign.generate_traceid() — 已完成且有测试 |
1.2 待迁移的 Boss 层(spiderJobs)
spiderJobs/platforms/boss/ 下已有:
| 文件 | 当前依赖 | 迁移目标 |
|---|---|---|
sign.py |
独立实现(与 crawler_core/boss/sign.py 功能相同) | 弃用,改为 import crawler_core |
client.py |
spiderJobs.core.http_client.HTTPClient |
改为 crawler_core.http_client.HTTPClient |
api.py |
spiderJobs.core.base.ApiResult/BaseFetcher/BaseSearcher |
改为 crawler_core.base.Result/BaseFetcher/BaseSearcher |
main.py |
spiderJobs.core.base.BaseFetcher/BaseSearcher |
更新 import,功能保持不变 |
1.3 待保留的反爬机制(SmartIPManager)
旧 boos_api.py 中有 SmartIPManager(代理轮换+本机 fallback),这套逻辑已被 crawler_core 的 HTTPClient 代理池简化替代:
HTTPClient(proxy_pool=...)自动随机选择代理(每次请求)HTTPClient(tunnel_proxy=...)每次新建 session(效果类似隧道代理的 IP 轮换)- tenacity 重试 min=10s 已满足强制延迟要求
结论:不需要将 SmartIPManager 迁移进来,crawler_core 已经覆盖了其功能。
2. API 接口清单(已确认)
spiderJobs/platforms/boss/api.py 实现了 4 个接口:
| 类 | 端点 | 方式 |
|---|---|---|
SearchRecJobs |
/wapi/zpgeek/miniapp/homepage/recjoblist.json |
GET |
GetJobDetail |
/wapi/batch/requests(批量子请求) |
POST |
GetBrandDetail |
/wapi/zpgeek/miniapp/brand/detail.json |
GET |
SearchBrandJobs |
/wapi/zpgeek/miniapp/brand/joblist.json |
GET |
Boss 响应格式与 crawler_core 默认解析不同:
- Boss:
code=0表示成功,zpData为业务数据(而非statusCode/data) - 需要在迁移后的 api.py 中保留自定义
_parse_boss_response()函数
3. 迁移差异分析
3.1 ApiResult → Result[T]
spiderJobs.core.base.ApiResult 与 crawler_core.base.Result[T] 字段对比:
| 字段 | ApiResult | Result[T] | 兼容? |
|---|---|---|---|
success |
✓ | ✓ | ✅ |
status_code |
✓ | ✓ | ✅ |
data |
✓ | ✓ | ✅ |
list |
✓ | ✓ | ✅ |
count |
✓ | ✓ | ✅ |
is_end_page |
✓ | ✓ | ✅ |
error |
✓ | ✓ | ✅ |
完全兼容,仅需修改 import 路径。
3.2 BaseFetcher._http → BaseFetcher.http_client
旧 spiderJobs 的 BaseFetcher 使用 self._http 引用 HTTP 客户端,而 crawler_core.base.BaseFetcher 使用 self.http_client。
api.py 中两处引用需要更新:
SearchRecJobs._request():self._http.get(...)→self.http_client.get(...)GetJobDetail.fetch():client: BossClient = self._http→client: BossClient = self.http_clientSearchBrandJobs._request():self._http.get(...)→self.http_client.get(...)
3.3 BossClient 迁移
client.py 中 BossClient 继承的是 spiderJobs.core.http_client.HTTPClient,需改为继承 crawler_core.http_client.HTTPClient。两个 HTTPClient 接口完全相同,无结构性差异。
sign.py(spiderJobs 版)与 crawler_core/boss/sign.py 功能完全相同,迁移后 client.py 直接从 crawler_core.boss.sign 导入即可。
4. 测试策略(QUAL-03)
4.1 测试框架
项目已有:
conftest.py在项目根目录(用于 pytest path 设置)tests/crawler_core/已有 41 个签名单元测试(Phase 1)
建议新增测试目录:tests/boss/,文件:test_boss_client.py
4.2 Mock/Respx 测试(注意事项)
crawler_core 的 HTTPClient 使用 requests_go(非标准 requests),respx 专门 mock httpx,不适用。
替代方案:使用 unittest.mock.patch
# 正确的 mock 方式
from unittest.mock import MagicMock, patch
def test_search_rec_jobs_success():
mock_client = MagicMock()
mock_client.get.return_value = (200, {
"code": 0, "zpData": {"jobList": [{"title": "测试职位"}], "hasMore": False}
})
searcher = SearchRecJobs(client=mock_client)
result = searcher.search(page_index=1)
assert result.success
assert len(result.list) == 1
由于 HTTPClient 是作为依赖注入传入的,直接用 MagicMock() mock 即可,无需 patch 装饰器。
5. 关键词搜索接口说明
当前 SearchRecJobs 只使用 cityCode 过滤,没有关键词搜索参数。
查看 Boss API,关键词搜索应使用 /wapi/zpgeek/miniapp/search.json(需要 query 参数)或直接使用 recjoblist 接口配合 query 字段(如果 API 支持)。
推荐方案: Phase 2 保持现有 SearchRecJobs(推荐列表),关键词过滤通过 main.py 的城市映射传入。真正的关键词搜索 API 端点验证留给手动测试阶段确认。
6. 反爬机制验证
Phase 2 成功标准之一是"反爬机制保留",具体验证点:
| 机制 | 实现位置 | 验证方式 |
|---|---|---|
| 随机延迟 10-20s | HTTPClient tenacity min=10s |
单元测试检查 wait_random_exponential 配置 |
| TLS 指纹伪装 | HTTPClient._new_session() → TLS_CHROME_LATEST |
代码审查(无法 mock TLS 层) |
| 代理轮换 | HTTPClient(tunnel_proxy=...) 每次新建 session |
单元测试确认 _new_session() 被调用 |
| Traceid 注入 | BossClient._boss_headers() |
单元测试检查请求头包含 Traceid |
7. Validation Architecture(Nyquist)
暂不适用本阶段:Phase 2 是代码迁移,无新功能/新接口,验证维度以单元测试+手动验证为主。
RESEARCH COMPLETE
Phase 2 可以规划。 迁移任务明确,风险低(接口兼容),主要工作量在:
- 更新 3 个文件的 import(client.py、api.py、main.py)
- 修正 2 处
self._http→self.http_client引用 - 新增 mock 测试
预计拆分为 2 个 PLAN:
- Plan 01:迁移 client.py、api.py、main.py(去除 spiderJobs.core 依赖)
- Plan 02:新增
tests/boss/test_boss_client.pymock 测试