Python Selenium 完全指南:從入門到精通

Python Selenium 完全指南:從入門到精通

📚 目錄

  1. 環境準備與基礎入門
  2. 元素定位與交互操作
  3. 等待機制與異常處理
  4. 面向對象封裝與框架設計
  5. 進階技巧與最佳實踐
  6. 性能優化與調試技巧
  7. 實戰案例分析

環境準備與基礎入門

1. 安裝 Selenium 與瀏覽器驅動

安裝 Selenium
# 使用pip安裝最新版本
pip install selenium# 安裝特定版本
pip install selenium==4.10.0# 在虛擬環境中安裝(推薦)
python -m venv selenium_env
source selenium_env/bin/activate  # Linux/Mac
selenium_env\Scripts\activate.bat  # Windows
pip install selenium
安裝瀏覽器驅動

從Selenium 4.0開始,提供了自動管理驅動的功能,但了解手動安裝方法仍然很重要:

Chrome瀏覽器:

  • 訪問 ChromeDriver 下載頁面
  • 下載與本地Chrome版本匹配的驅動程序
  • 將驅動添加到系統PATH中或在代碼中指定路徑

Firefox瀏覽器:

  • 訪問 GeckoDriver 下載頁面
  • 下載適用于你操作系統的版本
  • 將驅動添加到系統PATH中或在代碼中指定路徑

Edge瀏覽器:

  • 訪問 Microsoft Edge Driver 下載頁面
  • 下載與本地Edge版本匹配的驅動程序

Safari瀏覽器:

  • Safari驅動已內置于macOS中
  • 需要在Safari瀏覽器中啟用開發者模式

2. Selenium 4.x 新特性

Selenium 4.x引入了許多重要的改進和新功能:

  • 相對定位器:允許基于其他元素的位置來查找元素
  • Service對象:用于更好地管理驅動程序服務
  • WebDriver Manager:自動管理驅動程序的下載和設置
  • CDP(Chrome DevTools Protocol)支持:允許訪問瀏覽器特定的功能

3. WebDriver初始化方法

使用Selenium Manager(推薦,Selenium 4.x)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service# 自動管理驅動
driver = webdriver.Chrome()
傳統方法(指定驅動路徑)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service# 指定驅動路徑
service = Service(executable_path='/path/to/chromedriver')
driver = webdriver.Chrome(service=service)
配置瀏覽器選項
from selenium import webdriver
from selenium.webdriver.chrome.options import Options# 創建Chrome選項對象
chrome_options = Options()
chrome_options.add_argument("--headless")  # 無頭模式
chrome_options.add_argument("--window-size=1920,1080")  # 設置窗口大小
chrome_options.add_argument("--disable-gpu")  # 禁用GPU加速
chrome_options.add_argument("--disable-extensions")  # 禁用擴展
chrome_options.add_argument("--proxy-server='direct://'")  # 代理設置
chrome_options.add_argument("--proxy-bypass-list=*")  # 繞過代理
chrome_options.add_argument("--start-maximized")  # 啟動時最大化窗口
chrome_options.add_experimental_option("prefs", {"download.default_directory": "/path/to/download/directory",  # 設置下載目錄"download.prompt_for_download": False,  # 禁用下載提示"download.directory_upgrade": True,"safebrowsing.enabled": True
})# 初始化WebDriver
driver = webdriver.Chrome(options=chrome_options)

4. 基礎瀏覽器操作

from selenium import webdriver# 初始化WebDriver
driver = webdriver.Chrome()# 窗口操作
driver.maximize_window()  # 最大化窗口
driver.set_window_size(1920, 1080)  # 設置窗口大小
driver.set_window_position(0, 0)  # 設置窗口位置# 導航操作
driver.get('https://www.example.com')  # 打開URL
driver.back()  # 后退
driver.forward()  # 前進
driver.refresh()  # 刷新頁面# 頁面信息
title = driver.title  # 獲取頁面標題
url = driver.current_url  # 獲取當前URL
page_source = driver.page_source  # 獲取頁面源代碼# Cookie操作
driver.add_cookie({"name": "key", "value": "value"})  # 添加Cookie
cookies = driver.get_cookies()  # 獲取所有Cookies
driver.delete_cookie("key")  # 刪除特定Cookie
driver.delete_all_cookies()  # 刪除所有Cookies# 關閉操作
driver.close()  # 關閉當前標簽頁
driver.quit()  # 關閉瀏覽器,釋放資源

5. 常見瀏覽器配置

無頭模式(Headless)
from selenium import webdriver
from selenium.webdriver.chrome.options import Optionschrome_options = Options()
chrome_options.add_argument("--headless")
driver = webdriver.Chrome(options=chrome_options)
使用代理
from selenium import webdriver
from selenium.webdriver.chrome.options import Optionschrome_options = Options()
chrome_options.add_argument('--proxy-server=http://proxyserver:port')
driver = webdriver.Chrome(options=chrome_options)
禁用圖片加載(提高性能)
from selenium import webdriver
from selenium.webdriver.chrome.options import Optionschrome_options = Options()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=chrome_options)

元素定位與交互操作

1. 元素定位基礎

Selenium提供了多種定位元素的方法,每種都有其適用場景:

from selenium import webdriver
from selenium.webdriver.common.by import Bydriver = webdriver.Chrome()
driver.get("https://www.example.com")# 1. 通過ID定位(最推薦,高效且唯一)
element = driver.find_element(By.ID, "login-button")# 2. 通過Name屬性定位
element = driver.find_element(By.NAME, "username")# 3. 通過Class Name定位(不唯一時返回第一個匹配元素)
element = driver.find_element(By.CLASS_NAME, "login-form")# 4. 通過Tag Name定位
element = driver.find_element(By.TAG_NAME, "button")# 5. 通過Link Text定位(完全匹配)
element = driver.find_element(By.LINK_TEXT, "Forgot Password?")# 6. 通過Partial Link Text定位(部分匹配)
element = driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")# 7. 通過CSS選擇器定位(強大且靈活)
element = driver.find_element(By.CSS_SELECTOR, "#login-form .submit-button")# 8. 通過XPath定位(最強大但可能較慢)
element = driver.find_element(By.XPATH, "//div[@id='login-form']//button")

