NanmiCoder/MediaCrawler · error · ForbiddenError
{response.text}
Error message
{response.text} What it means
ForbiddenError raised by ZhiHuClient.request when a signed request returns HTTP 403. For zhihu this means the signature (x-zse-96) was rejected or the session/IP is forbidden; the exception message carries the raw response body for diagnosis.
Source
Thrown at media_platform/zhihu/client.py:109
url: Request URL
**kwargs: Other request parameters such as headers, body, etc.
Returns:
"""
# Check if proxy is expired before each request
await self._refresh_proxy_if_expired()
# return response.text
return_response = kwargs.pop('return_response', False)
async with make_async_client(proxy=self.proxy) as client:
response = await client.request(method, url, timeout=self.timeout, **kwargs)
if response.status_code != 200:
utils.logger.error(f"[ZhiHuClient.request] Requset Url: {url}, Request error: {response.text}")
if response.status_code == 403:
raise ForbiddenError(response.text)
elif response.status_code == 404: # Content without comments also returns 404
return {}
raise DataFetchError(response.text)
if return_response:
return response.text
try:
data: Dict = response.json()
if data.get("error"):
utils.logger.error(f"[ZhiHuClient.request] Request error: {data}")
raise DataFetchError(data.get("error", {}).get("message"))
return data
except json.JSONDecodeError:
utils.logger.error(f"[ZhiHuClient.request] Request error: {response.text}")
raise DataFetchError(response.text)
async def get(self, uri: str, params=None, **kwargs) -> Union[Response, Dict, str]:View on GitHub (pinned to d6f7c5bb90)
Solutions
- Refresh zhihu cookies (re-login) first - cheapest fix - and retry.
- If 403 persists with fresh cookies, update the zhihu signing implementation (x-zse-96 algorithm version) to match the current site build.
- Enable/rotate proxies if the IP is the blocked factor.
- Catch ForbiddenError at loop level to back off instead of immediate retries.
Example fix
// before
data = await zhihu_client.request("GET", url)
// after
from media_platform.zhihu.exception import ForbiddenError
try:
data = await zhihu_client.request("GET", url)
except ForbiddenError as e:
utils.logger.error(f"zhihu 403: {e}")
await asyncio.sleep(30)
raise Defensive patterns
Strategy: retry
Try / catch
from media_platform.zhihu.exception import ForbiddenError
try:
data = await zhihu_client.request("GET", url)
except ForbiddenError as e:
utils.logger.error(f"zhihu 403 body: {e}")
await asyncio.sleep(60)
raise Prevention
- Refresh cookies at the first 403 - stale credentials invalidate signatures.
- Track zhihu signature-algorithm updates (x-zse-96 version) and update the signer.
- Rotate IPs when 403 persists across fresh cookies.
When it happens
Trigger: Any zhihu API call where the signing algorithm version no longer matches the site (signature rejected with 403), the cookie is expired so the signature is computed over stale credentials, or the IP is blocked by zhihu risk control.
Common situations: Zhihu updates their signature algorithm and the local signer becomes outdated; long crawl with expired login; datacenter IP flagged by zhihu.
Related errors
- XHS request blocked with HTTP {response.status_code}
- d_c0 not found in cookies
- No crawler is running
- Request failed, method: {method}, url: {url}, status code: {
- get weibo detail err: {response.text}
AI-assisted analysis of NanmiCoder/MediaCrawler@d6f7c5bb90 (2026-08-15).
Data as JSON: /api/errors/2f2b94cc9c9f53f9.
Report an issue: GitHub.