AI智能体网站登录自动化:从原理到ChatGPT Work实战
1. 智能体与网站登录技术概述在当今的AI应用开发中智能体AI Agent已经成为连接人工智能能力与实际业务场景的重要桥梁。特别是ChatGPT Work这类智能体平台通过模拟人类操作行为实现了对网站登录流程的自动化处理。这项技术不仅提高了工作效率还为数据采集、业务流程自动化等领域带来了革命性的变化。智能体本质上是一个能够感知环境、进行决策并执行动作的软件实体。在网站登录场景中智能体需要完成识别登录页面元素、填写凭证信息、处理验证机制等一系列复杂操作。与传统的爬虫技术相比智能体具备更强的交互能力和适应性能够处理JavaScript动态加载内容、验证码识别等挑战。从技术架构角度看一个完整的网站登录智能体通常包含以下几个核心模块页面导航与元素定位模块凭证管理与安全存储模块会话维持与Cookie处理模块异常检测与重试机制模块安全验证绕过处理模块2. 环境准备与工具选型2.1 基础开发环境配置在开始构建网站登录智能体之前需要确保开发环境准备就绪。以下是推荐的基础配置方案操作系统要求Windows 10/11 或 macOS 10.15Linux发行版Ubuntu 20.04 LTS或CentOS 8Python环境配置# 创建虚拟环境 python -m venv chatgpt_work_agent source chatgpt_work_agent/bin/activate # Linux/macOS chatgpt_work_agent\Scripts\activate # Windows # 安装核心依赖 pip install selenium4.15.0 pip install webdriver-manager4.0.1 pip install requests2.31.0 pip install beautifulsoup44.12.2 pip install pillow10.1.0 # 图像处理用于验证码浏览器驱动管理现代智能体开发推荐使用WebDriver Manager自动管理浏览器驱动避免手动配置的繁琐from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.core.os_manager import ChromeType def setup_driver(): service Service(ChromeDriverManager().install()) options webdriver.ChromeOptions() options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) return webdriver.Chrome(serviceservice, optionsoptions)2.2 辅助工具与库选择根据不同的登录场景需求可能需要额外的工具支持验证码处理工具Tesseract OCR用于简单验证码识别OpenCV图像预处理增强识别准确率第三方验证码识别API谨慎选择可靠服务商代理IP管理对于需要处理访问频率限制的网站合理的代理IP轮换策略至关重要import requests from itertools import cycle class ProxyManager: def __init__(self, proxy_list): self.proxy_pool cycle(proxy_list) def get_proxy(self): return next(self.proxy_pool) def test_proxy(self, proxy): try: response requests.get(http://httpbin.org/ip, proxies{http: proxy, https: proxy}, timeout10) return response.status_code 200 except: return False3. 网站登录原理与技术实现3.1 登录流程分析与元素定位网站登录本质上是一个表单提交过程但现代网站采用了多种技术增强安全性。智能体需要准确识别登录表单的关键元素常见的登录表单结构!-- 典型登录表单示例 -- form idloginForm action/login methodpost input typetext nameusername idusername placeholder用户名/邮箱 input typepassword namepassword idpassword placeholder密码 input typecheckbox nameremember idremember button typesubmit idloginButton登录/button /form智能体元素定位策略from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginAgent: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def locate_login_elements(self): # 多种定位策略确保稳定性 username_selectors [ input[nameusername], input[typetext], #username, .username-input ] for selector in username_selectors: try: username_field self.wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, selector)) ) return username_field except: continue raise Exception(未找到用户名输入框)3.2 会话管理与Cookie处理成功的登录操作会产生会话标识智能体需要妥善管理这些凭证import json import pickle import os from datetime import datetime class SessionManager: def __init__(self, session_filesessions.json): self.session_file session_file self.sessions self.load_sessions() def load_sessions(self): if os.path.exists(self.session_file): with open(self.session_file, r) as f: return json.load(f) return {} def save_session(self, website, cookies, username): session_data { cookies: cookies, username: username, last_login: datetime.now().isoformat(), user_agent: self.driver.execute_script(return navigator.userAgent) } self.sessions[website] session_data with open(self.session_file, w) as f: json.dump(self.sessions, f, indent2) def load_session(self, website, driver): if website in self.sessions: session_data self.sessions[website] driver.get(website) # 先访问网站域名 for cookie in session_data[cookies]: driver.add_cookie(cookie) driver.refresh() # 刷新页面应用Cookie return True return False4. 完整实战构建ChatGPT Work登录智能体4.1 项目结构与配置设计首先创建清晰的项目目录结构chatgpt-work-agent/ ├── src/ │ ├── agents/ │ │ ├── __init__.py │ │ ├── base_agent.py │ │ └── chatgpt_agent.py │ ├── utils/ │ │ ├── config_loader.py │ │ ├── session_manager.py │ │ └── captcha_solver.py │ └── main.py ├── config/ │ ├── credentials.json │ └── settings.yaml ├── logs/ └── requirements.txt配置文件示例config/settings.yamlbrowser: headless: false timeout: 30 window_size: 1920,1080 chatgpt_work: login_url: https://chat.openai.com/auth/login dashboard_url: https://chat.openai.com/ element_selectors: username: input[typeemail] password: input[typepassword] login_button: button[typesubmit] continue_button: button:contains(Continue) security: max_retries: 3 retry_delay: 5 use_proxy: false4.2 核心智能体类实现创建基础智能体类封装通用登录逻辑import time import logging from abc import ABC, abstractmethod from selenium.common.exceptions import TimeoutException, NoSuchElementException class BaseLoginAgent(ABC): def __init__(self, config): self.config config self.driver None self.logger self.setup_logging() self.setup_driver() def setup_logging(self): logger logging.getLogger(self.__class__.__name__) logger.setLevel(logging.INFO) handler logging.FileHandler(logs/agent.log) formatter logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s) handler.setFormatter(formatter) logger.addHandler(handler) return logger abstractmethod def login(self, username, password): pass def safe_find_element(self, by, value, timeout10): 安全查找元素带重试机制 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC for attempt in range(self.config[security][max_retries]): try: element WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located((by, value)) ) return element except TimeoutException: self.logger.warning(f元素定位失败第{attempt1}次重试: {value}) if attempt self.config[security][max_retries] - 1: raise time.sleep(self.config[security][retry_delay])4.3 ChatGPT Work专属登录实现针对ChatGPT Work平台的特性实现具体登录逻辑class ChatGPTWorkAgent(BaseLoginAgent): def __init__(self, config): super().__init__(config) self.login_url config[chatgpt_work][login_url] self.selectors config[chatgpt_work][element_selectors] def login(self, username, password): self.logger.info(f开始登录ChatGPT Work用户名: {username}) try: # 访问登录页面 self.driver.get(self.login_url) time.sleep(2) # 处理可能的弹窗或重定向 self.handle_pre_login_actions() # 输入用户名 username_field self.safe_find_element( By.CSS_SELECTOR, self.selectors[username] ) username_field.clear() username_field.send_keys(username) # 点击继续按钮 continue_btn self.safe_find_element( By.CSS_SELECTOR, self.selectors[continue_button] ) continue_btn.click() time.sleep(2) # 输入密码 password_field self.safe_find_element( By.CSS_SELECTOR, self.selectors[password] ) password_field.clear() password_field.send_keys(password) # 执行登录 login_btn self.safe_find_element( By.CSS_SELECTOR, self.selectors[login_button] ) login_btn.click() # 验证登录结果 return self.verify_login_success() except Exception as e: self.logger.error(f登录过程出错: {str(e)}) self.save_screenshot(login_error) raise def handle_pre_login_actions(self): 处理登录前的额外操作 try: # 检查是否有隐私弹窗需要处理 privacy_selectors [ button:contains(Accept), button:contains(同意), .cookie-accept, #accept-cookies ] for selector in privacy_selectors: try: accept_btn self.driver.find_element(By.CSS_SELECTOR, selector) if accept_btn.is_displayed(): accept_btn.click() time.sleep(1) break except NoSuchElementException: continue except Exception as e: self.logger.warning(f处理预登录操作时出错: {e}) def verify_login_success(self, timeout30): 验证登录是否成功 try: # 等待页面跳转到仪表盘 WebDriverWait(self.driver, timeout).until( lambda driver: self.config[chatgpt_work][dashboard_url] in driver.current_url ) # 检查登录后特有的元素 success_indicators [ nav[aria-labelChat history], textarea[idprompt-textarea], button[aria-labelSend message] ] for indicator in success_indicators: try: element self.driver.find_element(By.CSS_SELECTOR, indicator) if element.is_displayed(): self.logger.info(登录验证成功) return True except NoSuchElementException: continue self.logger.warning(未找到登录成功指示器但URL跳转成功) return True except TimeoutException: self.logger.error(登录验证超时) return False4.4 凭证安全管理实现安全存储和管理登录凭证是关键环节import keyring import json from cryptography.fernet import Fernet import base64 class CredentialManager: def __init__(self, service_namechatgpt_work_agent): self.service_name service_name self.key self._get_or_create_key() self.cipher Fernet(self.key) def _get_or_create_key(self): 获取或创建加密密钥 try: key_base64 keyring.get_password(system, f{self.service_name}_key) if key_base64: return base64.urlsafe_b64decode(key_base64) except: pass # 创建新密钥 new_key Fernet.generate_key() keyring.set_password(system, f{self.service_name}_key, base64.urlsafe_b64encode(new_key).decode()) return new_key def save_credentials(self, username, password, identifierdefault): 加密保存凭证 credentials { username: username, password: password, timestamp: time.time() } encrypted_data self.cipher.encrypt( json.dumps(credentials).encode() ) keyring.set_password(self.service_name, identifier, base64.urlsafe_b64encode(encrypted_data).decode()) def load_credentials(self, identifierdefault): 解密加载凭证 try: encrypted_b64 keyring.get_password(self.service_name, identifier) if not encrypted_b64: return None encrypted_data base64.urlsafe_b64decode(encrypted_b64) decrypted_data self.cipher.decrypt(encrypted_data) return json.loads(decrypted_data.decode()) except Exception as e: print(f加载凭证失败: {e}) return None4.5 完整使用示例整合所有组件提供完整的登录流程示例def main(): # 加载配置 import yaml with open(config/settings.yaml, r) as f: config yaml.safe_load(f) # 初始化智能体 agent ChatGPTWorkAgent(config) # 加载凭证 cred_manager CredentialManager() credentials cred_manager.load_credentials() if not credentials: # 首次使用需要输入凭证 username input(请输入ChatGPT Work用户名: ) password input(请输入密码: ) cred_manager.save_credentials(username, password) else: username credentials[username] password credentials[password] try: # 执行登录 success agent.login(username, password) if success: print(登录成功) # 保存会话供后续使用 session_manager SessionManager() cookies agent.driver.get_cookies() session_manager.save_session( config[chatgpt_work][dashboard_url], cookies, username ) # 可以继续执行其他自动化操作 # agent.perform_automated_tasks() else: print(登录失败请检查凭证或网络连接) except Exception as e: print(f执行过程中出错: {e}) finally: if agent.driver: agent.driver.quit() if __name__ __main__: main()5. 常见问题与解决方案5.1 登录流程中的典型问题在实际使用中开发者经常会遇到各种登录相关问题。以下是经过整理的常见问题排查表问题现象可能原因解决方案元素定位失败页面结构变化、动态加载延迟增加等待时间、使用多种选择器策略、添加重试机制验证码拦截触发反爬虫机制添加人工干预点、使用验证码识别服务、优化操作间隔账号被限制频繁登录尝试增加登录间隔、使用代理IP轮换、模拟人类操作行为会话过期Cookie失效实现会话持久化、定期验证会话有效性、自动重新登录5.2 反爬虫机制应对策略现代网站普遍采用各种反爬虫技术智能体需要相应的应对措施检测绕过技术def stealth_operations(driver): 隐藏自动化特征 # 移除webdriver属性 driver.execute_script(Object.defineProperty(navigator, webdriver, {get: () undefined})) # 设置合理的用户代理 driver.execute_cdp_cmd(Network.setUserAgentOverride, { userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 }) # 模拟人类输入模式 def human_type(element, text): for char in text: element.send_keys(char) time.sleep(random.uniform(0.05, 0.2))操作频率控制import random class HumanizedOperator: def __init__(self): self.last_operation_time 0 def random_delay(self, min_delay1, max_delay3): 随机延迟模拟人类操作间隔 delay random.uniform(min_delay, max_delay) elapsed time.time() - self.last_operation_time if elapsed delay: time.sleep(delay - elapsed) self.last_operation_time time.time() def human_click(self, element): 模拟人类点击行为 self.random_delay(0.5, 1.5) element.click()6. 安全与最佳实践6.1 安全注意事项在开发和使用登录智能体时安全应该是首要考虑因素凭证安全存储永远不要在代码中硬编码用户名和密码使用操作系统提供的安全存储机制考虑使用硬件安全模块HSM保护密钥定期轮换加密密钥访问权限控制def permission_check(): 权限验证机制 required_permissions [ network_access, file_system_read, file_system_write ] # 实现细粒度的权限控制 for permission in required_permissions: if not check_permission(permission): raise PermissionError(f缺少必要权限: {permission})6.2 性能优化建议并发登录管理对于需要处理多个账号的场景合理的并发控制很重要import concurrent.futures from threading import Lock class ConcurrentLoginManager: def __init__(self, max_workers3): self.max_workers max_workers self.lock Lock() self.active_sessions {} def batch_login(self, accounts): 批量登录处理 with concurrent.futures.ThreadPoolExecutor( max_workersself.max_workers ) as executor: future_to_account { executor.submit(self.single_login, account): account for account in accounts } results {} for future in concurrent.futures.as_completed(future_to_account): account future_to_account[future] try: results[account] future.result() except Exception as e: results[account] f登录失败: {e} return results资源清理与内存管理class ResourceManager: def __init__(self): self.resources [] def register_resource(self, resource): self.resources.append(resource) def cleanup(self): 清理所有注册的资源 for resource in reversed(self.resources): try: if hasattr(resource, close): resource.close() elif hasattr(resource, quit): resource.quit() except Exception as e: print(f清理资源时出错: {e})6.3 监控与日志记录完善的监控体系有助于及时发现和解决问题import logging from logging.handlers import RotatingFileHandler def setup_comprehensive_logging(): 设置全面的日志记录系统 logger logging.getLogger(chatgpt_work_agent) logger.setLevel(logging.INFO) # 文件处理器自动轮转 file_handler RotatingFileHandler( logs/agent.log, maxBytes10*1024*1024, # 10MB backupCount5 ) file_handler.setFormatter(logging.Formatter( %(asctime)s - %(name)s - %(levelname)s - %(message)s )) # 控制台处理器 console_handler logging.StreamHandler() console_handler.setFormatter(logging.Formatter( %(levelname)s - %(message)s )) logger.addHandler(file_handler) logger.addHandler(console_handler) return logger通过本文的完整实现开发者可以构建一个稳定可靠的ChatGPT Work登录智能体。重点在于理解网站登录机制、处理各种边界情况、确保代码的健壮性和安全性。在实际项目中还需要根据具体的业务需求进行相应的调整和优化。

