源码如下:
selenium_tools.py
import json
from pathlib import Path
from typing import Optional, Dict, Any, Tuple
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.common.exceptions import (
NoSuchElementException, ElementClickInterceptedException,
WebDriverException, TimeoutException
)
from selenium.webdriver.chrome.service import Service
class WebAutoFramework:
def __init__(self, headless: bool = False, max_token_length: int = 8000):
self.driver: Optional[WebDriver] = None
self.max_token_length = max_token_length
self.headless = headless
self._initialize_driver()
self.element = None
# 扩展需要收集的标签类型
self.target_tags = [
'button', 'input', 'select', 'textarea', # 表单元素
'a', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', # 文本与链接
'div', 'section', 'article', 'header', 'footer', # 布局元素
'table', 'tr', 'td', 'th', 'ul', 'ol', 'li', # 列表与表格
'img', 'video', 'audio' # 媒体元素
]
def _initialize_driver(self) -> None:
try:
options = webdriver.ChromeOptions()
if self.headless:
options.add_argument('--headless=new')
# 增强浏览器稳定性参数
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
options.add_argument('--ignore-certificate-errors')
options.add_argument('--disable-dev-shm-usage')
# 驱动路径(确保正确)
driver_path = r"D:\chrome_download\chrome_driver\chromedriver-win64\chromedriver.exe"
# 验证驱动存在性
if not Path(driver_path).exists():
raise FileNotFoundError(f"ChromeDriver不存在: {driver_path}")
service = Service(executable_path=driver_path)
self.driver = webdriver.Chrome(
service=service,
options=options
)
self.driver.set_page_load_timeout(30)
self.driver.implicitly_wait(10)
except WebDriverException as e:
raise RuntimeError(f"初始化浏览器失败: {str(e)}")
# def __init__(self):
# self.driver = None
# self.element = None
#
# def init(self):
# if not self.driver:
# self.driver = webdriver.Chrome()
# self.driver.implicitly_wait(5)
def open(self, url):
self.driver.get(url)
return self.source()
def source(self):
return self.driver.execute_script(
"""
var content="";
document.querySelectorAll('button').forEach(x=> content+=x.outerHTML);
document.querySelectorAll('input').forEach(x=> content+=x.outerHTML);
//document.querySelectorAll('li').forEach(x=> content+=x.outerHTML);
//document.querySelectorAll('table').forEach(x=> content+=x.outerHTML);
return content;
"""
)
def click(self):
"""
点击当前的元素
:return:
"""
self.element.click()
return self.source()
def send_keys(self, text):
self.element.clear()
self.element.send_keys(text)
return self.source()
def find(self, locator):
print(f"find css = {locator}")
element = self.driver.find_element(by=By.CSS_SELECTOR, value=locator)
self.element = element
return self.source()
def maximizeWindow(self):
"""
浏览器最大化
:return:
"""
self.driver.maximize_window()
return self.source()
def quit(self):
# self.driver.quit()
pass
def get_current_url(self):
print(f"当前的url为{self.driver.current_url}")
return self.driver.current_url
selenium_agent.py如下
import time
from hogwarts_logger import info
from langchain.globals import set_verbose
from langchain_core.globals import set_debug
from langgraph.prebuilt.chat_agent_executor import AgentState, create_react_agent
from testcase_gen.llm import model_chatpt, model_ollama
set_debug(True)
set_verbose(True)
from langchain_core.tools import tool
from langraph_agent.selenium_tools_ad import WebAutoFramework
web = WebAutoFramework()
@tool
def open(url: str):
"""
使用浏览器打开特定的url,并返回网页内容
"""
r = web.open(url)
return r
@tool
def find(css: str):
"""定位网页元素"""
return web.find(css)
@tool
def click(css: str = None):
"""以css的方式定位网页元素后点击"""
web.find(css)
return web.click()
@tool
def send_keys(css, text):
"""定位到css指定的元素,并输入text"""
web.find(css)
return web.send_keys(text)
@tool
def sleep(seconds: int):
"""等待指定的秒数"""
time.sleep(seconds)
@tool
def quit():
"""退出浏览器"""
web.quit()
@tool
def get_current_url():
"""获取当前的url"""
return web.get_current_url()
@tool
def maximizeWindow():
"""
浏览器最大化
:return:
"""
return web.maximizeWindow()
tools = [open, quit, get_current_url, find, click, send_keys,maximizeWindow]
web_agent = create_react_agent(model=model_ollama,
tools=tools,
prompt="你是一个测试工程师。"
"每次只返回包含一个action的工具调用。"
"每次只返回一个tool_call action。"
"每次只返回一个function_call action。")
def test_page_source_v3():
# 构建符合AgentState要求的输入
state = {
"messages": [{
"role": "user",
"content": "1. 浏览器最大化后打开 https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard "
"2. 输入用户名 hogwarts"
" 3. 输入密码 test12345"
" 4. 点击登录按钮"
" 5. 进入首页后"
"6.点击商品管理,点击商品列表,点击添加按钮"
}],
"tools": tools,
"tool_names": [tool.name for tool in tools]
}
r = web_agent.invoke(state,config={"recursion_limit": 50})
info(r)
问题
无法定位到登录按钮,有时候可以定位登录按钮,有时候又定位不到;
报错信息
{
"model":"qwen2.5:latest","stream":true,"options": {
}
,"messages":[ {
"role":"system","content":"你是一个测试工程师。每次只返回包含一个action的工具调用。每次只返回一个tool_call action。每次只返回一个function_call action。"
}
, {
"role":"user","content":"1. 浏览器最大化后打开 https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard 2. 输入用户名 hogwarts 3. 输入密码 test12345 4. 点击登录按钮 5. 进入首页后6.点击商品管理,点击商品列表,点击添加按钮"
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"maximizeWindow","arguments": {
}
}
}
, {
"function": {
"name":"open","arguments": {
"url":"https://litemall.hogwarts.ceshiren.com/#/login?redirect=%2Fdashboard"
}
}
}
, {
"function": {
"name":"send_keys","arguments": {
"css":".username-input","text":"hogwarts"
}
}
}
, {
"function": {
"name":"send_keys","arguments": {
"css":".password-input","text":"test12345"
}
}
}
, {
"function": {
"name":"click","arguments": {
"css":".login-button"
}
}
}
]
}
, {
"role":"tool","content":"<button data-v-31756f19=\"\" type=\"button\" class=\"el-button el-button--primary el-button--mini\" style=\"width: 100%; margin-bottom: 30px;\"><!----><!----><span>登录</span></button><input tabindex=\"1\" type=\"text\" autocomplete=\"on\" name=\"username\" placeholder=\"管理员账户\" class=\"el-input__inner\"><input tabindex=\"2\" type=\"password\" autocomplete=\"on\" name=\"password\" placeholder=\"管理员密码\" class=\"el-input__inner\">"
}
, {
"role":"tool","content":"<button data-v-31756f19=\"\" type=\"button\" class=\"el-button el-button--primary el-button--mini\" style=\"width: 100%; margin-bottom: 30px;\"><!----><!----><span>登录</span></button><input tabindex=\"1\" type=\"text\" autocomplete=\"on\" name=\"username\" placeholder=\"管理员账户\" class=\"el-input__inner\"><input tabindex=\"2\" type=\"password\" autocomplete=\"on\" name=\"password\" placeholder=\"管理员密码\" class=\"el-input__inner\">"
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".login-button"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":".login-button"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"<button data-v-31756f19=\"\" type=\"button\" class=\"el-button el-button--primary el-button--mini is-loading\" style=\"width: 100%; margin-bottom: 30px;\" disabled=\"disabled\"><i class=\"el-icon-loading\"></i><!----><span>登录</span></button><input tabindex=\"1\" type=\"text\" autocomplete=\"on\" name=\"username\" placeholder=\"管理员账户\" class=\"el-input__inner\"><input tabindex=\"2\" type=\"password\" autocomplete=\"on\" name=\"password\" placeholder=\"管理员密码\" class=\"el-input__inner\">"
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini.is-loading"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[disabled='disabled'] + input[type='text'][name='username']"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"send_keys","arguments": {
"css":".el-input__inner[name='username']","text":"hogwarts"
}
}
}
]
}
, {
"role":"tool","content":"<button data-v-31756f19=\"\" type=\"button\" class=\"el-button el-button--primary el-button--mini is-loading\" style=\"width: 100%; margin-bottom: 30px;\" disabled=\"disabled\"><i class=\"el-icon-loading\"></i><!----><span>登录</span></button><input tabindex=\"1\" type=\"text\" autocomplete=\"on\" name=\"username\" placeholder=\"管理员账户\" class=\"el-input__inner\"><input tabindex=\"2\" type=\"password\" autocomplete=\"on\" name=\"password\" placeholder=\"管理员密码\" class=\"el-input__inner\">"
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"send_keys","arguments": {
"css":".el-input__inner[name='password']","text":"test12345"
}
}
}
]
}
, {
"role":"tool","content":"<button data-v-31756f19=\"\" type=\"button\" class=\"el-button el-button--primary el-button--mini is-loading\" style=\"width: 100%; margin-bottom: 30px;\" disabled=\"disabled\"><i class=\"el-icon-loading\"></i><!----><span>登录</span></button><input tabindex=\"1\" type=\"text\" autocomplete=\"on\" name=\"username\" placeholder=\"管理员账户\" class=\"el-input__inner\"><input tabindex=\"2\" type=\"password\" autocomplete=\"on\" name=\"password\" placeholder=\"管理员密码\" class=\"el-input__inner\">"
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini.is-loading"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[disabled='disabled']"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":".el-button--primary.el-button--mini.is-loading + .el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[disabled='disabled'] + .el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[disabled='disabled'] + .el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[disabled='disabled'] + .el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":"#home-page"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":"#home-page"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini"
}
}
}
]
}
, {
"role":"tool","content":"Error: ElementClickInterceptedException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":"#home-page"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-menu-item[title='商品管理']"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"find","arguments": {
"css":".el-submenu__item[title='商品管理'] .el-menu-item[title='商品列表']"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-submenu__item[title='商品管理'] .el-menu-item[title='商品列表']"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
, {
"role":"assistant","tool_calls":[ {
"function": {
"name":"click","arguments": {
"css":".el-button--primary.el-button--mini[title='添加']"
}
}
}
]
}
, {
"role":"tool","content":"Error: NoSuchElementException()\n Please fix your mistakes."
}
],"tools":[ {
"type":"function","function": {
"name":"open","description":"使用浏览器打开特定的url,并返回网页内容","parameters": {
"type":"object","required":["url"],"properties": {
"url": {
"type":"string"
}
}
}
}
}
, {
"type":"function","function": {
"name":"quit","description":"退出浏览器","parameters": {
"type":"object","properties": {
}
}
}
}
, {
"type":"function","function": {
"name":"get_current_url","description":"获取当前的url","parameters": {
"type":"object","properties": {
}
}
}
}
, {
"type":"function","function": {
"name":"find","description":"定位网页元素","parameters": {
"type":"object","required":["css"],"properties": {
"css": {
"type":"string"
}
}
}
}
}
, {
"type":"function","function": {
"name":"click","description":"以css的方式定位网页元素后点击","parameters": {
"type":"object","properties": {
"css": {
"type":"string"
}
}
}
}
}
, {
"type":"function","function": {
"name":"send_keys","description":"定位到css指定的元素,并输入text","parameters": {
"type":"object","required":["css","text"],"properties": {
"css": {
}
,"text": {
}
}
}
}
}
, {
"type":"function","function": {
"name":"maximizeWindow","description":"浏览器最大化\n:return:","parameters": {
"type":"object","properties": {
}