Files
getDomain/domainCheck/detect/baidu.py
Your Name 7cbde2aa78 d
2026-04-22 14:13:21 +08:00

296 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: UTF-8 -*-
'''
@Project :domainScanDemo
@File :baidu.py
@IDE :PyCharm
@Author :梦伴
@Date :2026/3/29 21:54
@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 # 类型提示
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]]:
'''
通过百度搜索查询域名的相关信息
使用curl_cffi模拟浏览器访问百度搜索获取site:domain的搜索结果标题
:param domain: 待查询的域名www.baidu.com
:param proxies: 代理配置(如:{'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'}
:return: list - 搜索结果标题列表,如果查询失败则返回空列表
'''
# 初始化敏感词列表
if sensitive_words is None:
sensitive_words = []
try: # try-except块用于捕获网络请求中的异常
# 构建搜索查询参数
# wd: 搜索关键词格式为site:域名
params = {
'wd': f'site:{domain}' # 搜索site:domain限制搜索结果为指定域名
}
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(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:
return True, ''
# 检查每个搜索结果是否包含敏感词
for result in search_results:
for sensitive_word in sensitive_words:
# 使用正则表达式检查是否包含敏感词
if re.search(sensitive_word, result, re.IGNORECASE):
logger.warning(f'检测到敏感词: {sensitive_word} 在结果: {result}')
return False, f'检测到敏感词: {sensitive_word} 在结果: {result}'
# 所有搜索结果都不包含敏感词,返回通过
return True, ''
except Exception as e: # 捕获所有异常
logger.error(f"百度site检测失败: {domain}, 错误: {e}")
return False, str(e)
def baidu(domain: str, proxies: dict = None, budget_seconds: Optional[float] = None) -> str:
'''
通过百度安全API检测域名安全状态
调用百度移动安全API检测域名是否为风险网站
:param domain: 待检测的域名www.baidu.com
:param proxies: 代理配置(如:{'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'}
:return: str - 安全状态描述("风险网站提示""高危网站提示""正常"),如果检测失败则返回错误信息
'''
# 构建JSON请求数据
json_data = {
'url': f'https://{domain}/', # 待检测的URL添加https协议头
}
# 提取URL作为后续获取结果的键
key = json_data['url']
try: # try-except块用于捕获API调用中的异常
# 发送POST请求到百度安全API
# url: API接口地址
# params: 查询参数
# headers: HTTP请求头
# json: JSON请求数据
# proxies: 代理配置
# .json(): 解析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)
# 安全状态码映射表
msgs = {
2193: '风险网站提示', # 风险网站
2243: '高危网站提示', # 高危网站
'_': '正常' # 默认正常
}
# 从响应中获取安全等级并映射为状态描述
# response['response']['datas'][key].get('grand_level', 'level'): 获取安全等级,默认值为'level'
# msgs.get(): 根据安全等级获取对应的状态描述,默认值为'正常'
return msgs.get(response['response']['datas'][key].get('grand_level', 'level'), '正常')
except Exception as e: # 捕获所有异常
logger.error(f"百度网页安全中心检测失败: {domain}, 错误: {e}")
return str(e)
# # ========================= 【测试代码】 =========================
# if __name__ == '__main__': # 程序入口,当直接运行此文件时执行以下代码
# # proxies = get_proxies()
# proxies = None
# # 测试用例1查询域名搜索结果
# logger.info("=== 测试百度搜索结果查询 ===")
# test_domain_1 = '920zl.com'
# logger.info(f"查询域名: {test_domain_1}")
#
# result_1 = check_site(test_domain_1, proxies=proxies)
# logger.info(result_1)
#
# # 测试用例3检测域名安全状态
# logger.info("=== 测试域名安全检测 ===")
# test_domain_3 = 'baidu.com'
# logger.info(f"检测域名: {test_domain_3}")
#
# result_3 = baidu(test_domain_3, proxies=proxies)
# logger.info(f"安全状态: {result_3}")
# logger.debug(proxies)
# domains = ['Lqyingye.com', 'tjjinLikeji.com', 'guokangLxs.com', 'zhuoyijz.com', 'jiandanrongyi.com', 'fromhhc.com',
# 'djxow.com', '725game.com', 'gcLpchd.cn', '6rdb.com.cn', 'sunmantech.com', 'hroxa.com', 'guLhu65.cn',
# 'xxjdch.com', '7e7fm1.cn', 'kaoxueb.com', 'jabas.cn', 'shzhongyi.com', 'figuangze.com', 'eztw9k.cn',
# 'foxok.com', 'oLLbmr.com', 'vwpwwkp.cn', 'cbmrs.com', 'zaozhuang56.com', 'hengxiangracing.com',
# 'jdyfbpq.cn', 'czca.cn', 'shibingxiongdi.com', 'ubxbm.cn', 'ydbaoshi.com', '2046hd.cn', 'mffmcp.com',
# 'hgLdhzz.com', 'okswmi.cn', 'caidaozg.com', 'dkdywx.cn', 'qkjps.com', 'Lwxgk.cn', 'drtechnoLogy.cn',
# 'akcie.cn', '588uu.com', 'kuaiyijian.net', 'c5b.cn', 'faka866.cn', 'wiraf03.cn', 'bobjibar.com',
# '166233.cn', 'adLfjcjq.cn', 'whrsom.com']
# for domain in domains:
# result_1 = check_site(domain)
# logger.info(f"搜索结果标题数: {len(result_1)}")
# for i, title in enumerate(result_1, 1):
# logger.info(f" 结果{i}: {title}")
# time.sleep(1)