使用Playwright+Pytest構(gòu)建Web?UI自動化測試框架完整示例
一、為什么選擇 Playwright?
1.1 Playwright vs Selenium 對比
先來看一個直觀的對比:
| 特性 | Playwright | Selenium |
|---|---|---|
| 瀏覽器支持 | ? 原生支持 Chromium、Firefox、WebKit | ?? 需要額外驅(qū)動程序 |
| 等待機(jī)制 | ? 內(nèi)置自動同步,無需顯式等待 | ? 需要手動 WebDriverWait |
| 網(wǎng)絡(luò)攔截 | ? 原生 Mock API 能力 | ? 第三方擴(kuò)展支持差 |
| 鏈路追蹤 | ? 內(nèi)置 Trace 功能,完整記錄操作路徑 | ? 不支持 |
| 跨域處理 | ? 無跨域限制 | ?? 有跨域問題 |
| 執(zhí)行效率 | ? 更快(優(yōu)化程度更高) | ?? 相對較慢 |
| API 設(shè)計 | ? 現(xiàn)代化、簡潔直觀 | ?? 有歷史包袱 |
核心優(yōu)勢示例:
# ? Playwright:自動等待,開箱即用
page.click('button') # 自動等待元素可點擊,無需手動等待
page.fill('#username', 'admin') # 自動等待輸入框可用
# ? Selenium:需要手動配置等待
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.TAG_NAME, "button")))
element.click()
二、架構(gòu)設(shè)計與分層
2.1 項目目錄結(jié)構(gòu)
playwright_ui/ ├── cases/ # ?? 測試用例層 │ ├── conftest.py # Fixture 定義 │ ├── test_login.py # 登錄功能測試 │ ├── test_add_project.py # 創(chuàng)建項目測試 │ ├── test_project_list.py # 項目列表測試 │ └── more_accounts/ # 多賬號場景 │ ├── pages/ # ?? Page Object Model 層 │ ├── login_page.py # 登錄頁面對象 │ ├── register_page.py # 注冊頁面對象 │ ├── add_project_page.py # 新增項目頁面對象 │ ├── project_list_page.py # 項目列表頁面對象 │ └── list_env_page.py # 環(huán)境列表頁面對象 │ ├── plugins/ # ?? 自定義插件 │ ├── pytest_playwright.py # Playwright 集成插件 │ └── pytest_base_url_plugin.py # URL 管理插件 │ ├── mocks/ # ?? Mock 數(shù)據(jù) │ └── mock_api.py # API Mock 配置 │ ├── conftest.py # 全局 Fixture ├── pytest.ini # Pytest 配置 ├── run.py # 啟動入口 └── requirements.txt # 依賴列表
2.2 分層架構(gòu)設(shè)計
┌──────────────────────────────────┐
│ Test Case Layer │
│ - 業(yè)務(wù)場景測試 │
│ - 數(shù)據(jù)驅(qū)動 │
└─────────────┬──────────────────┘
│
┌─────────────▼──────────────────┐
│ Page Object Model Layer │
│ - 頁面元素封裝 │
│ - 業(yè)務(wù)操作方法 │
└─────────────┬──────────────────┘
│
┌─────────────▼──────────────────┐
│ Fixture & Plugin Layer │
│ - Browser/Page fixture │
│ - Screenshot/Video/Trace │
│ - 自動化報告 │
└─────────────┬──────────────────┘
│
┌─────────────▼──────────────────┐
│ Playwright API │
│ - 瀏覽器自動化能力 │
└──────────────────────────────────┘三、核心特性詳解
3.1 Page Object Model(POM)模式
什么是 POM? 將頁面元素定位和業(yè)務(wù)操作封裝為對象,提高代碼可維護(hù)性。
示例:LoginPage
# pages/login_page.py
from playwright.sync_api import Page
class LoginPage:
"""登錄頁面對象"""
def __init__(self, page: Page):
self.page = page
# 元素定位 - 使用 Playwright 的強(qiáng)大選擇器
self.locator_username = page.get_by_label("用 戶 名:")
self.locator_password = page.get_by_label("密 碼:")
self.locator_login_btn = page.locator('text=立即登錄')
self.locator_login_error = page.locator('text=賬號或密碼不正確!')
# 頁面操作 - 原子操作
def navigate(self):
"""導(dǎo)航到登錄頁"""
self.page.goto("/login.html")
def fill_username(self, username):
"""輸入用戶名"""
self.locator_username.fill(username)
def fill_password(self, password):
"""輸入密碼"""
self.locator_password.fill(password)
def click_login_button(self):
"""點擊登錄按鈕"""
self.locator_login_btn.click()
# 業(yè)務(wù)操作 - 組合操作
def login(self, username, password) -> None:
"""完整登錄流程"""
self.fill_username(username)
self.fill_password(password)
self.click_login_button()
測試代碼變得簡潔優(yōu)雅:
# cases/test_login.py
class TestLogin:
"""登錄功能測試"""
@pytest.fixture(autouse=True)
def start_for_each(self, unlogin_page):
self.login = LoginPage(unlogin_page)
self.login.navigate()
yield
def test_login_success(self):
"""正常登錄流程"""
# 業(yè)務(wù)代碼一目了然
self.login.login("py", "123456")
expect(self.login.page).to_have_url("/index.html")
def test_login_with_empty_username(self):
"""用戶名為空驗證"""
self.login.fill_username('')
self.login.fill_password('123456')
self.login.click_login_button()
# 使用 Playwright 的斷言
expect(self.login.locator_username_tip1).to_be_visible()
expect(self.login.locator_username_tip1).to_contain_text("不能為空")
POM 的優(yōu)勢:
? 提高可維護(hù)性:定位器改變只需改一處
? 提高復(fù)用性:多個測試文件可共享同一 Page 對象
? 提高可讀性:測試用例自文檔化
? 便于擴(kuò)展:添加新功能無需修改測試代碼
3.2 強(qiáng)大的 Fixture 機(jī)制
Pytest 的 Fixture 是依賴注入的優(yōu)雅實現(xiàn),框架采用三層 Fixture 架構(gòu):
# ┌─────────────────────────────────────────┐ # │ Session 級別(整個測試會話) │ # │ - browser_type_launch_args │ # │ - browser_context_args │ # │ - login_first (全局登錄) │ # └─────────────────────────────────────────┘ # # ┌─────────────────────────────────────────┐ # │ Module 級別(每個測試模塊) │ # │ - unlogin_context (無登錄上下文) │ # │ - admin_context (管理員上下文) │ # └─────────────────────────────────────────┘ # # ┌─────────────────────────────────────────┐ # │ Function 級別(每個測試函數(shù)) │ # │ - page (頁面對象) │ # │ - unlogin_page (無登錄頁面) │ # └─────────────────────────────────────────┘
全局登錄 Fixture(Session 級別):
# cases/conftest.py
@pytest.fixture(scope="session", autouse=True)
def login_first(context, base_url, pytestconfig) -> None:
"""
有些網(wǎng)站網(wǎng)頁關(guān)閉cookie就失效了,全局登錄一次
這樣所有測試都能共享登錄狀態(tài),避免重復(fù)登錄浪費時間
"""
print("base_url----", base_url)
page = context.new_page()
LoginPage(page).navigate()
LoginPage(page).login("py", "123456")
# 等待登錄成功頁面重定向
page.wait_for_url(url='**/index.html')多賬號隔離 Fixture(Module 級別):
# cases/more_accounts/conftest.py
@pytest.fixture(scope="module")
def admin_context(browser, base_url, pytestconfig, browser_context_args):
"""
創(chuàng)建 admin 賬戶的獨立上下文
避免與默認(rèn)登錄沖突,支持多賬號測試
"""
context = browser.new_context(**browser_context_args)
page = context.new_page()
LoginPage(page).navigate()
LoginPage(page).login("admin", "123456")
page.wait_for_url(url='**/index.html')
yield context
context.close()使用示例:
class TestAddProject:
"""添加項目 - 使用全局登錄"""
@pytest.fixture(autouse=True)
def start_for_each(self, login_first, page):
self.add_project = AddProjectPage(page)
self.add_project.navigate()
yield
class TestLogin:
"""登錄測試 - 使用無登錄上下文"""
@pytest.fixture(autouse=True)
def start_for_each(self, unlogin_page):
self.login = LoginPage(unlogin_page)
self.login.navigate()
yield
3.3 網(wǎng)絡(luò)攔截與 API Mock
Playwright 的網(wǎng)絡(luò)攔截能力是其一大優(yōu)勢,可以輕松 Mock API 返回值,測試異常場景。
API Mock 配置:
# mocks/mock_api.py
mock_project_400 = (
'**/api/addProject',
lambda route: route.abort_with_error_code(400)
)
mock_project_500 = (
'**/api/addProject',
lambda route: route.abort_with_error_code(500)
)
mock_project_200 = (
'**/api/addProject',
lambda route: route.continue_()
)
測試不同 HTTP 狀態(tài)碼:
# cases/test_add_project.py
def test_add_project_400(self, page):
"""項目名稱重復(fù),返回 400 錯誤"""
self.add_project.fill_project_name("yo yo")
self.add_project.fill_publish_app("xx")
# Mock API 返回 400
page.route(**mock_api.mock_project_400)
self.add_project.click_save_button()
# 驗證 UI 正確處理錯誤
expect(self.add_project.locator_boot_box).to_be_visible()
expect(self.add_project.locator_boot_box).to_contain_text('已存在')
def test_add_project_500(self, page):
"""服務(wù)器異常,返回 500 錯誤"""
self.add_project.fill_project_name("test")
# Mock API 返回 500
page.route(**mock_api.mock_project_500)
self.add_project.click_save_button()
# 驗證 UI 提示
expect(self.add_project.locator_boot_box).to_contain_text('操作異常')
捕獲網(wǎng)絡(luò)請求和響應(yīng):
def test_login_ajax(self):
"""捕獲并驗證登錄請求"""
self.login.fill_username("py")
self.login.fill_password("123456")
# 捕獲 API 請求
with self.login.page.expect_request('**/api/login') as req:
self.login.click_login_button()
# 驗證請求內(nèi)容
assert req.value.method == 'POST'
assert req.value.header_value('content-type') == 'application/json'
assert req.value.post_data_json == {
'username': 'py',
'password': '123456'
}
def test_login_ajax_response(self):
"""捕獲并驗證登錄響應(yīng)"""
self.login.fill_username("py")
self.login.fill_password("123456")
# 捕獲 API 響應(yīng)
with self.login.page.expect_response('**/api/login') as res:
self.login.click_login_button()
# 驗證響應(yīng)
assert res.value.ok
assert res.value.status == 200
3.4 項目列表搜索功能測試
這個例子展示了如何進(jìn)行實際的業(yè)務(wù)流程測試:
# cases/test_project_list.py
class TestProjectList:
"""項目列表功能測試"""
@pytest.fixture(autouse=True)
def start_for_each(self, login_first, page):
"""每個測試前打開項目列表頁"""
self.project = ProjectListPage(page)
self.project.navigate()
yield
def test_search_project_ajax(self):
"""驗證搜索請求的參數(shù)和方法"""
self.project.search_project("test")
# 點擊搜索時捕獲請求
with self.project.page.expect_request('**/api/project**') as req:
self.project.click_search_button()
# 驗證請求參數(shù)
assert "project_name=test" in req.value.url
assert req.value.method == "GET"
def test_search_project_empty_result(self):
"""測試搜索無結(jié)果的情況"""
self.project.search_project("test")
# Mock 搜索返回空結(jié)果
self.project.page.route(**mock_api.mock_project_search_0)
self.project.click_search_button()
# 驗證提示信息
expect(self.project.locator_table_tr).to_contain_text('沒有找到匹配的記錄')
def test_search_project_single_result(self):
"""測試搜索返回單條結(jié)果"""
self.project.search_project("test")
# Mock 搜索返回 1 條結(jié)果
self.project.page.route(**mock_api.mock_project_search_1)
self.project.click_search_button()
# 驗證結(jié)果數(shù)量
expect(self.project.locator_table_tr).to_have_count(1)
def test_table_delete(self):
"""表格行內(nèi)刪除操作"""
# Mock 搜索結(jié)果
self.project.page.route(**mock_api.mock_project_search_1)
self.project.page.reload()
# 點擊刪除
self.project.locator_table_delete.click()
expect(self.project.locator_boot_box).to_contain_text('確定要刪除選中的數(shù)據(jù)?')
# Mock 刪除返回 403(無權(quán)限)
self.project.page.route(**mock_api.mock_project_delete_403)
self.project.locator_boot_box_accept.click()
# 驗證錯誤提示
expect(self.project.locator_boot_box.last).to_contain_text('無權(quán)限操作')
3.5 參數(shù)化測試
使用 @pytest.mark.parametrize 進(jìn)行數(shù)據(jù)驅(qū)動測試,避免代碼重復(fù):
# cases/test_env_list.py
class TestEnvList:
"""環(huán)境列表參數(shù)化測試示例"""
@pytest.mark.parametrize("name", ["abc!@", "$32", "\\xx"])
def test_add_env_name_invalid(self, name):
"""
參數(shù)化測試:不能有特殊字符
生成 3 個測試用例
"""
self.env.click_add_env()
self.env.input_env_name(name)
self.env.click_modal_save()
# 驗證錯誤提示
expect(self.env.locator_modal_env_tip3).to_be_visible()
expect(self.env.locator_modal_env_tip3).to_have_text('不能有特殊字符')
@pytest.mark.parametrize("address", ["abchttp", "httpx:", "httpsx://"])
def test_add_env_address_invalid(self, address):
"""
參數(shù)化測試:URL 格式驗證
生成 3 個測試用例
"""
self.env.click_add_env()
self.env.input_env_name('env')
self.env.input_env_address(address)
self.env.click_modal_save()
# 驗證錯誤提示
expect(self.env.locator_boot_box).to_be_visible()
expect(self.env.locator_boot_box).to_have_text('base_url must start with http:// or https://')
執(zhí)行結(jié)果:
$ pytest cases/test_env_list.py::TestEnvList::test_add_env_name_invalid -v test_add_env_name_invalid[abc!@] PASSED test_add_env_name_invalid[$32] PASSED test_add_env_name_invalid[\xx] PASSED
3.6 自動化報告生成
框架集成了完整的自動化報告系統(tǒng),??括截圖、視頻、鏈路追蹤:
# plugins/pytest_playwright.py 中實現(xiàn)
@pytest.fixture
def page(context, pytestconfig, request):
page = context.new_page()
yield page
# 判斷測試是否失敗
failed = request.node.rep_call.failed if hasattr(request.node, "rep_call") else True
# 根據(jù)配置自動截圖
screenshot_option = pytestconfig.getoption("--screenshot")
capture_screenshot = screenshot_option == "on" or (failed and screenshot_option == "only-on-failure")
if capture_screenshot:
for index, page in enumerate(pages):
screenshot_path = _build_artifact_test_folder(pytestconfig, request, f"test-{human_readable_status}-{index + 1}.png")
page.screenshot(timeout=5000, path=screenshot_path)
# 自動附加到 Allure 報告
allure.attach.file(screenshot_path, attachment_type=allure.attachment_type.PNG)
# 自動錄制視頻
video_option = pytestconfig.getoption("--video")
preserve_video = video_option == "on" or (failed and video_option == "retain-on-failure")
if preserve_video:
for page in pages:
video = page.video
if video:
video_path = video.path()
video.save_as(path=file_path)
allure.attach.file(file_path, attachment_type=allure.attachment_type.WEBM)
啟動報告:
# 運行測試 python run.py # 或使用命令行 pytest --alluredir ./reports allure serve ./reports
報告產(chǎn)物:
test-results/ ├── trace.zip # 完整鏈路追蹤(可在線查看) ├── test-failed-1.png # 失敗時截圖 └── test-failed.webm # 失敗時視頻
四、最佳實踐
4.1 等待策略
Playwright 的智能等待是其一大優(yōu)勢,但也需要正確使用:
# ? 優(yōu)先使用隱式等待(推薦)
page.click() # 自動等待元素可點擊
page.fill() # 自動等待元素可輸入
# ? 導(dǎo)航等待
with page.expect_navigation(url='**/list_project.html'):
self.add_project.click_save_button()
# ? 網(wǎng)絡(luò)等待
page.wait_for_load_state('networkidle') # 所有網(wǎng)絡(luò)請求完成
# ? 避免硬編碼等待
time.sleep(2) # 不要這樣做!
4.2 測試組織最佳實踐
class TestLogin:
"""登錄功能測試"""
@pytest.fixture(autouse=True)
def setup(self, unlogin_page):
"""前置條件:準(zhǔn)備測試環(huán)境"""
self.login = LoginPage(unlogin_page)
self.login.navigate()
yield
# 后置處理(可選)
# 正常流程
def test_login_success(self):
"""用戶使用正確的用戶名和密碼能成功登錄"""
self.login.login("py", "123456")
expect(self.login.page).to_have_url("/index.html")
# 異常場景1:輸入驗證
def test_login_with_empty_username(self):
"""用戶名為空時應(yīng)該顯示錯誤提示"""
self.login.fill_username('')
self.login.fill_password('123456')
self.login.click_login_button()
expect(self.login.locator_username_tip1).to_contain_text("不能為空")
# 異常場景2:錯誤憑證
def test_login_with_wrong_password(self):
"""密碼錯誤時應(yīng)該顯示錯誤提示"""
self.login.login("py", "wrongpassword")
expect(self.login.locator_login_error).to_be_visible()
# 邊界值測試
def test_login_with_long_username(self):
"""用戶名過長時應(yīng)該受限"""
self.login.fill_username('hello world hello world hello world')
expect(self.login.locator_login_btn).not_to_be_enabled()
4.3 調(diào)試技巧
# 1. 使用 --headed 模式查看瀏覽器窗口
pytest --headed cases/test_login.py
# 2. 使用 --slowmo 延遲每個操作
pytest --slowmo=1000 cases/test_login.py
# 3. 啟用詳細(xì) Trace
pytest --tracing=on cases/test_login.py
# 4. 查看 Trace
playwright show-trace test-results/trace.zip
# 5. 在代碼中暫停
def test_with_pause(page):
page.pause() # 瀏覽器會暫停,允許手動操作
五、性能優(yōu)化建議
5.1 減少重復(fù)登錄
# ? 不好的做法:每個測試都登錄
@pytest.fixture(autouse=True)
def setup(self, page):
LoginPage(page).login("py", "123456") # 浪費時間
# ? 好的做法:全局登錄一次
@pytest.fixture(scope="session", autouse=True)
def login_first(context):
page = context.new_page()
LoginPage(page).login("py", "123456")
# 所有測試共享 Cookie
5.2 使用 Mock API
# ? 真實 API 調(diào)用(慢)
self.add_project.click_save() # 等待數(shù)據(jù)庫操作
# ? Mock API(快)
page.route('**/api/addProject', lambda route: route.continue_())
5.3 并行執(zhí)行
# 安裝 pytest-xdist pip install pytest-xdist # 4 個進(jìn)程并行執(zhí)行 pytest -n 4 # 自動檢測 CPU 核數(shù) pytest -n auto
六、完整示例:添加項目功能測試
最后,讓我展示一個完整的功能測試示例:
# cases/test_add_project.py
from pages.add_project_page import AddProjectPage
from playwright.sync_api import expect, Page
import pytest
import uuid
from mocks import mock_api
class TestAddProject:
"""添加項目功能測試"""
@pytest.fixture(autouse=True)
def start_for_each(self, login_first, page: Page):
"""前置條件:打開添加項目頁"""
print("for each--start: 打開添加項目頁")
self.add_project = AddProjectPage(page)
self.add_project.navigate()
yield
print("for each--end: 后置操作")
# 異常場景:輸入驗證
@pytest.mark.parametrize("name, app, desc", [
["abc!@", "", ""], # 特殊字符
["aaaaabbbbbcccccdddddeeeeefffff1", "", ""], # 超過 30 字符
["abc", "aa!@", ""] # app 特殊字符
])
def test_add_project_disabled(self, name, app, desc):
"""異常場景-項目名稱無效:特殊字符/大于30個字符"""
self.add_project.fill_project_name(name)
self.add_project.fill_publish_app(app)
self.add_project.fill_project_desc(desc)
# 斷言提交按鈕不可點擊
expect(self.add_project.locator_save_button).to_be_disabled()
# 異常場景:空值驗證
def test_add_project_null(self):
"""異常場景-項目名稱不能為空"""
self.add_project.fill_project_name("")
# 斷言提交按鈕不可點擊
expect(self.add_project.locator_save_button).to_be_disabled()
# 異常場景:服務(wù)器返回 400(項目已存在)
def test_add_project_400(self, page: Page):
"""項目名稱重復(fù),服務(wù)器返回 400 錯誤"""
self.add_project.fill_project_name("yo yo")
self.add_project.fill_publish_app("xx")
self.add_project.fill_project_desc("xxx")
# Mock API 返回 400
page.route(**mock_api.mock_project_400)
self.add_project.click_save_button()
# 驗證錯誤提示
expect(self.add_project.locator_boot_box).to_be_visible()
expect(self.add_project.locator_boot_box).to_contain_text('已存在')
# 異常場景:服務(wù)器返回 500(服務(wù)器異常)
def test_add_project_500(self, page: Page):
"""服務(wù)器異常,返回 500 狀態(tài)碼"""
self.add_project.fill_project_name("test")
self.add_project.fill_publish_app("xx")
self.add_project.fill_project_desc("xxx")
# Mock API 返回 500
page.route(**mock_api.mock_project_500)
self.add_project.click_save_button()
# 驗證錯誤提示
expect(self.add_project.locator_boot_box).to_contain_text('操作異常')
# 正常流程:成功添加項目
def test_add_project_success(self, page: Page):
"""提交成功,跳轉(zhuǎn)到項目列表"""
# 生成隨機(jī)項目名
project_name = str(uuid.uuid4()).replace('-', '')[:25]
self.add_project.fill_project_name(project_name)
self.add_project.fill_publish_app("xx")
self.add_project.fill_project_desc("xxx")
# 驗證頁面重定向到列表頁
with page.expect_navigation(url="**/list_project.html"):
self.add_project.click_save_button()
# 完整流程:驗證新項目在列表中
def test_add_project_success_verify_in_list(self, page: Page):
"""添加項目成功后,驗證項目??現(xiàn)在列表中"""
# 生成隨機(jī)項目???
new_project_name = str(uuid.uuid4()).replace('-', '')[:25]
self.add_project.fill_project_name(new_project_name)
self.add_project.fill_publish_app("xx")
self.add_project.fill_project_desc("xxx")
self.add_project.click_save_button()
# 等待頁面重定向和網(wǎng)絡(luò)請求完成
self.add_project.page.wait_for_load_state('networkidle')
# 獲取表格中所有項目名稱
loc_projects = self.add_project.page.locator('//table[@id="table"]//td[3]/a')
project_names = [i.inner_text() for i in loc_projects.all()]
print(f"新增項目名稱: {new_project_name}")
print(f"列表中的項目: {project_names}")
# 驗證新項目在列表中
assert new_project_name in project_names, f"項目 {new_project_name} 未在列表中"
七、總結(jié)與啟示
通過這個企業(yè)級的 Playwright UI 自動化測試框架,我們學(xué)到了:
核心設(shè)計原則:
- 分層架構(gòu):清晰的分層使代碼更易維護(hù)和擴(kuò)展
- Page Object Model:將頁面元素和業(yè)務(wù)邏輯分離,提高復(fù)用性
- Fixture 機(jī)制:優(yōu)雅的依賴注入,減少代碼重復(fù)
- 網(wǎng)絡(luò)攔截:Mock API 能力,便于測試異常場景
- 自動化報告:完整的測試產(chǎn)物,快速定位問題
最佳實踐:
- ? 優(yōu)先使用 Playwright 的隱式等待
- ? 全局登錄一次,避免重復(fù)
- ? 使用參數(shù)化進(jìn)行數(shù)據(jù)驅(qū)動測試
- ? 完整的異常場景覆蓋
- ? 充分利用 Mock API 提升速度
關(guān)鍵優(yōu)勢:
Playwright 相比 Selenium:
- 更快的執(zhí)行速度(優(yōu)化程度更高)
- 更好的等待機(jī)制(自動同步)
- 原生網(wǎng)絡(luò)攔截能力(Mock API)
- 完整的鏈路追蹤能力(快速定位問題)
參考資源
到此這篇關(guān)于使用Playwright+Pytest構(gòu)建Web UI自動化測試框架完整示例的文章就介紹到這了,更多相關(guān)Playwright+Pytest自動化測試框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python利用PIL庫實現(xiàn)圖片轉(zhuǎn)Base64字符串的完整指南
這篇文章主要為大家詳細(xì)介紹了如何使用Python的PIL(Pillow)庫將圖像轉(zhuǎn)換為Base64編碼字符串,并提供多種實用場景的示例代碼,有需要的小伙伴可以了解下2026-01-01
對python實現(xiàn)二維函數(shù)高次擬合的示例詳解
今天小編就為大家分享一篇對python實現(xiàn)二維函數(shù)高次擬合的示例詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12
通過Python模塊filecmp 對文件比較的實現(xiàn)方法
這篇文章主要介紹了通過Python模塊filecmp 對文件比較的實現(xiàn)方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06
OpenCV模板匹配matchTemplate的實現(xiàn)
這篇文章主要介紹了OpenCV模板匹配matchTemplate的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
利用Python產(chǎn)生加密表和解密表的實現(xiàn)方法
這篇文章主要介紹了利用Python產(chǎn)生加密表和解密表的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
Python創(chuàng)建Excel的4種方式小結(jié)
這篇文章主要為大家詳細(xì)介紹了Python中創(chuàng)建Excel的4種常見方式,文中的示例代碼簡潔易懂,具有一定的參考價值,感興趣的小伙伴可以學(xué)習(xí)一下2025-02-02