相关新闻

LLM架构对比:Encoder-Decoder与Decoder-only技术解析

LLM架构对比:Encoder-Decoder与Decoder-only技术解析

1. 从架构选择看LLM发展脉络2017年Transformer论文的发表彻底改变了自然语言处理的游戏规则,而架构选择始终是模型设计中的首要决策。在主流LLM发展历程中,Encoder-Decoder和Decoder-only两种架构形成了明显的技术分水岭。早期的机器翻译系统普遍采用Enc…

2026/10/15 15:09:42 阅读更多 →
终极指南:解决ComfyUI IPAdapter Plus的FaceID依赖错误,实现完美人脸控制

终极指南:解决ComfyUI IPAdapter Plus的FaceID依赖错误,实现完美人脸控制

终极指南:解决ComfyUI IPAdapter Plus的FaceID依赖错误,实现完美人脸控制 【免费下载链接】ComfyUI_IPAdapter_plus 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI_IPAdapter_plus ComfyUI IPAdapter Plus是一个强大的AI图像生成插件&am…

2026/10/12 18:33:15 阅读更多 →
小白python入门 - 41. 路由、路径与查询参数

小白python入门 - 41. 路由、路径与查询参数

1. 本课定位:是什么、为何重要上一课服务已经能跑:/health 会回 ok,/docs 也能点。但真实 API 不会只有探活——书签业务要按资源区分列表、详情、删除,还要支持搜索和分页雏形。如果所有逻辑都堆在一个 main.py 里用硬编码路径&a…

