Selenium
Selenium
基础
selenium
运行原理
- 自动化程序调用
Selenium
客户端库函数(比如点击按钮元素) - 客户端库会发送
Selenium
命令给浏览器的驱动程序 - 浏览器驱动程序接收到命令后,驱动浏览器去执行命令
- 浏览器执行命令
- 浏览器驱动程序获取命令执行的结果,返回给我们自动化程序
- 自动化程序对返回结果进行处理
最大化、最小化、前进、后退、刷新、关闭
import time
from selenium import webdriver
driver = webdriver.Chrome()
url = "https://petstore.octoperf.com/actions/Catalog.action"
driver.get(url)
time.sleep(2)
driver.minimize_window() # 最小化
time.sleep(2)
driver.maximize_window() # 最大化
time.sleep(2)
driver.get("https://www.douyin.com/") # 访问另一个网址
time.sleep(2)
driver.back() # 后退
time.sleep(2)
driver.forward() # 前进
time.sleep(2)
driver.refresh() # 刷新
time.sleep(2)
driver.close() # 关闭
隐性等待
import time
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # 设置隐性等待
url = "https://www.baidu.com/"
driver.get(url)
# 输入框搜索
driver.find_element("id",'kw').send_keys("哈")
driver.find_element("id",'su').click()
driver.find_element("xpath",'//span[contains(text(),"百度汉语")]')
time.sleep(3)
driver.close()
隐性等待
隐性等待一般配合.until()
使用
from selenium.webdriver.support.wait import WebDriverWait
WebDriverWait(driver, 超时时长, 调用频率(可选,有默认值), 忽略异常(可选,有默认值)).until(可执行方法, 超时时返回的信息)
EC
模块常用函数
from selenium.webdriver.support import expected_conditions as EC
EC.presence_of_element_located(locator) # 判断一个元素是否存在,locator为一个元组,最经常使用的函数
EC.element_to_be_clickable(locator) # 判断元素是否可点击
import time
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
driver = webdriver.Chrome()
# driver.implicitly_wait(10) # 设置隐性等待
url = "https://www.baidu.com/"
driver.get(url)
driver.find_element("id",'kw').send_keys("哈")
driver.find_element("id",'su').click()
wait = WebDriverWait(driver, 10) # 初始化一个等待器
locator = ("xpath",'//span[contains(text(),"百度汉语")]')
# 直到
el = wait.until(expected_conditions.element_to_be_clickable(locator))
# WebDriverWait(driver, 10).until(expected_conditions.element_to_be_clickable(("xpath",'//span[contains(text(),"百度汉语")]')))
time.sleep(3)
driver.quit()
click
点击
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/buttons")
time.sleep(2)
print(driver.find_element("id","home").get_attribute("outerHTML")) # 打印该按钮的全部属性
driver.find_element("id","home").click()
assert driver.title == 'LetCode with Koushik'
driver.save_screenshot("./picture/bbb.png")
time.sleep(3)
driver.quit()
input
文本输入
import time
from selenium import webdriver
from selenium.webdriver import Keys
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/edit")
driver.find_element("id","fullName").send_keys("ppp")
driver.find_element("xpath","//input[@value='I am good']").clear() # 清除文本框中的内容
driver.save_screenshot("./picture/aaa.png")
time.sleep(3)
driver.quit()
# 其他
driver.find_element("id", "s").send_keys("mysql")
driver.find_element("id", "s").send_keys(Keys.CONTROL,"a") # 全选
driver.find_element("id", "s").send_keys(Keys.CONTROL,"c") # 复制
driver.find_element("id", "s").send_keys(Keys.CONTROL,"v") # 粘贴
print(driver.find_element("id", "s").get_attribute("value")) # 获取输入框内的文本值
print(driver.find_element("id", "s").tag_name) # 获取元素的标签名称
driver.find_element("id", "s").send_keys(Keys.ENTER) # 回车键
driver.find_element("id", "s").submit() # 回车键
radio
&checkbox
单选按钮及复选框
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/radio")
time.sleep(2)
def is_select(locator): # 简单封装
el = driver.find_element(*locator)
if el.is_selected(): # 判断单选框是否被选中
print("已经选择")
pass
else:
el.click()
# driver.find_element("id","yes").click()
is_select(("id", "yes"))
time.sleep(2)
is_select(("id", "two"))
time.sleep(2)
is_select(("id", "notfoo"))
time.sleep(3)
driver.save_screenshot("./picture/rrr.png")
driver.quit()
alert
弹窗
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/alert")
driver.find_element("class name", "is-link").click()
time.sleep(2)
print(driver.switch_to.alert.text) # 打印弹窗上现实的文本
driver.switch_to.alert.accept() # 点击确定
driver.find_element("id", "confirm").click()
time.sleep(2)
driver.switch_to.alert.dismiss() # 点击取消
driver.find_element("id", "prompt").click()
time.sleep(2)
driver.switch_to.alert.send_keys("prompt") # 在 弹窗 上输入文本
time.sleep(2)
driver.switch_to.alert.accept()
driver.find_element("id", "modern").click()
time.sleep(3)
driver.find_element("class name", "modal-close").click()
time.sleep(3)
driver.quit()
select
下拉框
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.select import Select
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/dropdowns")
select_x = driver.find_element("id", "fruits")
for i in ["header", "0", "1", "2", "3", '4']:
Select(select_x).select_by_value(i) # 通过 value 定位
driver.save_screenshot(f"./picture/ccc_value{i}.png")
time.sleep(2)
# for i in range(5):
# Select(select_x).select_by_index(i) # 通过索引定位
# time.sleep(2)
# for i in ["Select Fruit","Apple","Mango","Orange","Banana",'Pine Apple']:
# Select(select_x).select_by_visible_text(i) # 通过下拉框text定位
# time.sleep(2)
select_y = driver.find_element("id", "superheros")
for i in ["am", "bt", "ca", "ds", "ff", 'im']:
Select(select_y).select_by_value(i)
time.sleep(2)
driver.quit()
frame
标签
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/frame")
time.sleep(2)
# driver.switch_to.frame('firstFr') # 可以直接通过 id 或 name 定位
driver.switch_to.frame(0) # 通过 iframe 的索引切换,从0开始
driver.find_element("xpath", '//input[@placeholder="Enter name"]').send_keys("fish")
driver.save_screenshot("./picture/fff_1.png")
time.sleep(2)
iframe = driver.find_element("xpath", "//iframe[@src='innerFrame']")
driver.switch_to.frame(iframe) # 通过iframe元素定位切换
driver.find_element("name", 'email').send_keys("37857796")
driver.save_screenshot("./picture/fff_2.png")
time.sleep(2)
driver.switch_to.parent_frame() # 回到上一层 iframe
driver.find_element("name", 'lname').send_keys("haha")
driver.save_screenshot("./picture/fff_3.png")
driver.switch_to.default_content() # 回到默认 iframe
print(driver.find_element("xpath", "//h1[text()=' Frame']").text)
time.sleep(2)
driver.quit()
window
切换
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/windows")
time.sleep(2)
driver.find_element("id", "home").click()
print(driver.current_window_handle) # 打印当前窗口名称
time.sleep(2)
handles = driver.window_handles # 当前浏览器所有窗口
print(handles)
driver.switch_to.window(handles[-1])
print(driver.current_window_handle)
assert driver.title == "LetCode - Testing Hub"
time.sleep(3)
driver.quit()
elements
多元素
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/elements")
time.sleep(2)
driver.find_element("name", "username").send_keys("dd")
driver.find_element("id", "search").click()
els = driver.find_elements("xpath", "//a[@target='_blank']")
time.sleep(3)
print(type(els))
for i in els:
print(i)
# print(i.get_attribute("outerHTML"))
time.sleep(3)
driver.quit()
selectable
多选择
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver import ActionChains, Keys
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/selectable")
time.sleep(2)
el1 = driver.find_element("xpath", "//h3[text()='Selenium']")
el2 = driver.find_element("xpath", "//h3[text()='Appium']")
ActionChains(driver).key_down(Keys.CONTROL).click(el1).click(el2).key_up(Keys.CONTROL).perform()
time.sleep(3)
driver.quit()
drag_and_drop
拖拽
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/dropable")
time.sleep(2)
source = driver.find_element("id", "draggable")
target = driver.find_element("id", "droppable")
ActionChains(driver).drag_and_drop(source, target).perform() # 将元素从 source 拖拽到 target
ActionChains(driver).drag_and_drop_by_offset(el, 290, 290).perform() # 拖动指定元素偏移(不生效,暂时找不到原因)
ActionChains(driver).click_and_hold(el).move_by_offset(290, 290).release().perform() # 不生效,暂时找不到原因
time.sleep(3)
driver.quit()
move_to_element
鼠标悬停
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(10)
driver.get("https://www.runoob.com/html/html-tutorial.html")
time.sleep(1)
el = driver.find_element("xpath", "//a[text()=' HTML / CSS']")
ActionChains(driver).move_to_element(el).perform()
driver.find_element("xpath", "//a[text()='HTML5 教程']").click()
assert driver.title == "HTML5 教程 | 菜鸟教程"
time.sleep(3)
driver.quit()
其他操作:
右击鼠标:context_click()
双击鼠标:double_click()
upload
文件上传
import time
import keyboard # pip install keyboard
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/file")
time.sleep(2)
driver.find_element("xpath", "//span[contains(text(),'Choose a file')]").click()
time.sleep(2)
keyboard.write("D:\win11专业版秘钥.txt") # 上传文件地址
time.sleep(1)
keyboard.press("enter") # 按 回车 键
time.sleep(3)
driver.quit()
download
文件下载
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
# profile.default_content_settings.popups:设置为0禁止弹出窗口
# download.default_directory:设置下载路径(路径前面加转义字符r会下载失败)
prefs = {'profile.default_content_settings.popups': 0, 'download.default_directory': 'D:\code\PycharmProjects\selenium_test\picture\'}
option.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(60)
driver.get("https://letcode.in/file")
time.sleep(2)
driver.find_element("id", "xls").click()
time.sleep(3)
driver.quit()
js
处理自动化
js
滚动条
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.get('https://www.runoob.com/')
driver.implicitly_wait(60)
time.sleep(1)
# window.scrollTo(x, y) # 滚动条横向移动到x像素位置,纵向移动到y像素位置
# document.documentElement.scrollHeight # 整个页面竖向滚动条像素大小
# js = "var q=document.documentElement.scrollTo=n" # 控制滚动条竖向移动到n像素的位置
js_bottom = "window.scrollTo(0,document.documentElement.scrollHeight)"
driver.execute_script(js_bottom) # 滚到页面最底部
time.sleep(2)
el_XQuery = driver.find_element("xpath", '//h4[text()="【学习 XQuery】"]')
js_XQuery = "arguments[0].scrollIntoView()" # 下拉聚焦元素的位置
driver.execute_script(js_XQuery, el_XQuery)
el_XQuery.click()
time.sleep(3)
assert driver.title == "XQuery 教程 | 菜鸟教程"
driver.quit()
js
处理只读控件
# 删除readonly属性
js = 'document.getElementById("id元素值").removeAttribute("readonly");'
driver.execute_script(js)
# driver.find_element_by_id("id元素值").clear()
# driver.find_element_by_id("id元素值").send_keys("2021-12-25")
js
处理元素定位
有时候的定位元素就展示在页面上,但是webdriver
一直无法对浏览器操作,所以需要使用js
来操作
ele = driver.find_element("xpath","//div[text()='箱包']")
driver.execute_script("arguments[0].click();",ele)
driver.execute_script("return document.getElementById('id元素值').click()")
cookie
操作
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
option = Options()
option.page_load_strategy = "eager"
driver = webdriver.Chrome(options=option)
driver.implicitly_wait(10)
driver.get("https://www.runoob.com/html/html-tutorial.html")
time.sleep(1)
cookies = driver.get_cookies() # 获取当前所有cookie信息
for cookie in cookies:
# 值打印cookie中的name和value
print(f"{cookie['name']} -> {cookie['value']}")
print("================================================================================")
c1 = [{'name': '.CNBlogsCookie', 'value': 'xxxx'}, {"name": "key-aaaaaaa", "value": "value-aaaaaaa"}]
for i in c1:
driver.add_cookie(i)
cookies = driver.get_cookies()
for cookie in cookies:
print(f"{cookie['name']} -> {cookie['value']}")
print("================================================================================")
print(driver.get_cookie("key-aaaaaaa")) # 获取当前指定名称的cookie信息
driver.delete_cookie(".CNBlogsCookie") # 删除当前指定名称的cookie信息
print(driver.get_cookies())
driver.delete_all_cookies() # 删除所有的cookie信息
print("================================================================================")
print(driver.get_cookies())
time.sleep(3)
driver.quit()
Selenium
自动化
框架
-
PO(Page Object):页面对象模型,它是一种设计思想,意思是,把一个页面,当做一个对象,页面的元素和元素之间操作方法就是页面对象的属性和行为。
- 表现层(基础层BasePage):封装一些最基础的selenium原生的api方法,元素定位,框架跳转等
- 操作层(PO层):元素定位、获得元素对象,页面动作,例如点击、输入、拖拽等
- 业务层(测试用例层):业务逻辑,数据驱动,在页面中对若干元素操作后所实现的步骤(测试用例)
Page Object模式是一种自动化测试设计模式,将页面定位和业务操作分开,分离测试对象(元素对象)和测试脚本(用例脚本),UI界面的改变不需要修改业务逻辑代码,只需要找到对应的PO页修改定位即可,数据代码分离,PO模式能提高代码的可读性、高复用性、可维护性。
-
Python+Selenium+Pytest+pymysql+ openpyxl+log
常见 Selenium 异常
NoSuchElementException
:没有该元素异常
TimeoutException
:超时异常
ElementNotVisibleException
:元素不可见异常
NoSuchAttributeException
:没有这样属性异常
NoSuchFrameException
:没有该frame异常
配置相关
import pathlib
class Config():
# 当前的文件路径
current_path = pathlib.Path(__file__).absolute()
# root
root = current_path.parent.parent
# 驱动路径
driver_path = root / "drivers/chromedriver.exe" # 谷歌浏览器驱动位置
# 默认等待时间
default_timeout = 10
# 域名
host = "测试网站"
# 默认加载策略
load_strategy = "eager"
# 用户名
user_mobile = "手机号"
# 密码
passwd = "密码"
# Database
database = dict(host="数据库IP地址",
port=端口号,
user="用户名",
password="密码",
database="数据库")
测试夹具-驱动封装
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from config.setting import Config
from pages.loginpages import Loginpage
@pytest.fixture()
def driver():
driver_path = Config.driver_path # 浏览器驱动地址
option = Options()
option.page_load_strategy = Config.load_strategy # 页面加载策略
service = Service(executable_path=driver_path)
d = webdriver.Chrome(options=option, service=service)
d.implicitly_wait(Config.default_timeout) # 设置隐性等待
yield d
time.sleep(3)
d.quit()
@pytest.fixture()
def login(driver):
Loginpage(driver).login(Config.user_mobile, Config.user_passwd)
Loginpage(driver).get_account_info(Config.user_mobile)
logging
日志模块二次封装
import logging
import time
from datetime import datetime
from logging.handlers import RotatingFileHandler
from PycharmProjects.shangcheng.config.setting import Config
if not (Config.root / "logs").exists():
(Config.root / "logs").mkdir()
logs_path = Config.root / "logs"
data = time.strftime("%Y-%m-%d_%H-%M-%S_%p", time.localtime())
class Logging():
def __init__(self):
self.logger = logging.getLogger('日志处理')
self.logger.setLevel(logging.INFO)
self.log_formatter = logging.Formatter(
fmt="%(asctime)s,%(msecs)03d - %(levelname)s - [%(filename)s] - %(funcName)s - [line: %(lineno)d] - %(message)s",
datefmt="%Y-%m-%d %p %H:%M:%S")
def __create_console_handler(self):
# self.console_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
self.console_handler = logging.StreamHandler() # 默认是sys.stderr
self.console_handler.setLevel(logging.DEBUG)
self.console_handler.setFormatter(self.log_formatter)
return self.console_handler
def __create_file_handler(self):
# self.file_handler = logging.FileHandler(logs_path / 'logs.log', encoding="utf-8")
self.file_handler = RotatingFileHandler(filename=logs_path / f"log_{data}.log", mode="a", maxBytes=5 * 1024 * 1024, backupCount=5, encoding='utf-8')
self.file_handler.setLevel(logging.INFO)
self.file_handler.setFormatter(self.log_formatter)
return self.file_handler
def add_handler(self):
self.__create_console_handler()
self.__create_file_handler()
if not self.logger.handlers:
self.logger.addHandler(self.console_handler)
self.logger.addHandler(self.file_handler)
return self.logger
logger = Logging().add_handler()
Mysql
数据库封装
import pymysql
from pymysql.cursors import DictCursor
from config.setting import Config
class DB:
def __init__(self):
self.conn = pymysql.connect(**Config.database)
def query_one(self, sql):
cursor = self.conn.cursor(cursor=DictCursor)
cursor.execute(sql)
result = cursor.fetchone()
cursor.close()
return result
def query_all(self, sql):
cursor = self.conn.cursor(cursor=DictCursor)
cursor.execute(sql)
result = cursor.fetchall()
cursor.close()
return result
def close(self):
"""关闭数据库"""
self.conn.close()
openpyxl
封装
"""专门去操作Excel表格的"""
import openpyxl
def read_excel(file, sheet_name):
workbook = openpyxl.load_workbook(file)
sheet = workbook[sheet_name]
items = list(sheet.values)
title = items[0]
new_list = [dict(zip(title, item)) for item in items[1:]]
return new_list
Selenium
常用操作方法
from selenium.webdriver import Chrome, ActionChains, Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage(object):
"""把所有的浏览器常用操作都封装成现成的方法直接使用"""
def __init__(self, driver: Chrome):
self.driver = driver
def goto(self, url):
"""访问URL"""
self.driver.get(url)
def click(self, locator):
"""封装浏览器的点击操作"""
# el.click() locator = ("xpath", 'xxx')
# ActionChains().click()
el = self.driver.find_element(*locator)
try:
el.click()
except:
ActionChains(self.driver).click(el).perform()
def send_keys(self, words, locator=None):
# 输入
if locator:
el = self.driver.find_element(*locator)
# 相当于 ActionChains 里面的 send_keys_to_element
# 先点击元素el,再然后在输入内容,先会有光标的聚焦
el.send_keys(words)
return
else:
ActionChains(self.driver).send_keys(words).perform()
def switch_to_frame(self, locator):
"""切换iframe"""
iframe = self.driver.find_element(*locator)
self.driver.switch_to.frame(iframe)
def switch_to_window(self):
"""切换窗口"""
handles = self.driver.window_handles
self.driver.switch_to.window(handles[-1])
def click_alert(self, type, word=None):
"""弹框处理"""
alert = self.driver.switch_to.alert
if type == "accept":
alert.accept()
elif type == "dismiss":
alert.dismiss()
elif type == "sendkeys":
alert.send_keys(word)
alert.accept()
def select_by_text(self, locator, text):
"""通过值定位下拉框"""
el = self.driver.find_element(*locator)
Select(el).select_by_visible_text(text)
def assert_text_equal(self, locator, expected):
"""断言元素的文本"""
el = self.driver.find_element(*locator)
assert el.text == expected
def wait_element_located(self, locator):
"""等待元素出现"""
wait = WebDriverWait(self.driver, 10)
wait.until(EC.presence_of_element_located(locator))
def assert_title_equal(self, expected):
"""断言当前网页的title"""
assert self.driver.title == expected
def double_click(self, locator):
"""双击"""
el = self.driver.find_element(*locator)
ActionChains(self.driver).double_click(el).perform()
def drag_and_drop(self, locator1, locator2):
"""鼠标拖动"""
el1 = self.driver.find_element(*locator1)
el2 = self.driver.find_element(*locator2)
ActionChains(self.driver).drag_and_drop(el1, el2).perform()
def press_enter(self):
"""回车"""
ActionChains(self.driver).send_keys(Keys.ENTER).perform()
def copy(self):
"""复制"""
action = ActionChains(self.driver)
action.key_down(Keys.CONTROL).send_keys("c").key_up(Keys.CONTROL).perform()
def paste(self):
"""粘贴"""
action = ActionChains(self.driver)
action.key_down(Keys.CONTROL).send_keys("v").key_up(Keys.CONTROL).perform()
def js_click(self, locator):
"""使用js点击坐标"""
el = self.driver.find_element(*locator)
js = "arguments[0].click();"
self.driver.execute_script(js, el)
def scroll_to_element(self, locator):
"""js滚动到指定元素位置"""
el = self.driver.find_element(*locator)
js = "arguments[0].scrollIntoView()"
self.driver.execute_script(js, el)
def scroll_to_bottom(self):
"""滚动到窗口底部"""
script = 'window.scrollTo(0, document.body.scrollHeight)'
self.driver.execute_script(script)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix