This commit is contained in:
Your Name
2026-04-22 14:13:21 +08:00
parent e0406b5d0e
commit 7cbde2aa78
145 changed files with 23086 additions and 2243 deletions

View File

@@ -8,15 +8,121 @@
@explain : 百度域名检测工具 - 包含百度搜索结果查询和百度安全API检测功能
'''
import os
import random # 随机数生成模块,用于随机选择浏览器类型
import re # 正则表达式模块用于从HTML中提取搜索结果
import threading
import time
from loguru import logger # 日志记录模块,用于记录程序运行日志
from curl_cffi import requests # curl_cffi库支持浏览器模拟的HTTP请求
from typing import List, Optional, Union, Any # 类型提示
def check_site(domain: str, sensitive_words: Optional[List[str]] = None, proxies: dict = None) -> Union[
BAIDU_SITE_TIMEOUT_PROXY = max(
0.8,
float(os.getenv("DOMAINCHECK_BAIDU_TIMEOUT_PROXY", "1.4") or 1.4),
)
BAIDU_SITE_TIMEOUT_DIRECT = max(
1.0,
float(os.getenv("DOMAINCHECK_BAIDU_TIMEOUT_DIRECT", "1.8") or 1.8),
)
BAIDU_SCAN_TIMEOUT_PROXY = max(
1.0,
float(os.getenv("DOMAINCHECK_BAIDU_SCAN_TIMEOUT_PROXY", "1.6") or 1.6),
)
BAIDU_SCAN_TIMEOUT_DIRECT = max(
1.2,
float(os.getenv("DOMAINCHECK_BAIDU_SCAN_TIMEOUT_DIRECT", "2.0") or 2.0),
)
BAIDU_SITE_URL = 'https://m.baidu.com/s'
BAIDU_SAFE_SCAN_URL = 'https://mobsec-sec.baidu.com/3.1/scanurl'
BAIDU_SITE_PATTERN = r'<!--s-text-->([^<]+)<!--/s-text-->'
_THREAD_LOCAL = threading.local()
def _resolve_baidu_timeout(proxies: dict = None, budget_seconds: Optional[float] = None, *, scan: bool = False) -> float:
if scan:
timeout = BAIDU_SCAN_TIMEOUT_PROXY if proxies else BAIDU_SCAN_TIMEOUT_DIRECT
else:
timeout = BAIDU_SITE_TIMEOUT_PROXY if proxies else BAIDU_SITE_TIMEOUT_DIRECT
timeout = float(timeout or 0.0)
if budget_seconds not in (None, "", 0, "0"):
timeout = min(timeout, max(0.6, float(budget_seconds or 0.0)))
return max(0.6, timeout)
BAIDU_SITE_COOKIES = {
'BIDUPSID': '053DBE4D820C6EFB729DC7B13B1F82B2',
'PSTM': '1775657119',
'H_PS_PSSID': '63148_67862_67986_68002_68142_68148_68152_68141_68165_68189_68226_68267_68296_68336_68369_68453_68438_68464_68541_68546_68558_68520_68589_68621_68615_68606_68601_68682_68671_68735_68544_68733_68766_68807_68901_68918_68836_68921_68955_68976_68997_69007_69010_69018_69024_69014',
'BAIDUID': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'BDORZ': 'B490B5EBF6F3CD402E515D22BCDA1598',
'BAIDUID_BFESS': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'BA_HECTOR': 'ah0l24210la4050l0g0k0g2g0h20071ktcocj27',
'ZFY': 'Qxyr75Xm7o8zUxYzuFnYoW7cnS:AnVp:BpnrNVkr3usno:C',
'delPer': '0',
'BAIDUID_REF': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'H_WISE_SIDS': '110085_661771_667681_673676_683089_682564_685373_660925_687556_686285_690306_690303_690478_690576_690334_691545_685595_690883_692754_692777_692118_692910_693221_693364_693403_693656_693941_694124_693392_694171_694236_693996_694316_694324_693886_694409_694379_694696_694780_694783_692006_694884_694918_694996_694991_695032_695118_694840_695140_695193_695110_695262_688610_694933_695278_694865_695214_695374_690651_694577_692378_695452_695457_695480_695603_695642_695631_695668_695824_695843_695866_695888_695892_695897_695975_694177_695900_695939_696145_696150_696153_696069_696078_696066_694359_696288_696308_694985_696297_695715_696128_696313_696457_696431_696473_696492_696590_693385_696610_696682_696679_696662_696651_696655_696658_696673_696643_696110_696729_696733_696772_8000116_8000133_8000138_8000159_8000163_8000167_8000176_8000186_8000190_8000204',
'H_WISE_SIDS_BFESS': '110085_661771_667681_673676_683089_682564_685373_660925_687556_686285_690306_690303_690478_690576_690334_691545_685595_690883_692754_692777_692118_692910_693221_693364_693403_693656_693941_694124_693392_694171_694236_693996_694316_694324_693886_694409_694379_694696_694780_694783_692006_694884_694918_694996_694991_695032_695118_694840_695140_695193_695110_695262_688610_694933_695278_694865_695214_695374_690651_694577_692378_695452_695457_695480_695603_695642_695631_695668_695824_695843_695866_695888_695892_695897_695975_694177_695900_695939_696145_696150_696153_696069_696078_696066_694359_696288_696308_694985_696297_695715_696128_696313_696457_696431_696473_696492_696590_693385_696610_696682_696679_696662_696651_696655_696658_696673_696643_696110_696729_696733_696772_8000116_8000133_8000138_8000159_8000163_8000167_8000176_8000186_8000190_8000204',
'MSA_PBT': '147',
'MSA_ZOOM': '1000',
'wpr': '0',
'COOKIE_SESSION': '0_0_0_0_0_0_0_0_0_0_0_0_0_1775657396%7C1%230_0_0_0_0_0_0_0_1775657396%7C1',
'MSA_PHY_WH': '1440_3440',
'POLYFILL': '0',
'MSA_WH': '1254_940',
'kleck': '7ce2abac229d2e8ba435a8bcc6256f57f53e9d08954443bf',
'PSCBD': '16%3A1%3A3',
'SE_LAUNCH': '5%3A1775657396_16%3A29594323%3A3',
'BDSVRTM': '3',
'PSINO': '6',
'__bsi': '17976785662214065461_00_7_R_R_6_0303_c02f_Y',
}
BAIDU_SITE_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0',
'sec-ch-ua': '"Chromium";v="146", "Not-A.Brand";v="24", "Microsoft Edge";v="146"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
BAIDU_SAFE_SCAN_HEADERS = {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json; charset=utf-8',
'Host': 'mobsec-sec.baidu.com',
'User-Agent': 'okhttp/3.12.12',
}
BAIDU_SAFE_SCAN_PARAMS = {
'auth_ver': '2',
'appkey': '4665fd2c6b0922a551e8ae74',
'nonce': '1751309255340',
'lc': '77qHTv4VtmRiXYtd',
'pkg': 'com.baidu.searchbox',
'vc': '-1',
'cuid': 'CF0E6FCCD824D146605C16AC1C8BAC95%7CVFWAO56TC',
'tk': '',
'type': '3',
'vn': '2.6.0',
's': 'fdfef2ce96c1c7193f9b80425020f63e',
}
def _get_thread_session():
session = getattr(_THREAD_LOCAL, "session", None)
if session is None:
session = requests.Session()
_THREAD_LOCAL.session = session
return session
def check_site(domain: str, sensitive_words: Optional[List[str]] = None, proxies: dict = None, budget_seconds: Optional[float] = None) -> Union[
None, tuple[bool, str], list[Any]]:
'''
通过百度搜索查询域名的相关信息
@@ -31,80 +137,41 @@ def check_site(domain: str, sensitive_words: Optional[List[str]] = None, proxies
if sensitive_words is None:
sensitive_words = []
# 添加重试机制
max_retries = 3
for retry in range(max_retries):
try: # try-except块用于捕获网络请求中的异常
try: # try-except块用于捕获网络请求中的异常
# 构建搜索查询参数
# wd: 搜索关键词格式为site:域名
params = {
'wd': f'site:{domain}' # 搜索site:domain限制搜索结果为指定域名
}
cookies = {
'BIDUPSID': '053DBE4D820C6EFB729DC7B13B1F82B2',
'PSTM': '1775657119',
'H_PS_PSSID': '63148_67862_67986_68002_68142_68148_68152_68141_68165_68189_68226_68267_68296_68336_68369_68453_68438_68464_68541_68546_68558_68520_68589_68621_68615_68606_68601_68682_68671_68735_68544_68733_68766_68807_68901_68918_68836_68921_68955_68976_68997_69007_69010_69018_69024_69014',
'BAIDUID': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'BDORZ': 'B490B5EBF6F3CD402E515D22BCDA1598',
'BAIDUID_BFESS': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'BA_HECTOR': 'ah0l24210la4050l0g0k0g2g0h20071ktcocj27',
'ZFY': 'Qxyr75Xm7o8zUxYzuFnYoW7cnS:AnVp:BpnrNVkr3usno:C',
'delPer': '0',
'BAIDUID_REF': '053DBE4D820C6EFB729DC7B13B1F82B2:FG=1',
'H_WISE_SIDS': '110085_661771_667681_673676_683089_682564_685373_660925_687556_686285_690306_690303_690478_690576_690334_691545_685595_690883_692754_692777_692118_692910_693221_693364_693403_693656_693941_694124_693392_694171_694236_693996_694316_694324_693886_694409_694379_694696_694780_694783_692006_694884_694918_694996_694991_695032_695118_694840_695140_695193_695110_695262_688610_694933_695278_694865_695214_695374_690651_694577_692378_695452_695457_695480_695603_695642_695631_695668_695824_695843_695866_695888_695892_695897_695975_694177_695900_695939_696145_696150_696153_696069_696078_696066_694359_696288_696308_694985_696297_695715_696128_696313_696457_696431_696473_696492_696590_693385_696610_696682_696679_696662_696651_696655_696658_696673_696643_696110_696729_696733_696772_8000116_8000133_8000138_8000159_8000163_8000167_8000176_8000186_8000190_8000204',
'H_WISE_SIDS_BFESS': '110085_661771_667681_673676_683089_682564_685373_660925_687556_686285_690306_690303_690478_690576_690334_691545_685595_690883_692754_692777_692118_692910_693221_693364_693403_693656_693941_694124_693392_694171_694236_693996_694316_694324_693886_694409_694379_694696_694780_694783_692006_694884_694918_694996_694991_695032_695118_694840_695140_695193_695110_695262_688610_694933_695278_694865_695214_695374_690651_694577_692378_695452_695457_695480_695603_695642_695631_695668_695824_695843_695866_695888_695892_695897_695975_694177_695900_695939_696145_696150_696153_696069_696078_696066_694359_696288_696308_694985_696297_695715_696128_696313_696457_696431_696473_696492_696590_693385_696610_696682_696679_696662_696651_696655_696658_696673_696643_696110_696729_696733_696772_8000116_8000133_8000138_8000159_8000163_8000167_8000176_8000186_8000190_8000204',
'MSA_PBT': '147',
'MSA_ZOOM': '1000',
'wpr': '0',
'COOKIE_SESSION': '0_0_0_0_0_0_0_0_0_0_0_0_0_1775657396%7C1%230_0_0_0_0_0_0_0_1775657396%7C1',
'MSA_PHY_WH': '1440_3440',
'POLYFILL': '0',
'MSA_WH': '1254_940',
'kleck': '7ce2abac229d2e8ba435a8bcc6256f57f53e9d08954443bf',
'PSCBD': '16%3A1%3A3',
'SE_LAUNCH': '5%3A1775657396_16%3A29594323%3A3',
'BDSVRTM': '3',
'PSINO': '6',
'__bsi': '17976785662214065461_00_7_R_R_6_0303_c02f_Y',
}
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0',
'sec-ch-ua': '"Chromium";v="146", "Not-A.Brand";v="24", "Microsoft Edge";v="146"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
# 百度搜索URL
url = 'https://m.baidu.com/s'
# 发送GET请求获取搜索结果
# url: 请求URL
# params: 查询参数
# headers: HTTP请求头
# timeout=15: 超时时间15秒
# proxies: 代理配置
# impersonate: 模拟的浏览器类型
# .content: 获取响应内容(字节流)
# .decode('utf-8'): 解码为UTF-8字符串
response_text = requests.get(url, params=params, headers=headers, cookies=cookies, timeout=15,
proxies=proxies).content.decode('utf-8')
session = _get_thread_session()
request_started_at = time.perf_counter()
logger.info(
f"百度site阶段: domain={domain} | stage=request_start | proxy={'yes' if proxies else 'no'}"
)
response = session.get(
BAIDU_SITE_URL,
params=params,
headers=BAIDU_SITE_HEADERS,
cookies=BAIDU_SITE_COOKIES,
timeout=_resolve_baidu_timeout(proxies, budget_seconds=budget_seconds, scan=False),
proxies=proxies,
)
logger.info(
f"百度site阶段: domain={domain} | stage=request_done | status={response.status_code} "
f"| elapsed_ms={int((time.perf_counter() - request_started_at) * 1000)}"
)
response_text = response.content.decode('utf-8', errors='ignore')
# 使用正则表达式提取搜索结果标题
# re.findall()返回所有匹配的结果列表
# 正则解释:匹配 <!--s-text--> 和 <!--/s-text--> 之间的内容
match = re.findall(r'<!--s-text-->([^<]+)<!--/s-text-->', response_text,re.S)
match = re.findall(BAIDU_SITE_PATTERN, response_text, re.S)
# 返回匹配结果列表,如果没有匹配则返回空列表
search_results= match if match else []
logger.info(
f"百度site阶段: domain={domain} | stage=parsed | result_count={len(search_results)}"
)
# 如果没有搜索结果,直接返回通过
if not search_results:
@@ -120,19 +187,12 @@ def check_site(domain: str, sensitive_words: Optional[List[str]] = None, proxies
# 所有搜索结果都不包含敏感词,返回通过
return True, ''
except Exception as e: # 捕获所有异常
# logger.warning(f"百度site检测第{retry+1}次失败: {domain}, 错误: {e}")
if retry < max_retries - 1:
import time
time.sleep(2) # 等待2秒后重试
proxies=None
else:
# 达到最大重试次数,返回检测失败
logger.error(f"百度site检测失败: {domain}, 已达到最大重试次数")
return False, str(e) # 返回错误信息
except Exception as e: # 捕获所有异常
logger.error(f"百度site检测失败: {domain}, 错误: {e}")
return False, str(e)
def baidu(domain: str, proxies: dict = None) -> str:
def baidu(domain: str, proxies: dict = None, budget_seconds: Optional[float] = None) -> str:
'''
通过百度安全API检测域名安全状态
@@ -142,29 +202,6 @@ def baidu(domain: str, proxies: dict = None) -> str:
:param proxies: 代理配置(如:{'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'}
:return: str - 安全状态描述("风险网站提示""高危网站提示""正常"),如果检测失败则返回错误信息
'''
# 构建HTTP请求头
headers = {
'Cache-Control': 'no-cache', # 不使用缓存
'Content-Type': 'application/json; charset=utf-8', # 内容类型为JSON
'Host': 'mobsec-sec.baidu.com', # 目标主机
'User-Agent': 'okhttp/3.12.12', # 模拟OKHttp客户端
}
# 构建请求参数
params = {
'auth_ver': '2', # 认证版本
'appkey': '4665fd2c6b0922a551e8ae74', # API应用密钥
'nonce': '1751309255340', # 随机数(注意:实际使用时应该动态生成)
'lc': '77qHTv4VtmRiXYtd', # 本地配置参数
'pkg': 'com.baidu.searchbox', # 应用包名
'vc': '-1', # 版本码
'cuid': 'CF0E6FCCD824D146605C16AC1C8BAC95%7CVFWAO56TC', # 设备ID注意实际使用时应该动态生成
'tk': '', # 令牌(空)
'type': '3', # 检测类型
'vn': '2.6.0', # 版本号
's': 'fdfef2ce96c1c7193f9b80425020f63e', # 签名(注意:实际使用时应该动态生成)
}
# 构建JSON请求数据
json_data = {
'url': f'https://{domain}/', # 待检测的URL添加https协议头
@@ -173,10 +210,7 @@ def baidu(domain: str, proxies: dict = None) -> str:
# 提取URL作为后续获取结果的键
key = json_data['url']
# 添加重试机制
max_retries = 3
for retry in range(max_retries):
try: # try-except块用于捕获API调用中的异常
try: # try-except块用于捕获API调用中的异常
# 发送POST请求到百度安全API
# url: API接口地址
# params: 查询参数
@@ -184,8 +218,24 @@ def baidu(domain: str, proxies: dict = None) -> str:
# json: JSON请求数据
# proxies: 代理配置
# .json(): 解析JSON响应
response = requests.post('https://mobsec-sec.baidu.com/3.1/scanurl', params=params, headers=headers,
json=json_data, proxies=proxies, timeout=15).json()
session = _get_thread_session()
request_started_at = time.perf_counter()
logger.info(
f"百度安全阶段: domain={domain} | stage=request_start | proxy={'yes' if proxies else 'no'}"
)
response = session.post(
BAIDU_SAFE_SCAN_URL,
params=BAIDU_SAFE_SCAN_PARAMS,
headers=BAIDU_SAFE_SCAN_HEADERS,
json=json_data,
proxies=proxies,
timeout=_resolve_baidu_timeout(proxies, budget_seconds=budget_seconds, scan=True),
)
logger.info(
f"百度安全阶段: domain={domain} | stage=request_done | status={response.status_code} "
f"| elapsed_ms={int((time.perf_counter() - request_started_at) * 1000)}"
)
response = response.json()
# # 记录API响应调试用
# logger.info(response)
@@ -202,27 +252,9 @@ def baidu(domain: str, proxies: dict = None) -> str:
# msgs.get(): 根据安全等级获取对应的状态描述,默认值为'正常'
return msgs.get(response['response']['datas'][key].get('grand_level', 'level'), '正常')
except Exception as e: # 捕获所有异常
logger.warning(f"百度网页安全中心检测{retry+1}失败: {domain}, 错误: {e}")
if retry < max_retries - 1:
time.sleep(2) # 等待2秒后重试
else:
# 达到最大重试次数,尝试不使用代理
if proxies:
logger.info('百度网页安全中心检测失败,尝试不使用代理')
try:
response = requests.post('https://mobsec-sec.baidu.com/3.1/scanurl', params=params, headers=headers,
json=json_data, proxies=None, timeout=15).json()
msgs = {
2193: '风险网站提示', # 风险网站
2243: '高危网站提示', # 高危网站
'_': '正常' # 默认正常
}
return msgs.get(response['response']['datas'][key].get('grand_level', 'level'), '正常')
except Exception as e2:
logger.error(f"不使用代理的百度网页安全中心检测也失败: {domain}, 错误: {e2}")
return str(e2) # 返回错误信息
return str(e) # 返回错误信息
except Exception as e: # 捕获所有异常
logger.error(f"百度网页安全中心检测失败: {domain}, 错误: {e}")
return str(e)
# # ========================= 【测试代码】 =========================
@@ -260,4 +292,4 @@ def baidu(domain: str, proxies: dict = None) -> str:
# logger.info(f"搜索结果标题数: {len(result_1)}")
# for i, title in enumerate(result_1, 1):
# logger.info(f" 结果{i}: {title}")
# time.sleep(1)
# time.sleep(1)