2. 高級定位策略

XPath進階用法
# 絕對路徑(從根節點開始)
element = driver.find_element(By.XPATH, "/html/body/div/form/input")# 相對路徑(從任意節點開始)
element = driver.find_element(By.XPATH, "//input[@name='username']")# 使用contains()函數
element = driver.find_element(By.XPATH, "//button[contains(@class, 'login')]")# 使用text()函數
element = driver.find_element(By.XPATH, "//a[text()='Forgot Password?']")
element = driver.find_element(By.XPATH, "//a[contains(text(), 'Forgot')]")# 使用AND和OR操作符
element = driver.find_element(By.XPATH, "//input[@type='text' and @name='username']")
element = driver.find_element(By.XPATH, "//button[@type='submit' or @type='button']")# 通過父子關系定位
element = driver.find_element(By.XPATH, "//form[@id='login-form']/input")
parent = driver.find_element(By.XPATH, "//input[@id='username']/..")# 通過兄弟關系定位
element = driver.find_element(By.XPATH, "//input[@id='username']/following-sibling::input")
element = driver.find_element(By.XPATH, "//input[@id='password']/preceding-sibling::input")# 按索引定位
element = driver.find_element(By.XPATH, "(//input[@type='text'])[2]")# 使用軸(axes)
element = driver.find_element(By.XPATH, "//input[@id='username']/ancestor::form")
element = driver.find_element(By.XPATH, "//form/descendant::input")
CSS選擇器進階用法
# 基本選擇器
element = driver.find_element(By.CSS_SELECTOR, "#login-button")  # ID選擇器
element = driver.find_element(By.CSS_SELECTOR, ".login-form")    # Class選擇器
element = driver.find_element(By.CSS_SELECTOR, "input")          # 標簽選擇器# 屬性選擇器
element = driver.find_element(By.CSS_SELECTOR, "input[name='username']")
element = driver.find_element(By.CSS_SELECTOR, "input[name^='user']")  # 以user開頭
element = driver.find_element(By.CSS_SELECTOR, "input[name$='name']")  # 以name結尾
element = driver.find_element(By.CSS_SELECTOR, "input[name*='erna']")  # 包含erna# 組合選擇器
element = driver.find_element(By.CSS_SELECTOR, "form input[type='text']")
element = driver.find_element(By.CSS_SELECTOR, "form > input")  # 直接子元素
element = driver.find_element(By.CSS_SELECTOR, "label + input")  # 緊鄰兄弟元素
element = driver.find_element(By.CSS_SELECTOR, "label ~ input")  # 通用兄弟元素# 偽類選擇器
element = driver.find_element(By.CSS_SELECTOR, "input:first-child")
element = driver.find_element(By.CSS_SELECTOR, "input:last-child")
element = driver.find_element(By.CSS_SELECTOR, "input:nth-child(2)")
相對定位器(Selenium 4.x新特性)
from selenium.webdriver.support.relative_locator import locate_with# 獲取參考元素
username_field = driver.find_element(By.ID, "username")# 使用相對定位器
password_field = driver.find_element(locate_with(By.TAG_NAME, "input").below(username_field))
login_button = driver.find_element(locate_with(By.TAG_NAME, "button").below(password_field))
remember_me = driver.find_element(locate_with(By.TAG_NAME, "input").to_right_of(password_field))
forgot_password = driver.find_element(locate_with(By.TAG_NAME, "a").above(login_button))

3. 查找多個元素

# 查找所有符合條件的元素
elements = driver.find_elements(By.CSS_SELECTOR, ".product-item")# 遍歷元素列表
for element in elements:name = element.find_element(By.CLASS_NAME, "product-name").textprice = element.find_element(By.CLASS_NAME, "product-price").textprint(f"產品名稱: {name}, 價格: {price}")

4. 元素交互操作

# 輸入操作
element.send_keys("test@example.com")  # 輸入文本
element.send_keys(Keys.CONTROL, 'a')   # 鍵盤組合鍵(全選)
element.send_keys(Keys.BACK_SPACE)     # 退格鍵# 點擊操作
element.click()                        # 點擊元素
element.submit()                       # 提交表單(適用于表單元素內)# 清除操作
element.clear()                        # 清除文本輸入框# 獲取元素屬性和狀態
value = element.get_attribute("value")  # 獲取屬性值
text = element.text                     # 獲取元素文本內容
tag = element.tag_name                  # 獲取標簽名
size = element.size                     # 獲取元素大小
location = element.location             # 獲取元素位置
is_enabled = element.is_enabled()       # 元素是否啟用
is_selected = element.is_selected()     # 元素是否選中(復選框、單選按鈕等)
is_displayed = element.is_displayed()   # 元素是否可見# 特殊元素操作
# 下拉菜單
from selenium.webdriver.support.select import Select
select = Select(driver.find_element(By.ID, "dropdown"))
select.select_by_visible_text("Option 1")  # 通過文本選擇
select.select_by_value("option1")         # 通過值選擇
select.select_by_index(1)                 # 通過索引選擇
options = select.options                  # 獲取所有選項
first_option = select.first_selected_option  # 獲取當前選中選項
select.deselect_all()                      # 取消所有選擇(多選下拉框)# 復選框和單選按鈕
checkbox = driver.find_element(By.ID, "checkbox")
if not checkbox.is_selected():checkbox.click()

5. 元素查找最佳實踐

  1. 性能優化順序:ID > Name > CSS > XPath
  2. 避免使用
    • 絕對XPath路徑(容易失效)
    • 基于視覺位置的選擇器
    • 多級嵌套CSS選擇器
  3. 推薦使用
    • 有意義的ID和名稱屬性
    • 數據測試屬性(如data-testid)
    • 短而明確的CSS選擇器
  4. 建議添加
    • 頁面加載和元素的等待機制
    • 查找元素的超時和重試機制
    • 詳細的錯誤處理機制

等待機制與異常處理

1. 等待策略

在Web自動化中,頁面加載和元素渲染需要時間,等待機制至關重要。