2026/10/14 10:34:42 阅读更多 →

最新新闻

kkFileView 4.4.0-beta 部署与集成实战:文件在线预览服务配置、调用与避坑指南

kkFileView 4.4.0-beta 部署与集成实战:文件在线预览服务配置、调用与避坑指南

简介:kkFileView-4.4.0-beta.zip 是一份面向 Java 后端开发者与运维人员的文件在线预览解决方案源码包,版本处于 4.4.0 测试阶段,适合需要为业务系统集成文档、图片、音视频等多格式在线预览能力的团队参考与二次开发。压缩包共收录 2000 个文…

2026/10/15 18:38:53 阅读更多 →
从零搭建LAMP架构:本地部署RAG+Agent+LLM的完整实操记录

从零搭建LAMP架构:本地部署RAG+Agent+LLM的完整实操记录

这个标题我看到很久了,一直想写一篇完整的实操记录。先说个有意思的事,我第一次看到"lamp环境部署 RAG Agent LLM"这个标题时,第一反应是"Linux Apache MySQL PHP"那套经典组合。但在这个语境里,LAMP完全…

2026/10/15 18:38:53 阅读更多 →
OpenFrame OSS Lib 开发环境搭建指南:JDK 21、Maven、Docker、Node.js 一步到位

OpenFrame OSS Lib 开发环境搭建指南:JDK 21、Maven、Docker、Node.js 一步到位

