Architecture clarification from user: spiderJobs/ is standalone execution,
NOT meant to be imported by app/. Correct dependency graph:
crawler_core ← shared base library
↑ ↑
spiderJobs app/services/crawler/
(standalone) (FastAPI backend, private layer)
Changes:
- boss.py/qcwy.py/zhilian.py: revert import back to private _boss_api etc.
- _boss/job51/zhilian_api.py: use crawler_core.base.Result/BaseFetcher/BaseSearcher
+ fix self._http → self.http_client
- _boss/job51/zhilian_client.py: use crawler_core.http_client.HTTPClient
+ _boss_client uses crawler_core.boss.sign.BossSign directly
- _boss/job51/zhilian_sign.py: backward-compat stubs → crawler_core.*.sign
Full regression: 106 passed in 0.68s
149 lines
5.5 KiB
Python
149 lines
5.5 KiB
Python
# ⚠️ DEPRECATED — 2026-03-21
|
|
# 此文件是内部手工复制文件,已废弃,不再由任何 facade 引用。
|
|
# 请改用 spiderJobs.platforms.* 或 crawler_core 中的对应模块。
|
|
# 将在下一里程碑中删除。
|
|
#
|
|
"""
|
|
智联招聘 - 所有 API 接口
|
|
复制自 spiderJobs/platforms/zhilian/api.py — import 改为本地引用
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from crawler_core.base import BaseFetcher, BaseSearcher
|
|
from app.services.crawler._zhilian_client import ZhilianClient, create_cgate_client, create_capi_client
|
|
|
|
|
|
_SEARCH_BODY = {
|
|
"eventScenario": "wxmpZhaopinSearchV2",
|
|
"filterMinSalary": 1,
|
|
"S_SOU_EXPAND": "SOU_COMPANY_ID",
|
|
"sortType": "DEFAULT",
|
|
"resumeNumber": "",
|
|
"version": "8.11.22",
|
|
"identity": 0,
|
|
"anonymous": 1,
|
|
}
|
|
|
|
_FILTER_KEYS = [
|
|
"S_SOU_SALARY", "S_SOU_EDUCATION_LOWESTLEVEL", "S_SOU_REFRESH_DATE",
|
|
"S_SOU_WORK_EXPERIENCE", "S_SOU_POSITION_TYPE", "S_SOU_COMPANY_TYPE",
|
|
"S_SOU_COMPANY_SCALE", "welfareLabels", "S_SOU_JD_INDUSTRY_LEVEL",
|
|
]
|
|
|
|
|
|
class SearchPositions(BaseSearcher):
|
|
ENDPOINT = "/positionbusiness/searchrecommend/searchPositions"
|
|
|
|
def __init__(
|
|
self, *, keyword: str = "", city_code: int | str = "",
|
|
collected_purpose: Optional[dict] = None,
|
|
filters: Optional[dict] = None, page_size: int = 15,
|
|
client: Optional[ZhilianClient] = None,
|
|
):
|
|
super().__init__(page_size=page_size, http_client=client or create_cgate_client())
|
|
self.keyword = keyword
|
|
self.city_code = city_code
|
|
self.collected_purpose = collected_purpose
|
|
self.filters = filters or {}
|
|
|
|
def _build_params(self, page_index: int) -> dict:
|
|
body = {**_SEARCH_BODY, "pageIndex": page_index, "pageSize": self.page_size}
|
|
if self.collected_purpose:
|
|
body.update(self._purpose_params(self.collected_purpose, page_index))
|
|
if self.keyword and "S_SOU_JD_JOB_LEVEL3" not in body:
|
|
body["S_SOU_FULL_INDEX"] = self.keyword
|
|
if self.city_code and "S_SOU_WORK_CITY" not in body:
|
|
body["S_SOU_WORK_CITY"] = self.city_code
|
|
body.update({k: self.filters[k] for k in _FILTER_KEYS if self.filters.get(k)})
|
|
return body
|
|
|
|
@staticmethod
|
|
def _purpose_params(purpose: dict, page_index: int) -> dict:
|
|
params: dict = {"pageIndex": page_index}
|
|
pnew = purpose.get("pnew_preferred_job_type", "")
|
|
name = purpose.get("job_type_name", "")
|
|
if pnew:
|
|
params["S_SOU_JD_JOB_LEVEL3"] = pnew
|
|
elif name:
|
|
params["S_SOU_FULL_INDEX"] = name
|
|
city = purpose.get("city_id", "") or purpose.get("preferred_location", "")
|
|
if city:
|
|
params["S_SOU_WORK_CITY"] = city
|
|
sal_min = purpose.get("preferred_salary_min", "")
|
|
sal_max = purpose.get("preferred_salary_max", "")
|
|
if sal_min not in ("", "-1") or sal_max != "":
|
|
params["S_SOU_SALARY"] = f"{sal_min},{sal_max}"
|
|
return params
|
|
|
|
|
|
class GetPositionDetail(BaseFetcher):
|
|
ENDPOINT = "/positionbusiness/position/getPositionModule"
|
|
|
|
def __init__(self, *, number: str, identity: int = 0, client: Optional[ZhilianClient] = None):
|
|
super().__init__(http_client=client or create_cgate_client())
|
|
self.number = number
|
|
self.identity = identity
|
|
|
|
def _build_params(self) -> dict:
|
|
return {"number": self.number, "identity": self.identity, "resumeNumber": ""}
|
|
|
|
|
|
class GetCompanyExtDetail(BaseFetcher):
|
|
ENDPOINT = "/riskstorm/company/getCompanyExtDetail"
|
|
|
|
def __init__(self, *, company_name: str, company_number: str, client: Optional[ZhilianClient] = None):
|
|
super().__init__(http_client=client or create_cgate_client())
|
|
self.company_name = company_name
|
|
self.company_number = company_number
|
|
|
|
def _build_params(self) -> dict:
|
|
return {"companyName": self.company_name, "companyNumber": self.company_number}
|
|
|
|
|
|
class GetCompanyDetail(BaseFetcher):
|
|
ENDPOINT = "/positionbusiness/exposure/companyDetail"
|
|
|
|
def __init__(self, *, number: str, client: Optional[ZhilianClient] = None):
|
|
super().__init__(http_client=client or create_cgate_client())
|
|
self.number = number
|
|
|
|
def _build_params(self) -> dict:
|
|
return {"number": self.number}
|
|
|
|
|
|
class SearchCompanyPositions(BaseSearcher):
|
|
ENDPOINT = "/capi/searchrecommend/searchPositionsCompany"
|
|
|
|
def __init__(
|
|
self, *, company_id: str, job_level: str = "",
|
|
city_code: str = "", page_size: int = 30,
|
|
client: Optional[ZhilianClient] = None,
|
|
):
|
|
self._client = client or create_capi_client()
|
|
super().__init__(page_size=page_size, http_client=self._client)
|
|
self.company_id = company_id
|
|
self.job_level = job_level
|
|
self.city_code = city_code
|
|
|
|
def _build_params(self, page_index: int) -> dict:
|
|
params = {**self._client.signer.sign_params()}
|
|
params.update({
|
|
"S_SOU_COMPANY_ID": self.company_id,
|
|
"S_SOU_POSITION_SOURCE_TYPE": "1",
|
|
"eventScenario": "wxmpZhaopinSearchPositionsCompany",
|
|
"pageCode": "wxmpZhaopinCompanyDetailPage",
|
|
"pageIndex": page_index,
|
|
"pageSize": self.page_size,
|
|
})
|
|
if self.job_level:
|
|
params["S_SOU_JD_JOB_LEVEL"] = self.job_level
|
|
if self.city_code:
|
|
params["S_SOU_WORK_CITY"] = self.city_code
|
|
return params
|
|
|
|
def _request(self, params: dict) -> tuple[int, Any]:
|
|
return self.http_client.get(self.ENDPOINT, params)
|