隱式等待(Implicit Wait)
# 設置隱式等待時間(全局設置)
driver.implicitly_wait(10)  # 等待最多10秒直到元素出現

隱式等待會在查找元素時自動等待一段時間直到元素出現,如果在指定時間內未找到元素,則拋出異常。

顯式等待(Explicit Wait)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC# 等待元素可見
element = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "element_id"))
)# 等待元素可點擊
element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "button_id"))
)# 等待頁面標題包含特定文本
WebDriverWait(driver, 10).until(EC.title_contains("Home Page")
)# 等待元素消失
WebDriverWait(driver, 10).until(EC.invisibility_of_element_located((By.CLASS_NAME, "loading"))
)# 等待警告框出現
WebDriverWait(driver, 10).until(EC.alert_is_present()
)# 等待元素的文本內容滿足條件
WebDriverWait(driver, 10).until(EC.text_to_be_present_in_element((By.ID, "status"), "Success")
)# 等待元素的屬性值滿足條件
WebDriverWait(driver, 10).until(EC.text_to_be_present_in_element_attribute((By.ID, "input"), "value", "text")
)
自定義等待條件
from selenium.webdriver.support.ui import WebDriverWait# 自定義等待條件
def element_has_class(element, class_name):return class_name in element.get_attribute("class").split()# 使用自定義等待條件
element = driver.find_element(By.ID, "myElement")
WebDriverWait(driver, 10).until(lambda driver: element_has_class(element, "active"))
流暢等待(FluentWait)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException# 創建FluentWait實例
wait = WebDriverWait(driver,timeout=30,poll_frequency=2,  # 每2秒檢查一次ignored_exceptions=[NoSuchElementException, StaleElementReferenceException]
)# 使用FluentWait
element = wait.until(EC.element_to_be_clickable((By.ID, "myElement")))

2. 異常處理

Selenium操作可能會觸發各種異常,合理的異常處理可以提高腳本的健壯性。

常見異常類型
from selenium.common.exceptions import (NoSuchElementException,  # 元素未找到TimeoutException,        # 等待超時ElementNotVisibleException,  # 元素不可見ElementNotInteractableException,  # 元素不可交互StaleElementReferenceException,  # 元素已過時(DOM已更新)WebDriverException,      # WebDriver通用異常InvalidSelectorException,  # 無效的選擇器UnexpectedAlertPresentException,  # 意外的警告框NoAlertPresentException,  # 沒有警告框SessionNotCreatedException,  # 會話創建失敗ElementClickInterceptedException  # 元素點擊被攔截
)
基本異常處理
try:element = driver.find_element(By.ID, "non_existent_element")element.click()
except NoSuchElementException:print("元素未找到")
except ElementNotInteractableException:print("元素不可交互")
except Exception as e:print(f"發生其他異常: {e}")
重試機制
def retry_click(driver, by, value, max_attempts=3, wait_time=1):"""嘗試多次點擊元素"""from time import sleepfor attempt in range(max_attempts):try:element = driver.find_element(by, value)element.click()return Trueexcept (NoSuchElementException, ElementNotInteractableException, ElementClickInterceptedException, StaleElementReferenceException) as e:if attempt == max_attempts - 1:print(f"無法點擊元素,錯誤: {e}")return Falsesleep(wait_time)return False
處理StaleElementReferenceException
def get_fresh_element(driver, by, value):"""獲取一個新鮮的元素引用,避免StaleElementReferenceException"""try:return driver.find_element(by, value)except StaleElementReferenceException:# 重新查找元素return driver.find_element(by, value)
使用裝飾器處理異常
import functools
from time import sleepdef retry(max_attempts=3, wait_time=1):"""函數重試裝飾器"""def decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):for attempt in range(max_attempts):try:return func(*args, **kwargs)except (NoSuchElementException, ElementNotInteractableException, StaleElementReferenceException) as e:if attempt == max_attempts - 1:raise esleep(wait_time)return wrapperreturn decorator# 使用裝飾器
@retry(max_attempts=5, wait_time=2)
def click_element(driver, by, value):driver.find_element(by, value).click()

3. 等待策略最佳實踐

  1. 避免使用time.sleep():不靈活且低效
  2. 優先使用顯式等待:更精確,可控性更強
  3. 結合使用隱式等待和顯式等待:隱式等待作為全局保護,顯式等待針對特定場景
  4. 設置合理的超時時間:不要過長或過短
  5. 捕獲并處理超時異常:提供適當的恢復機制或用戶友好的錯誤信息
  6. 為不同網絡環境調整等待策略:可配置的超時參數

面向對象封裝與框架設計

1. 頁面對象模型(Page Object Model, POM)

頁面對象模型是一種設計模式,將頁面的元素和操作封裝在類中,使測試代碼更加清晰和可維護。

基本POM結構
class BasePage:"""所有頁面的基類"""def __init__(self, driver):self.driver = driverdef find_element(self, locator):return self.driver.find_element(*locator)def find_elements(self, locator):return self.driver.find_elements(*locator)def click(self, locator):self.find_element(locator).click()def input_text(self, locator, text):element = self.find_element(locator)element.clear()element.send_keys(text)def get_text(self, locator):return self.find_element(locator).textdef is_element_present(self, locator):try:self.find_element(locator)return Trueexcept NoSuchElementException:return Falsedef wait_for_element(self, locator, timeout=10):try:WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))return Trueexcept TimeoutException:return Falseclass LoginPage(BasePage):"""登錄頁面對象"""# 頁面元素定位器_username_field = (By.ID, "username")_password_field = (By.ID, "password")_login_button = (By.ID, "login_button")_error_message = (By.CLASS_NAME, "error-message")def __init__(self, driver):super().__init__(driver)self.driver.get("https://example.com/login")def enter_username(self, username):self.input_text(self._username_field, username)return selfdef enter_password(self, password):self.input_text(self._password_field, password)return selfdef click_login(self):self.click(self._login_button)# 根據登錄結果返回不同的頁面對象if "dashboard" in self.driver.current_url:return DashboardPage(self.driver)return selfdef login(self, username, password):self.enter_username(username)self.enter_password(password)return self.click_login()def get_error_message(self):if self.is_element_present(self._error_message):return self.get_text(self._error_message)return ""class DashboardPage(BasePage):"""儀表盤頁面對象"""_welcome_message = (By.ID, "welcome")_logout_button = (By.ID, "logout")def is_loaded(self):return self.wait_for_element(self._welcome_message)def get_welcome_message(self):return self.get_text(self._welcome_message)def logout(self):self.click(self._logout_button)return LoginPage(self.driver)
使用POM進行測試
def test_login_success():driver = webdriver.Chrome()try:login_page = LoginPage(driver)dashboard_page = login_page.login("valid_user", "valid_password")assert dashboard_page.is_loaded()assert "Welcome" in dashboard_page.get_welcome_message()finally:driver.quit()def test_login_failure():driver = webdriver.Chrome()try:login_page = LoginPage(driver)result_page = login_page.login("invalid_user", "invalid_password")assert isinstance(result_page, LoginPage)assert "Invalid credentials" in result_page.get_error_message()finally:driver.quit()

2. 測試框架集成

與unittest集成
import unittest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManagerclass TestLogin(unittest.TestCase):def setUp(self):self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))self.driver.maximize_window()self.login_page = LoginPage(self.driver)def tearDown(self):self.driver.quit()def test_valid_login(self):dashboard_page = self.login_page.login("valid_user", "valid_password")self.assertTrue(dashboard_page.is_loaded())self.assertIn("Welcome", dashboard_page.get_welcome_message())def test_invalid_login(self):result_page = self.login_page.login("invalid_user", "invalid_password")self.assertIsInstance(result_page, LoginPage)self.assertIn("Invalid credentials", result_page.get_error_message())if __name__ == "__main__":unittest.main()
與pytest集成
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager@pytest.fixture
def driver():# 設置driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))driver.maximize_window()yield driver# 清理driver.quit()@pytest.fixture
def login_page(driver):return LoginPage(driver)def test_valid_login(login_page):dashboard_page = login_page.login("valid_user", "valid_password")assert dashboard_page.is_loaded()assert "Welcome" in dashboard_page.get_welcome_message()def test_invalid_login(login_page):result_page = login_page.login("invalid_user", "invalid_password")assert isinstance(result_page, LoginPage)assert "Invalid credentials" in result_page.get_error_message()
與Behave(BDD)集成
# features/login.feature
Feature: User LoginAs a userI want to be able to login to the applicationSo that I can access my accountScenario: Successful login with valid credentialsGiven the user is on the login pageWhen the user enters "valid_user" as usernameAnd the user enters "valid_password" as passwordAnd the user clicks the login buttonThen the user should be redirected to the dashboardAnd the dashboard should display a welcome messageScenario: Failed login with invalid credentialsGiven the user is on the login pageWhen the user enters "invalid_user" as usernameAnd the user enters "invalid_password" as passwordAnd the user clicks the login buttonThen the user should remain on the login pageAnd an error message should be displayed
# steps/login_steps.py
from behave import given, when, then
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage@given('the user is on the login page')
def step_impl(context):context.login_page = LoginPage(context.driver)@when('the user enters "{username}" as username')
def step_impl(context, username):context.login_page.enter_username(username)@when('the user enters "{password}" as password')
def step_impl(context, password):context.login_page.enter_password(password)@when('the user clicks the login button')
def step_impl(context):context.result_page = context.login_page.click_login()@then('the user should be redirected to the dashboard')
def step_impl(context):assert isinstance(context.result_page, DashboardPage)@then('the dashboard should display a welcome message')
def step_impl(context):assert "Welcome" in context.result_page.get_welcome_message()@then('the user should remain on the login page')
def step_impl(context):assert isinstance(context.result_page, LoginPage)@then('an error message should be displayed')
def step_impl(context):assert "Invalid credentials" in context.result_page.get_error_message()

3. 高級框架設計模式

工廠模式
class PageFactory:"""頁面對象工廠類"""@staticmethoddef get_page(page_name, driver):pages = {"login": LoginPage,"dashboard": DashboardPage,"profile": ProfilePage,"settings": SettingsPage}if page_name.lower() not in pages:raise ValueError(f"不支持的頁面: {page_name}")return pages[page_name.lower()](driver)
單例模式(驅動管理器)
class WebDriverManager:"""WebDriver管理器(單例模式)"""_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super(WebDriverManager, cls).__new__(cls)cls._instance.driver = Nonereturn cls._instancedef get_driver(self, browser="chrome"):if self.driver is None:if browser.lower() == "chrome":self.driver = webdriver.Chrome()elif browser.lower() == "firefox":self.driver = webdriver.Firefox()else:raise ValueError(f"不支持的瀏覽器: {browser}")self.driver.maximize_window()self.driver.implicitly_wait(10)return self.driverdef quit(self):if self.driver:self.driver.quit()self.driver = None
策略模式(等待策略)
from abc import ABC, abstractmethodclass WaitStrategy(ABC):"""等待策略基類"""@abstractmethoddef wait_for(self, driver, locator):passclass VisibilityStrategy(WaitStrategy):"""等待元素可見策略"""def wait_for(self, driver, locator, timeout=10):return WebDriverWait(driver, timeout).until(EC.visibility_of_element_located(locator))class ClickableStrategy(WaitStrategy):"""等待元素可點擊策略"""def wait_for(self, driver, locator, timeout=10):return WebDriverWait(driver, timeout).until(EC.element_to_be_clickable(locator))class PresenceStrategy(WaitStrategy):"""等待元素存在策略"""def wait_for(self, driver, locator, timeout=10):return WebDriverWait(driver, timeout).until(EC.presence_of_element_located(locator))# 使用策略模式的高級頁面基類
class AdvancedBasePage:def __init__(self, driver):self.driver = driverself.wait_strategies = {"visible": VisibilityStrategy(),"clickable": ClickableStrategy(),"present": PresenceStrategy()}def find_element(self, locator, strategy="present"):return self.wait_strategies[strategy].wait_for(self.driver, locator)

4. 配置與日志管理

配置管理
import json
import osclass ConfigManager:"""配置管理器"""_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super(ConfigManager, cls).__new__(cls)cls._instance.config = {}cls._instance.load_config()return cls._instancedef load_config(self, config_file="config.json"):if os.path.exists(config_file):with open(config_file, "r") as f:self.config = json.load(f)else:# 默認配置self.config = {"browser": "chrome","implicit_wait": 10,"explicit_wait": 20,"base_url": "https://example.com","headless": False,"screenshots_dir": "screenshots","logs_dir": "logs"}def get(self, key, default=None):return self.config.get(key, default)
日志管理
import logging
import os
from datetime import datetimeclass LogManager:"""日志管理器"""_instance = Nonedef __new__(cls):if cls._instance is None:cls._instance = super(LogManager, cls).__new__(cls)cls._instance.setup_logger()return cls._instancedef setup_logger(self):config = ConfigManager().get("logs", {})logs_dir = config.get("dir", "logs")log_level = config.get("level", "INFO")if not os.path.exists(logs_dir):os.makedirs(logs_dir)log_file = os.path.join(logs_dir, f"test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")# 設置日志級別映射level_map = {"DEBUG": logging.DEBUG,"INFO": logging.INFO,"WARNING": logging.WARNING,"ERROR": logging.ERROR,"CRITICAL": logging.CRITICAL}# 設置根日志記錄器self.logger = logging.getLogger("selenium_framework")self.logger.setLevel(level_map.get(log_level.upper(), logging.INFO))# 文件處理器file_handler = logging.FileHandler(log_file)file_handler.setLevel(level_map.get(log_level.upper(), logging.INFO))# 控制臺處理器console_handler = logging.StreamHandler()console_handler.setLevel(level_map.get(log_level.upper(), logging.INFO))# 日志格式formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')file_handler.setFormatter(formatter)console_handler.setFormatter(formatter)# 添加處理器self.logger.addHandler(file_handler)self.logger.addHandler(console_handler)def get_logger(self):return self.logger

進階技巧與最佳實踐

1. 高級交互操作