【免费下载链接】openframe-oss-lib Shared libraries between all services 项目地址: https://gitcode.com/gh_mirrors/op/openframe-oss-lib 点击查看 免费下载 想动手开发 OpenFrame OSS Lib,却被 JDK 21、Maven 多模块、Docker 集成测试和 Node.js…

2026/10/15 18:38:53 阅读更多 →
Django+Vue3构建可审计校园请假系统:状态机、Webhook与PDF兼容方案

Django+Vue3构建可审计校园请假系统:状态机、Webhook与PDF兼容方案

简介:校园请假管理信息系统是一套面向高校信息化建设场景的C#桌面端管理信息系统,适用于教育技术专业学生、信息系统开发初学者及学校教务管理人员,旨在解决传统纸质请假流程效率低、审批滞后、数据难统计等痛点。资源包共136个文件&#xff…

2026/10/15 18:38:53 阅读更多 →
Alpha形状:点云几何边界的可调式数学提取方法

Alpha形状:点云几何边界的可调式数学提取方法

简介:这是一份基于JavaScript实现的通用Alpha形状(Alpha Shape)计算工具包,面向计算机图形学、计算几何及数据可视化方向的开发者与学习者,用于从离散点集自动构建具有拓扑意义的几何轮廓。资源提供轻量级npm模块&…

2026/10/15 18:38:53 阅读更多 →
Socket到底是IPC吗?本地进程通信的机制与选型指南

Socket到底是IPC吗?本地进程通信的机制与选型指南

1. 从一个困惑说起:Socket怎么就成了IPC先交代下背景。最近在折腾一个模拟业务系统,里面有两个本地进程要互相传数据,一个进程负责接收外部网关的报文并解析,另一个进程负责把解析后的数据写入本地存储。当时的想法很简单&#xf…

2026/10/15 18:37:51 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →