mirror of
https://github.com/NanmiCoder/MediaCrawler.git
synced 2026-09-25 21:58:12 +08:00
fix(dy): 补上 ArgusSecurityPlugin 要求的 x-tt-argus 请求头
抖音在边缘网关新挂了 ArgusSecurityPlugin,对 aweme/detail、aweme/post 这批接口做 业务前置校验:缺少 x-tt-argus 时直接 403,响应体为 "Blocked by ArgusSecurityPlugin Uifid Not Found";只补 uifid 参数但仍没有这个头 则是 "... Signature Not Found"——后者很容易被误判成 a_bogus / verifyFp 的问题。 网关当前不校验该头取值,传固定字符串即可(实测 "1" 就够)。 参考 https://github.com/Johnserf-Seed/f2/issues/443 注意这是权宜之计:网关哪天升级到真校验该值,会重新出现 Signature Not Found, 届时需要改为页面内注入 JS 让抖音自带 SDK 补齐 Argus 头。 验证:真实 cookie 下 get_video_by_id 恢复可用。 - client.py: 默认请求头补 x-tt-argus,cookie 有 UIFID 时带 uifid 头 (两种都没有则不发,避免被当成「有但为空」) - 新增 tests/test_douyin_argus_header.py(不发网络请求)
This commit is contained in:
@@ -39,6 +39,10 @@ from .exception import *
|
||||
from .field import *
|
||||
from .help import *
|
||||
|
||||
# 抖音边缘网关 ArgusSecurityPlugin 要求的请求头。网关目前不校验取值,
|
||||
# 传固定字符串即可;将来若开始真校验,会重新出现 "Signature Not Found"。
|
||||
DOUYIN_ARGUS_HEADER_VALUE = "1"
|
||||
|
||||
|
||||
class DouYinClient(AbstractApiClient, ProxyRefreshMixin):
|
||||
|
||||
@@ -55,6 +59,15 @@ class DouYinClient(AbstractApiClient, ProxyRefreshMixin):
|
||||
self.proxy = proxy
|
||||
self.timeout = timeout
|
||||
self.headers = headers
|
||||
# 抖音边缘网关的 ArgusSecurityPlugin 会对这批接口做业务前置校验,缺少
|
||||
# x-tt-argus 头时直接 403,响应体为
|
||||
# "Blocked by ArgusSecurityPlugin Uifid Not Found"(补了 uifid 但没这个头则是
|
||||
# "... Signature Not Found")。当前网关尚未校验该头的值,可传任意字符串;
|
||||
# 一旦升级到真校验,需要改为 WebView 内注入 JS 让页面自带 SDK 补齐。
|
||||
self.headers.setdefault("x-tt-argus", DOUYIN_ARGUS_HEADER_VALUE)
|
||||
uifid = cookie_dict.get("UIFID") or cookie_dict.get("UIFID_TEMP", "")
|
||||
if uifid:
|
||||
self.headers.setdefault("uifid", uifid)
|
||||
self._host = "https://www.douyin.com"
|
||||
self.cookie_urls = [
|
||||
"https://douyin.com",
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2025 relakkes@gmail.com
|
||||
#
|
||||
# This file is part of MediaCrawler project.
|
||||
# Repository: https://github.com/NanmiCoder/MediaCrawler/blob/main/tests/test_douyin_argus_header.py
|
||||
# GitHub: https://github.com/NanmiCoder
|
||||
# Licensed under NON-COMMERCIAL LEARNING LICENSE 1.1
|
||||
#
|
||||
|
||||
"""抖音 ArgusSecurityPlugin 请求头的回归测试。
|
||||
|
||||
回归背景:抖音在边缘网关挂了 ArgusSecurityPlugin,对一批接口做业务前置校验。
|
||||
缺少 ``x-tt-argus`` 请求头时直接 403,响应体为
|
||||
``Blocked by ArgusSecurityPlugin Uifid Not Found``;补上 uifid 参数但仍没有这个头
|
||||
则是 ``... Signature Not Found``(容易误导成 a_bogus / verifyFp 的问题)。
|
||||
网关当前不校验该头的取值,传固定字符串即可。
|
||||
|
||||
这里不发起任何网络请求,只断言客户端默认请求头带上了这两个头。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from media_platform.douyin.client import DOUYIN_ARGUS_HEADER_VALUE, DouYinClient
|
||||
|
||||
COOKIE_DICT = {
|
||||
"sessionid": "fake-session",
|
||||
"UIFID": "uifid-from-cookie",
|
||||
"UIFID_TEMP": "uifid-temp-from-cookie",
|
||||
}
|
||||
|
||||
|
||||
class _StubPage:
|
||||
async def evaluate(self, expression): # noqa: ANN001
|
||||
return {}
|
||||
|
||||
|
||||
def _make_client(cookie_dict: dict) -> DouYinClient:
|
||||
return DouYinClient(
|
||||
headers={"User-Agent": "test-user-agent", "Cookie": "a=1"},
|
||||
playwright_page=_StubPage(),
|
||||
cookie_dict=cookie_dict,
|
||||
)
|
||||
|
||||
|
||||
def test_argus_header_is_present_by_default():
|
||||
"""x-tt-argus 必须在默认请求头里"""
|
||||
client = _make_client(COOKIE_DICT)
|
||||
|
||||
assert client.headers.get("x-tt-argus") == DOUYIN_ARGUS_HEADER_VALUE
|
||||
|
||||
|
||||
def test_uifid_header_comes_from_cookie():
|
||||
"""uifid 头取自 cookie 里的 UIFID"""
|
||||
client = _make_client(COOKIE_DICT)
|
||||
|
||||
assert client.headers.get("uifid") == "uifid-from-cookie"
|
||||
|
||||
|
||||
def test_uifid_header_falls_back_to_uifid_temp():
|
||||
"""没有 UIFID 时退到 UIFID_TEMP"""
|
||||
client = _make_client({"sessionid": "s", "UIFID_TEMP": "temp-only"})
|
||||
|
||||
assert client.headers.get("uifid") == "temp-only"
|
||||
|
||||
|
||||
def test_missing_uifid_omits_header():
|
||||
"""cookie 里两种都没有时不发这个头(发空值可能被当成「有但为空」)"""
|
||||
client = _make_client({"sessionid": "s"})
|
||||
|
||||
assert client.headers.get("uifid") is None
|
||||
assert client.headers.get("x-tt-argus") == DOUYIN_ARGUS_HEADER_VALUE
|
||||
|
||||
|
||||
def test_caller_supplied_values_win():
|
||||
"""调用方显式传了同名头时不覆盖"""
|
||||
client = DouYinClient(
|
||||
headers={"User-Agent": "ua", "Cookie": "a=1", "x-tt-argus": "custom"},
|
||||
playwright_page=_StubPage(),
|
||||
cookie_dict=COOKIE_DICT,
|
||||
)
|
||||
|
||||
assert client.headers.get("x-tt-argus") == "custom"
|
||||
Reference in New Issue
Block a user