ActionChains高級操作
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys# 基本鼠標操作
def perform_hover(driver, element):"""執行懸停操作"""ActionChains(driver).move_to_element(element).perform()def perform_right_click(driver, element):"""執行右鍵點擊操作"""ActionChains(driver).context_click(element).perform()def perform_double_click(driver, element):"""執行雙擊操作"""ActionChains(driver).double_click(element).perform()def perform_drag_and_drop(driver, source_element, target_element):"""執行拖放操作"""ActionChains(driver).drag_and_drop(source_element, target_element).perform()def perform_drag_and_drop_by_offset(driver, element, x_offset, y_offset):"""執行偏移拖放操作"""ActionChains(driver).drag_and_drop_by_offset(element, x_offset, y_offset).perform()# 組合鍵盤操作
def perform_ctrl_click(driver, element):"""執行Ctrl+點擊操作(多選)"""ActionChains(driver).key_down(Keys.CONTROL).click(element).key_up(Keys.CONTROL).perform()def perform_shift_click(driver, element):"""執行Shift+點擊操作(范圍選擇)"""ActionChains(driver).key_down(Keys.SHIFT).click(element).key_up(Keys.SHIFT).perform()def perform_select_all(driver):"""執行全選操作(Ctrl+A)"""ActionChains(driver).key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()def perform_copy(driver):"""執行復制操作(Ctrl+C)"""ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()def perform_paste(driver):"""執行粘貼操作(Ctrl+V)"""ActionChains(driver).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()# 鏈式組合操作
def perform_complex_action(driver, element1, element2):"""執行復雜組合操作"""ActionChains(driver)\.move_to_element(element1)\.pause(1)  # 暫停1秒.click()\.move_to_element(element2)\.click()\.perform()
處理JavaScript事件
def trigger_js_event(driver, element, event_name):"""觸發JavaScript事件"""js_code = f"arguments[0].dispatchEvent(new Event('{event_name}'));"driver.execute_script(js_code, element)def focus_element(driver, element):"""使元素獲取焦點"""driver.execute_script("arguments[0].focus();", element)def blur_element(driver, element):"""使元素失去焦點"""driver.execute_script("arguments[0].blur();", element)def scroll_to_element(driver, element):"""滾動到元素位置"""driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", element)def scroll_to_top(driver):"""滾動到頁面頂部"""driver.execute_script("window.scrollTo(0, 0);")def scroll_to_bottom(driver):"""滾動到頁面底部"""driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

2. 窗口與標簽頁管理

def switch_to_window_by_title(driver, title):"""切換到指定標題的窗口"""current_window = driver.current_window_handlefor window in driver.window_handles:driver.switch_to.window(window)if title in driver.title:return True# 如果沒有找到匹配標題的窗口,切回原窗口driver.switch_to.window(current_window)return Falsedef switch_to_window_by_url(driver, url_part):"""切換到URL包含指定部分的窗口"""current_window = driver.current_window_handlefor window in driver.window_handles:driver.switch_to.window(window)if url_part in driver.current_url:return True# 如果沒有找到匹配URL的窗口,切回原窗口driver.switch_to.window(current_window)return Falsedef close_all_windows_except_current(driver):"""關閉除當前窗口外的所有窗口"""current_window = driver.current_window_handlefor window in driver.window_handles:if window != current_window:driver.switch_to.window(window)driver.close()driver.switch_to.window(current_window)def open_new_tab(driver, url=None):"""打開新標簽頁"""driver.execute_script("window.open();")driver.switch_to.window(driver.window_handles[-1])if url:driver.get(url)def handle_popup_window(driver, action="accept"):"""處理彈出窗口"""try:if action.lower() == "accept":driver.switch_to.alert.accept()elif action.lower() == "dismiss":driver.switch_to.alert.dismiss()elif action.lower() == "text":return driver.switch_to.alert.textelse:raise ValueError(f"不支持的操作: {action}")return Trueexcept:return False

3. iframe處理

def switch_to_frame_by_index(driver, index):"""通過索引切換到iframe"""try:driver.switch_to.frame(index)return Trueexcept:return Falsedef switch_to_frame_by_name_or_id(driver, name_or_id):"""通過名稱或ID切換到iframe"""try:driver.switch_to.frame(name_or_id)return Trueexcept:return Falsedef switch_to_frame_by_element(driver, element):"""通過元素切換到iframe"""try:driver.switch_to.frame(element)return Trueexcept:return Falsedef switch_to_parent_frame(driver):"""切換到父iframe"""try:driver.switch_to.parent_frame()return Trueexcept:return Falsedef switch_to_default_content(driver):"""切換到主文檔"""try:driver.switch_to.default_content()return Trueexcept:return Falsedef get_iframe_count(driver):"""獲取頁面中iframe的數量"""return len(driver.find_elements(By.TAG_NAME, "iframe"))def execute_in_iframe(driver, iframe_locator, action_func):"""在iframe中執行操作"""driver.switch_to.frame(driver.find_element(*iframe_locator))try:result = action_func(driver)return resultfinally:driver.switch_to.default_content()

4. 文件上傳與下載

文件上傳
def upload_file(driver, file_input_locator, file_path):"""上傳文件(適用于<input type="file">元素)"""try:file_input = driver.find_element(*file_input_locator)file_input.send_keys(file_path)return Trueexcept Exception as e:print(f"文件上傳失敗: {e}")return Falsedef upload_file_without_input(driver, upload_button_locator, file_path):"""上傳文件(適用于沒有可見<input type="file">的情況)使用JS創建一個隱藏的文件輸入元素"""try:# 創建一個隱藏的文件輸入元素js_script = """const input = document.createElement('input');input.type = 'file';input.style.display = 'none';input.id = 'hidden-file-input';document.body.appendChild(input);return input;"""file_input = driver.execute_script(js_script)# 設置文件路徑file_input.send_keys(file_path)# 觸發上傳按鈕的點擊事件upload_button = driver.find_element(*upload_button_locator)driver.execute_script("arguments[0].click();", upload_button)# 移除隱藏的文件輸入元素driver.execute_script("document.getElementById('hidden-file-input').remove();")return Trueexcept Exception as e:print(f"文件上傳失敗: {e}")return False
文件下載
import os
import time
from pathlib import Pathdef setup_chrome_download_path(download_dir):"""設置Chrome瀏覽器的下載路徑"""options = webdriver.ChromeOptions()prefs = {"download.default_directory": download_dir,"download.prompt_for_download": False,"download.directory_upgrade": True,"safebrowsing.enabled": True}options.add_experimental_option("prefs", prefs)return optionsdef wait_for_download_to_complete(download_dir, timeout=60, check_interval=1):"""等待下載完成"""start_time = time.time()while time.time() - start_time < timeout:# 檢查是否有部分下載的文件(.crdownload, .part等)downloading_files = list(Path(download_dir).glob("*.crdownload")) + list(Path(download_dir).glob("*.part"))if not downloading_files:# 找出最近下載的文件downloaded_files = list(Path(download_dir).glob("*"))if downloaded_files:downloaded_files.sort(key=lambda x: x.stat().st_mtime, reverse=True)return str(downloaded_files[0])time.sleep(check_interval)raise TimeoutError("文件下載超時")def download_file(driver, download_button_locator, download_dir, timeout=60):"""下載文件"""try:# 確保下載目錄存在os.makedirs(download_dir, exist_ok=True)# 點擊下載按鈕download_button = driver.find_element(*download_button_locator)download_button.click()# 等待下載完成downloaded_file = wait_for_download_to_complete(download_dir, timeout)return downloaded_fileexcept Exception as e:print(f"文件下載失敗: {e}")return None

5. 截圖與日志

import os
import time
from datetime import datetimedef take_screenshot(driver, directory="screenshots", filename=None):"""截取屏幕截圖"""try:# 確保目錄存在os.makedirs(directory, exist_ok=True)# 如果未指定文件名,使用時間戳生成if not filename:timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"screenshot_{timestamp}.png"# 拼接完整路徑file_path = os.path.join(directory, filename)# 截圖driver.save_screenshot(file_path)return file_pathexcept Exception as e:print(f"截圖失敗: {e}")return Nonedef take_element_screenshot(driver, element, directory="screenshots", filename=None):"""截取元素截圖"""try:# 確保目錄存在os.makedirs(directory, exist_ok=True)# 如果未指定文件名,使用時間戳生成if not filename:timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"element_screenshot_{timestamp}.png"# 拼接完整路徑file_path = os.path.join(directory, filename)# 截取元素截圖element.screenshot(file_path)return file_pathexcept Exception as e:print(f"元素截圖失敗: {e}")return Nonedef screenshot_on_failure(func):"""失敗時自動截圖的裝飾器"""def wrapper(*args, **kwargs):try:return func(*args, **kwargs)except Exception as e:# 假設第一個參數是self,第二個參數是driverdriver = args[1] if len(args) > 1 else Noneif driver:timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"failure_{func.__name__}_{timestamp}.png"take_screenshot(driver, filename=filename)raise ereturn wrapper

6. 高級斷言與驗證

def verify_element_text(driver, locator, expected_text, contains=False):"""驗證元素文本"""try:element = driver.find_element(*locator)actual_text = element.textif contains:assert expected_text in actual_text, f"期望文本包含'{expected_text}',實際文本為'{actual_text}'"else:assert actual_text == expected_text, f"期望文本為'{expected_text}',實際文本為'{actual_text}'"return Trueexcept AssertionError as e:print(f"驗證失敗: {e}")return Falsedef verify_element_attribute(driver, locator, attribute, expected_value, contains=False):"""驗證元素屬性"""try:element = driver.find_element(*locator)actual_value = element.get_attribute(attribute)if contains:assert expected_value in actual_value, f"期望屬性'{attribute}'包含'{expected_value}',實際值為'{actual_value}'"else:assert actual_value == expected_value, f"期望屬性'{attribute}'為'{expected_value}',實際值為'{actual_value}'"return Trueexcept AssertionError as e:print(f"驗證失敗: {e}")return Falsedef verify_element_visible(driver, locator, timeout=10):"""驗證元素可見"""try:WebDriverWait(driver, timeout).until(EC.visibility_of_element_located(locator))return Trueexcept TimeoutException:print(f"元素在{timeout}秒內未可見: {locator}")return Falsedef verify_element_not_visible(driver, locator, timeout=10):"""驗證元素不可見"""try:WebDriverWait(driver, timeout).until(EC.invisibility_of_element_located(locator))return Trueexcept TimeoutException:print(f"元素在{timeout}秒內仍然可見: {locator}")return Falsedef verify_url(driver, expected_url, contains=False, timeout=10):"""驗證URL"""try:if contains:WebDriverWait(driver, timeout).until(lambda d: expected_url in d.current_url)else:WebDriverWait(driver, timeout).until(lambda d: d.current_url == expected_url)return Trueexcept TimeoutException:print(f"URL驗證失敗,期望URL{'' if contains else '為'}{expected_url},實際URL為{driver.current_url}")return Falsedef verify_title(driver, expected_title, contains=False, timeout=10):"""驗證頁面標題"""try:if contains:WebDriverWait(driver, timeout).until(lambda d: expected_title in d.title)else:WebDriverWait(driver, timeout).until(lambda d: d.title == expected_title)return Trueexcept TimeoutException:print(f"標題驗證失敗,期望標題{'' if contains else '為'}{expected_title},實際標題為{driver.title}")return False

性能優化與調試技巧

1. 性能測試與優化

測量頁面加載時間
def measure_page_load_time(driver, url):"""測量頁面加載時間"""start_time = time.time()driver.get(url)# 等待頁面完全加載WebDriverWait(driver, 60).until(lambda d: d.execute_script("return document.readyState") == "complete")end_time = time.time()load_time = end_time - start_timereturn load_time
使用Performance API獲取詳細性能數據
def get_performance_metrics(driver):"""獲取瀏覽器性能指標"""# 使用Navigation Timing APInavigation_timing = driver.execute_script("""var performance = window.performance;var timingObj = performance.timing;var loadTime = timingObj.loadEventEnd - timingObj.navigationStart;var dnsTime = timingObj.domainLookupEnd - timingObj.domainLookupStart;var tcpTime = timingObj.connectEnd - timingObj.connectStart;var serverTime = timingObj.responseEnd - timingObj.requestStart;var domTime = timingObj.domComplete - timingObj.domLoading;return {'loadTime': loadTime,'dnsTime': dnsTime,'tcpTime': tcpTime,'serverTime': serverTime,'domTime': domTime,'firstPaint': timingObj.responseStart - timingObj.navigationStart,'ttfb': timingObj.responseStart - timingObj.requestStart};""")return navigation_timingdef get_resource_timing(driver):"""獲取資源加載時間"""resources = driver.execute_script("""var resources = window.performance.getEntriesByType('resource');return resources.map(function(resource) {return {'name': resource.name,'startTime': resource.startTime,'duration': resource.duration,'initiatorType': resource.initiatorType,'size': resource.transferSize};});""")return resources
優化執行速度
def optimize_chrome_for_performance():"""優化Chrome瀏覽器以提高性能"""options = webdriver.ChromeOptions()# 禁用不必要的瀏覽器功能options.add_argument("--disable-extensions")options.add_argument("--disable-gpu")options.add_argument("--disable-dev-shm-usage")options.add_argument("--disable-browser-side-navigation")options.add_argument("--disable-infobars")options.add_argument("--disable-notifications")options.add_argument("--disable-popup-blocking")# 減少內存使用options.add_argument("--disable-features=site-per-process")options.add_argument("--process-per-site")# 禁用圖片加載以提高速度prefs = {"profile.managed_default_content_settings.images": 2,"profile.default_content_setting_values.notifications": 2,"profile.default_content_setting_values.geolocation": 2}options.add_experimental_option("prefs", prefs)# 使用無頭模式options.add_argument("--headless")return options

2. 高級調試技巧

獲取瀏覽器控制臺日志
def get_browser_logs(driver):"""獲取瀏覽器控制臺日志"""logs = driver.get_log('browser')return logsdef print_browser_logs(driver):"""打印瀏覽器控制臺日志"""logs = driver.get_log('browser')for log in logs:print(f"[{log['level']}] {log['message']}")

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81301.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81301.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81301.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于ffmpeg的音視頻編碼

1 音頻編碼 本質上是由pcm文件轉到一個協議文件 比如說aac協議 1.1 音頻基本知識回歸 比特率 比特率是指單位時間內傳輸或處理的比特&#xff08;bit&#xff09;數量&#xff0c;通常用 bps&#xff08;bits per second&#xff0c;比特每秒&#xff09;來表示。它是衡量數…

BT137-ASEMI機器人功率器件專用BT137

編輯&#xff1a;LL BT137-ASEMI機器人功率器件專用BT137 型號&#xff1a;BT137 品牌&#xff1a;ASEMI 封裝&#xff1a;TO-220F 批號&#xff1a;最新 引腳數量&#xff1a;3 封裝尺寸&#xff1a;如圖 特性&#xff1a;雙向可控硅 工作結溫&#xff1a;-40℃~150℃…

攻防世界 dice_game

dice_game ??????dice_game (1) motalymotaly-VMware-Virtual-Platform:~/桌面$ file game game: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]254…

Astral Ascent 星界戰士(星座上升) [DLC 解鎖] [Steam] [Windows SteamOS macOS]

Astral Ascent 星界戰士&#xff08;星座上升&#xff09; [DLC 解鎖] [Steam] [Windows & SteamOS & macOS] 需要有游戲正版基礎本體&#xff0c;安裝路徑不能帶有中文&#xff0c;或其它非常規拉丁字符&#xff1b; DLC 版本 至最新全部 DLC 后續可能無法及時更新文章…

git中reset和checkout的用法

git reset&#xff1a;重置分支的歷史與工作區? 核心作用??&#xff1a;移動當前分支的指針&#xff08;即改變分支的歷史&#xff09;&#xff0c;并可選地修改暫存區&#xff08;Index&#xff09;和工作目錄&#xff08;Working Directory&#xff09;。常用于撤銷提交或…

權限提升—Linux提權內核溢出漏洞輔助項目

前言 今天開啟Linux提權的篇章&#xff0c;主要是講一下Linux的內核漏洞提權&#xff0c;利用方式和Windows系統漏洞提權差不多&#xff0c;也是網上的項目掃一下&#xff0c;然后根據漏洞編號去找exp即可。 信息收集 首先要說一下Linux用戶的權限劃分。 系統用戶&#xff…

React Native Redux 使用指南 redux-toolkit

React Native Redux 使用指南 redux-toolkit 一個可預測和可維護的全局狀態管理 JavaScript 庫 Redux 和 React-Redux以及**reduxjs/toolkit 的關系&#xff1a;** Redux、React-Redux、reduxjs/toolkit 是 React 生態中狀態管理的「黃金三角組合」&#xff0c;它們的關系可…

JVM——Java 虛擬機是如何加載 Java 類的?

引入 在 Java 世界的底層運作中&#xff0c;類加載機制扮演著一個既神秘又關鍵的角色。它就像是一個精心設計的舞臺幕后 machinery&#xff0c;確保了 Java 程序能夠順利運行。今天&#xff0c;我們就深入探索 Java 虛擬機&#xff08;JVM&#xff09;是如何加載 Java 類的。 …

清華團隊提出時序聚類數據庫內高效方案,已被SIGMOD 2025接收

時間序列聚類是挖掘物聯網等場景下頻繁模式的關鍵技術&#xff0c;但現有SOTA方法&#xff08;如K-Shape&#xff09;面臨兩大瓶頸&#xff1a;1&#xff09;傳統數據庫因LSM-Tree存儲導致時間戳無序&#xff0c;難以直接支持高效聚類&#xff1b;2&#xff09;跨時間范圍查詢需…

【阿里云大模型高級工程師ACP學習筆記】2.8 部署模型

一、學習目標 特別說明:這一章節是2025年3月官方重點更新的部分,幾乎對內容重新翻新改造了一遍,重點突出了對于如何結合不同的阿里云產品來部署大模型進行了更加詳細的介紹和對比,這里整理給大家,方便大家參考。 在備考阿里云大模型高級工程師ACP認證的過程中,學習《2.8 …

第T10周:數據增強

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 從 tensorflow.keras 中導入 layers 模塊&#xff0c;包含了常用的神經網絡層&#xff0c;用來搭建模型結構。 檢查并列出系統中可用的物理 GPU 設備&#xff…

uniapp 支付寶小程序自定義 navbar 無效解決方案

如圖&#xff1a; uniapp編譯到支付寶小程序隱藏默認的導航欄失效了 解決方案&#xff1a; 在 pages.json 文件中找到 globalStyle 中加入以下代碼&#xff1a; "mp-alipay": {"transparentTitle": "always","titlePenetrate":…

vue2 el-element中el-select選中值,數據已經改變但選擇框中不顯示值,需要其他輸入框輸入值才顯示這個選擇框才會顯示剛才選中的值

項目場景&#xff1a; <el-table-column label"稅率" prop"TaxRate" width"180" align"center" show-overflow-tooltip><template slot-scope"{row, $index}"><el-form-item :prop"InquiryItemList. …

centos7 離線安裝python3 保留python2

一、事前準備&#xff1a; &#xff08;1&#xff09;查看centos具體版本 cat /etc/redhat-releaseCentOS Linux release 7.4.1708 (Core) &#xff08;2&#xff09;查看linux中當前python版本 centos7 默認安裝python2.7.5 &#xff08;3&#xff09;查看python3的依賴&#…

十三種通信接口芯片——《器件手冊--通信接口芯片》

目錄 通信接口芯片 簡述 基本功能 常見類型 應用場景 詳盡闡述 1 RS485/RS422芯片 1. RS485和RS422標準 2. 芯片功能 3. 典型芯片及特點 4. 應用場景 5. 設計注意事項 6. 選型建議 2 RS232芯片 1. RS232標準 2. 芯片功能 3. 典型芯片及特點 4. 應用場景 5. 設計注意事項 6…

2025年RAG技術發展現狀分析

2025年&#xff0c;大模型RAG&#xff08;檢索增強生成&#xff09;技術經歷了快速迭代與深度應用&#xff0c;逐漸從技術探索走向行業落地&#xff0c;同時也面臨安全性和實用性的新挑戰。以下是其發展現狀的綜合分析&#xff1a; 一、技術架構的持續演進 從單一到模塊化架構 …

case和字符串操作

使用if選擇結構 if [];then elif [];then #注意這個地方,java是else if else ; fi 使用for循環結構 使用for循環&#xff0c;語法結構如下所示&#xff1a; for 變量名 in 值1 值2 值3 #值的數量決定循環任務的次數 do命令序列 done#循環輸出1到10 for i in {1..10} #注…

Stm32 燒錄 Micropython

目錄 前言 準備工作 開始操作 問題回顧 后記 前言 去年曾經嘗試Pico制作openmv固件&#xff0c;由于知識儲備不夠最后失敗了&#xff0c;留了一個大坑&#xff0c;有了前幾天的基礎&#xff0c;慢慢補齊知識&#xff0c;最近這一周一直在學習如何編譯Stm固件并燒錄到單片機…

鹽化行業數字化轉型規劃詳細方案(124頁PPT)(文末有下載方式)

資料解讀&#xff1a;《鹽化行業數字化轉型規劃詳細解決方案》 詳細資料請看本解讀文章的最后內容。 該文檔聚焦鹽化行業數字化轉型&#xff0c;全面闡述了鹽化企業信息化建設的規劃方案&#xff0c;涵蓋戰略、架構、實施計劃、風險及效益等多個方面&#xff0c;旨在通過數字化…

2025年人工智能火爆技術總結

2025年人工智能火爆技術總結&#xff1a; 生成式人工智能 生成式人工智能可生成高質量的圖像、視頻、音頻和文本等多種內容。如昆侖萬維的SkyReels-V2能生成無限時長電影&#xff0c;其基于擴散強迫框架&#xff0c;結合多模態大語言模型和強化學習等技術&#xff0c;在運動動…