mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-04 19:29:59 +08:00
✨ feat(http_utils): 重构网络请求工具链,增强稳定性与易用性 (#1951)
* ✨ feat(http_utils): 重构网络请求工具链,增强稳定性与易用性 🔧 HTTP工具优化: • 全局httpx.AsyncClient管理,提升连接复用效率 • AsyncHttpx类重构,支持临时客户端和配置覆盖 • 新增JSON请求方法(get_json/post_json),内置重试机制 • 兼容httpx>=0.28.0版本 🔄 重试机制升级: • Retry装饰器重构,提供simple/api/download预设 • 支持指数退避、条件重试和自定义失败处理 • 扩展异常覆盖范围,提升网络容错能力 🏗️ 架构改进: • 新增AllURIsFailedError统一异常处理 • 浏览器工具模块化,提升代码组织性 * 🚨 auto fix by pre-commit hooks * 🎨 代码格式化 * 🐛 测试修复 --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com> Co-authored-by: HibiKier <775757368@qq.com>
This commit is contained in:
co-authored by
webjoin111
pre-commit-ci[bot]
HibiKier
HibiKier
parent
e0773174d0
commit
1c5f66beee
+84
-81
@@ -1,91 +1,94 @@
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
|
||||
from nonebot import get_driver
|
||||
from playwright.__main__ import main
|
||||
from playwright.async_api import Browser, Playwright, async_playwright
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
from nonebot_plugin_htmlrender import get_browser
|
||||
from playwright.async_api import Page
|
||||
|
||||
from zhenxun.configs.config import BotConfig
|
||||
from zhenxun.services.log import logger
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
_playwright: Playwright | None = None
|
||||
_browser: Browser | None = None
|
||||
from zhenxun.utils.message import MessageUtils
|
||||
|
||||
|
||||
# @driver.on_startup
|
||||
# async def start_browser():
|
||||
# global _playwright
|
||||
# global _browser
|
||||
# install()
|
||||
# await check_playwright_env()
|
||||
# _playwright = await async_playwright().start()
|
||||
# _browser = await _playwright.chromium.launch()
|
||||
class BrowserIsNone(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# @driver.on_shutdown
|
||||
# async def shutdown_browser():
|
||||
# if _browser:
|
||||
# await _browser.close()
|
||||
# if _playwright:
|
||||
# await _playwright.stop() # type: ignore
|
||||
class AsyncPlaywright:
|
||||
@classmethod
|
||||
@asynccontextmanager
|
||||
async def new_page(
|
||||
cls, cookies: list[dict[str, Any]] | dict[str, Any] | None = None, **kwargs
|
||||
) -> AsyncGenerator[Page, None]:
|
||||
"""获取一个新页面
|
||||
|
||||
|
||||
# def get_browser() -> Browser:
|
||||
# if not _browser:
|
||||
# raise RuntimeError("playwright is not initalized")
|
||||
# return _browser
|
||||
|
||||
|
||||
def install():
|
||||
"""自动安装、更新 Chromium"""
|
||||
|
||||
def set_env_variables():
|
||||
os.environ["PLAYWRIGHT_DOWNLOAD_HOST"] = (
|
||||
"https://npmmirror.com/mirrors/playwright/"
|
||||
)
|
||||
if BotConfig.system_proxy:
|
||||
os.environ["HTTPS_PROXY"] = BotConfig.system_proxy
|
||||
|
||||
def restore_env_variables():
|
||||
os.environ.pop("PLAYWRIGHT_DOWNLOAD_HOST", None)
|
||||
if BotConfig.system_proxy:
|
||||
os.environ.pop("HTTPS_PROXY", None)
|
||||
if original_proxy is not None:
|
||||
os.environ["HTTPS_PROXY"] = original_proxy
|
||||
|
||||
def try_install_chromium():
|
||||
参数:
|
||||
cookies: cookies
|
||||
"""
|
||||
browser = await get_browser()
|
||||
ctx = await browser.new_context(**kwargs)
|
||||
if cookies:
|
||||
if isinstance(cookies, dict):
|
||||
cookies = [cookies]
|
||||
await ctx.add_cookies(cookies) # type: ignore
|
||||
page = await ctx.new_page()
|
||||
try:
|
||||
sys.argv = ["", "install", "chromium"]
|
||||
main()
|
||||
except SystemExit as e:
|
||||
return e.code == 0
|
||||
return False
|
||||
yield page
|
||||
finally:
|
||||
await page.close()
|
||||
await ctx.close()
|
||||
|
||||
logger.info("检查 Chromium 更新")
|
||||
@classmethod
|
||||
async def screenshot(
|
||||
cls,
|
||||
url: str,
|
||||
path: Path | str,
|
||||
element: str | list[str],
|
||||
*,
|
||||
wait_time: int | None = None,
|
||||
viewport_size: dict[str, int] | None = None,
|
||||
wait_until: (
|
||||
Literal["domcontentloaded", "load", "networkidle"] | None
|
||||
) = "networkidle",
|
||||
timeout: float | None = None,
|
||||
type_: Literal["jpeg", "png"] | None = None,
|
||||
user_agent: str | None = None,
|
||||
cookies: list[dict[str, Any]] | dict[str, Any] | None = None,
|
||||
**kwargs,
|
||||
) -> UniMessage | None:
|
||||
"""截图,该方法仅用于简单快捷截图,复杂截图请操作 page
|
||||
|
||||
original_proxy = os.environ.get("HTTPS_PROXY")
|
||||
set_env_variables()
|
||||
|
||||
success = try_install_chromium()
|
||||
|
||||
if not success:
|
||||
logger.info("Chromium 更新失败,尝试从原始仓库下载,速度较慢")
|
||||
os.environ["PLAYWRIGHT_DOWNLOAD_HOST"] = ""
|
||||
success = try_install_chromium()
|
||||
|
||||
restore_env_variables()
|
||||
|
||||
if not success:
|
||||
raise RuntimeError("未知错误,Chromium 下载失败")
|
||||
|
||||
|
||||
async def check_playwright_env():
|
||||
"""检查 Playwright 依赖"""
|
||||
logger.info("检查 Playwright 依赖")
|
||||
try:
|
||||
async with async_playwright() as p:
|
||||
await p.chromium.launch()
|
||||
except Exception as e:
|
||||
raise ImportError("加载失败,Playwright 依赖不全,") from e
|
||||
参数:
|
||||
url: 网址
|
||||
path: 存储路径
|
||||
element: 元素选择
|
||||
wait_time: 等待截取超时时间
|
||||
viewport_size: 窗口大小
|
||||
wait_until: 等待类型
|
||||
timeout: 超时限制
|
||||
type_: 保存类型
|
||||
user_agent: user_agent
|
||||
cookies: cookies
|
||||
"""
|
||||
if viewport_size is None:
|
||||
viewport_size = {"width": 2560, "height": 1080}
|
||||
if isinstance(path, str):
|
||||
path = Path(path)
|
||||
wait_time = wait_time * 1000 if wait_time else None
|
||||
element_list = [element] if isinstance(element, str) else element
|
||||
async with cls.new_page(
|
||||
cookies,
|
||||
viewport=viewport_size,
|
||||
user_agent=user_agent,
|
||||
**kwargs,
|
||||
) as page:
|
||||
await page.goto(url, timeout=timeout, wait_until=wait_until)
|
||||
card = page
|
||||
for e in element_list:
|
||||
if not card:
|
||||
return None
|
||||
card = await card.wait_for_selector(e, timeout=wait_time)
|
||||
if card:
|
||||
await card.screenshot(path=path, timeout=timeout, type=type_)
|
||||
return MessageUtils.build_message(path)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user