使用Python實(shí)現(xiàn)Flutter項(xiàng)目的自動(dòng)化構(gòu)建與發(fā)布
前言
作為公司唯一的移動(dòng)端開發(fā),我需要同時(shí)負(fù)責(zé) 5 個(gè) Flutter App 的開發(fā)和維護(hù)工作。每個(gè)應(yīng)用都需要支持 iOS 和 Android 雙平臺(tái),這意味著每次發(fā)版我可能要進(jìn)行多達(dá) 10 次的打包操作。如果每次都手動(dòng)執(zhí)行構(gòu)建、上傳、通知這些重復(fù)性工作,不僅耗時(shí)巨大,還極易出錯(cuò)。
為了從繁瑣的重復(fù)勞動(dòng)中解放出來,把更多精力投入到真正有價(jià)值的開發(fā)工作中,我使用 Python 編寫了一套自動(dòng)化腳本,實(shí)現(xiàn)了一鍵完成構(gòu)建、上傳和通知的完整流程。
本文將分享我在這個(gè)過程中的實(shí)踐經(jīng)驗(yàn)和代碼實(shí)現(xiàn)。
項(xiàng)目背景
公司目前有 5 個(gè)移動(dòng)端應(yīng)用在同時(shí)運(yùn)營,而移動(dòng)端開發(fā)只有我一個(gè)人。每個(gè)應(yīng)用都是基于 Flutter 開發(fā)的跨平臺(tái)應(yīng)用,需要同時(shí)支持 iOS 和 Android 平臺(tái)。在日常開發(fā)中,存在以下痛點(diǎn):
- 項(xiàng)目多、人手少:5 個(gè) App × 2 個(gè)平臺(tái) = 10 個(gè)構(gòu)建任務(wù),一個(gè)人根本忙不過來
- 構(gòu)建流程繁瑣:每次打包都需要手動(dòng)執(zhí)行多個(gè)命令,切換項(xiàng)目、切換環(huán)境配置
- 上傳步驟重復(fù):構(gòu)建完成后需要手動(dòng)上傳到測(cè)試平臺(tái)(蒲公英),操作機(jī)械且耗時(shí)
- 通知不及時(shí):需要手動(dòng)通知測(cè)試人員新版本已就緒,容易遺漏
- iOS 構(gòu)建環(huán)境問題:CocoaPods 緩存問題經(jīng)常導(dǎo)致構(gòu)建失敗,排查費(fèi)時(shí)費(fèi)力
面對(duì)這樣的工作強(qiáng)度,自動(dòng)化不再是"錦上添花",而是"剛需"。
技術(shù)方案
我設(shè)計(jì)了以下幾個(gè) Python 腳本來解決這些問題:
python/ ├── build_app.py # 主構(gòu)建腳本(iOS + Android) ├── build_android_app.py # Android 單獨(dú)構(gòu)建腳本 ├── clean_ios_build.py # iOS 構(gòu)建環(huán)境清理 ├── force_clean_ios.py # 強(qiáng)制清理腳本 ├── bulk_email.py # 群發(fā)郵件工具類 ├── send_email.py # 單封郵件發(fā)送 └── test_email_auth.py # 郵箱授權(quán)測(cè)試
核心實(shí)現(xiàn)
1. 自動(dòng)構(gòu)建腳本
構(gòu)建腳本的核心功能是自動(dòng)執(zhí)行 Flutter 構(gòu)建命令,并支持不同環(huán)境(開發(fā)/生產(chǎn))的配置。
#!/usr/local/bin/python3
import os
import subprocess
# 獲取當(dāng)前腳本所在目錄
script_dir = os.path.dirname(os.path.abspath(__file__))
# Flutter項(xiàng)目根目錄(python文件夾在項(xiàng)目根目錄下)
flutter_root = os.path.dirname(script_dir)
# 獲取用戶輸入的環(huán)境
env = input("請(qǐng)輸入環(huán)境(dev/prod): ")
# 檢查環(huán)境配置文件是否存在
env_file = os.path.join(flutter_root, f"{env}.json")
if not os.path.exists(env_file):
print(f"錯(cuò)誤: 環(huán)境配置文件 {env}.json 不存在")
exit(1)
# 切換到Flutter項(xiàng)目根目錄
os.chdir(flutter_root)
# 構(gòu)建Android應(yīng)用
def build_android(env):
print("正在構(gòu)建Android應(yīng)用...")
env_text = '生產(chǎn)' if env == 'prod' else '開發(fā)'
print(f"構(gòu)建版本: {env_text}環(huán)境...")
# 構(gòu)建命令,支持代碼混淆
build_command = f'fvm flutter build apk --release --dart-define-from-file={env}.json --obfuscate --split-debug-info=./build/debug_info'
try:
process = subprocess.run(
build_command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print("構(gòu)建輸出:")
print(process.stdout)
print("Android構(gòu)建成功!")
print("APK文件路徑: build/app/outputs/flutter-apk/app-release.apk")
return True
except subprocess.CalledProcessError as e:
print(f"Android構(gòu)建失敗: {e}")
return False
# 構(gòu)建iOS應(yīng)用
def build_ios(env, upload_to_appstore=False):
print("正在構(gòu)建iOS應(yīng)用...")
env_text = '生產(chǎn)' if env == 'prod' else '開發(fā)'
# 根據(jù)是否上傳App Store選擇導(dǎo)出方法
export_method = "app-store" if upload_to_appstore else "development"
build_command = f"fvm flutter build ipa --release --export-method {export_method} --dart-define-from-file={env}.json --obfuscate --split-debug-info=./build/debug_info"
try:
process = subprocess.run(
build_command.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print("構(gòu)建輸出:")
print(process.stdout)
print("iOS構(gòu)建成功!")
return True
except subprocess.CalledProcessError as e:
print(f"iOS構(gòu)建失敗: {e}")
return False
2. 自動(dòng)上傳到蒲公英
構(gòu)建完成后,自動(dòng)將安裝包上傳到蒲公英測(cè)試平臺(tái):
import requests
def upload_to_pgyer(env, ipa_path, platform):
"""上傳到蒲公英測(cè)試平臺(tái)"""
print(f"正在上傳到蒲公英...")
print(f"文件路徑: {ipa_path}")
# 從配置文件或環(huán)境變量讀取 API Key
api_key = os.environ.get('PGYER_API_KEY', 'your_api_key')
user_key = os.environ.get('PGYER_USER_KEY', 'your_user_key')
files = {"file": open(ipa_path, "rb")}
headers = {"enctype": "multipart/form-data"}
platform_text = "android" if platform == "android" else "ios"
payload = {
"uKey": user_key,
"_api_key": api_key,
"installType": 1,
"updateDescription": f"{platform_text}自動(dòng)化打包"
}
try:
response = requests.post(
"https://www.pgyer.com/apiv2/app/upload",
data=payload,
files=files,
headers=headers
)
result = response.json()
# 獲取構(gòu)建信息
qr_code_url = result["data"]["buildQRCodeURL"]
version = result["data"]["buildVersion"]
version_no = result["data"]["buildVersionNo"]
build_name = result["data"]["buildName"]
print(f"上傳成功!")
print(f"二維碼地址: {qr_code_url}")
print(f"版本: {version} ({version_no})")
return {
"qr_code_url": qr_code_url,
"version": version,
"version_no": version_no,
"build_name": build_name
}
except Exception as e:
print(f"上傳失敗: {e}")
return None
3. 群發(fā)郵件通知
構(gòu)建并上傳成功后,自動(dòng)發(fā)送郵件通知團(tuán)隊(duì)成員:
#!/usr/local/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os
import time
from typing import List, Dict, Optional
class BulkEmailSender:
"""群發(fā)郵件發(fā)送器"""
def __init__(self, smtp_server: str, smtp_port: int,
sender_email: str, sender_password: str):
"""
初始化群發(fā)郵件發(fā)送器
Args:
smtp_server: SMTP服務(wù)器地址
smtp_port: SMTP端口
sender_email: 發(fā)送者郵箱
sender_password: 發(fā)送者郵箱密碼或授權(quán)碼
"""
self.smtp_server = smtp_server
self.smtp_port = smtp_port
self.sender_email = sender_email
self.sender_password = sender_password
def _create_connection(self):
"""創(chuàng)建SMTP連接"""
try:
if self.smtp_port == 465:
# 使用SSL連接(465端口)
server = smtplib.SMTP_SSL(self.smtp_server, self.smtp_port)
else:
# 使用STARTTLS連接(587/25端口)
server = smtplib.SMTP(self.smtp_server, self.smtp_port)
server.starttls()
server.login(self.sender_email, self.sender_password)
return server
except Exception as e:
print(f"連接失敗: {e}")
return None
def send_bulk_individual(self, recipients: List[str], subject: str,
body: str, html_body: Optional[str] = None,
attachment_path: Optional[str] = None,
delay: float = 1.0) -> Dict[str, bool]:
"""
逐個(gè)發(fā)送郵件(隱私保護(hù)最好,每個(gè)人只能看到自己的郵箱)
Args:
recipients: 收件人列表
subject: 郵件主題
body: 郵件內(nèi)容(純文本)
html_body: HTML郵件內(nèi)容(可選)
attachment_path: 附件路徑(可選)
delay: 發(fā)送間隔(秒),避免被服務(wù)器限制
"""
results = {}
server = self._create_connection()
if not server:
return {email: False for email in recipients}
try:
for i, recipient in enumerate(recipients):
try:
print(f"發(fā)送郵件 {i+1}/{len(recipients)} 到: {recipient}")
# 創(chuàng)建郵件
if html_body:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(body, 'plain', 'utf-8'))
msg.attach(MIMEText(html_body, 'html', 'utf-8'))
else:
msg = MIMEMultipart()
msg.attach(MIMEText(body, 'plain', 'utf-8'))
msg['From'] = self.sender_email
msg['To'] = recipient
msg['Subject'] = subject
# 添加附件
if attachment_path and os.path.exists(attachment_path):
self._add_attachment(msg, attachment_path)
# 發(fā)送郵件
server.sendmail(self.sender_email, [recipient], msg.as_string())
results[recipient] = True
print(f"? 發(fā)送成功: {recipient}")
# 延遲避免被限制
if i < len(recipients) - 1:
time.sleep(delay)
except Exception as e:
results[recipient] = False
print(f"? 發(fā)送失敗 {recipient}: {e}")
finally:
server.quit()
return results
def send_bulk_bcc(self, recipients: List[str], subject: str, body: str,
html_body: Optional[str] = None, batch_size: int = 50) -> bool:
"""
使用BCC批量發(fā)送(隱私保護(hù),收件人看不到其他人)
適合大批量發(fā)送通知郵件
"""
server = self._create_connection()
if not server:
return False
try:
# 分批發(fā)送,避免單次發(fā)送太多
for i in range(0, len(recipients), batch_size):
batch = recipients[i:i + batch_size]
print(f"發(fā)送批次 {i//batch_size + 1}: {len(batch)} 個(gè)收件人")
if html_body:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(body, 'plain', 'utf-8'))
msg.attach(MIMEText(html_body, 'html', 'utf-8'))
else:
msg = MIMEMultipart()
msg.attach(MIMEText(body, 'plain', 'utf-8'))
msg['From'] = self.sender_email
msg['To'] = self.sender_email # 顯示發(fā)送者自己
msg['Bcc'] = ', '.join(batch) # 密送給所有收件人
msg['Subject'] = subject
all_recipients = [self.sender_email] + batch
server.sendmail(self.sender_email, all_recipients, msg.as_string())
print(f"? 批次發(fā)送成功: {len(batch)} 個(gè)收件人")
if i + batch_size < len(recipients):
time.sleep(2)
return True
except Exception as e:
print(f"? BCC群發(fā)失敗: {e}")
return False
finally:
server.quit()
def _add_attachment(self, msg: MIMEMultipart, attachment_path: str):
"""添加附件到郵件"""
with open(attachment_path, "rb") as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename= {os.path.basename(attachment_path)}'
)
msg.attach(part)
4. 發(fā)送構(gòu)建通知郵件
將構(gòu)建信息通過 HTML 郵件發(fā)送給團(tuán)隊(duì):
def send_build_notification(build_info, env, platform, test_content=""):
"""發(fā)送構(gòu)建通知郵件"""
env_text = '生產(chǎn)' if env == 'prod' else '開發(fā)'
platform_text = "Android" if platform == "android" else "iOS"
# 構(gòu)建HTML郵件內(nèi)容
html_body = f"""
<html>
<body>
<h2>項(xiàng)目構(gòu)建通知</h2>
<p>構(gòu)建狀態(tài): <span style="color: green;"><b>成功</b></span></p>
<ul>
<li>構(gòu)建名稱: {build_info['build_name']}</li>
<li>平臺(tái): {platform_text}</li>
<li>環(huán)境: {env_text}</li>
<li>版本: {build_info['version']}</li>
<li>版本號(hào): {build_info['version_no']}</li>
<li>測(cè)試內(nèi)容: {test_content}</li>
</ul>
<img src="{build_info['qr_code_url']}" alt="下載二維碼">
<p>請(qǐng)掃描二維碼下載安裝測(cè)試。</p>
</body>
</html>
"""
# 從環(huán)境變量讀取郵件配置
smtp_server = os.environ.get('SMTP_SERVER', 'smtp.exmail.qq.com')
smtp_port = int(os.environ.get('SMTP_PORT', '587'))
sender_email = os.environ.get('SENDER_EMAIL')
sender_password = os.environ.get('SENDER_PASSWORD')
bulk_sender = BulkEmailSender(
smtp_server=smtp_server,
smtp_port=smtp_port,
sender_email=sender_email,
sender_password=sender_password
)
# 收件人列表(從配置文件讀?。?
recipients = load_recipients_from_config()
subject = f"構(gòu)建通知: {build_info['build_name']} - {platform_text} - {env_text}環(huán)境"
results = bulk_sender.send_bulk_individual(
recipients=recipients,
subject=subject,
body=f'{platform_text}打包通知',
html_body=html_body,
delay=0.5
)
return results
5. iOS 構(gòu)建環(huán)境清理腳本
在 iOS 開發(fā)中,經(jīng)常會(huì)遇到 CocoaPods 緩存導(dǎo)致的構(gòu)建問題。這個(gè)腳本可以徹底清理構(gòu)建環(huán)境:
#!/usr/bin/env python3
"""
iOS 構(gòu)建環(huán)境清理腳本
用于解決 Firebase Crashlytics 模塊化頭文件等常見問題
"""
import os
import subprocess
import shutil
def run_command(command, description):
"""執(zhí)行命令并打印結(jié)果"""
print(f"\n{description}...")
print(f"執(zhí)行命令: {command}")
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True)
if result.stdout:
print("輸出:", result.stdout)
if result.returncode == 0:
print(f"? {description} 成功")
else:
print(f"? {description} 失敗")
return result.returncode == 0
except Exception as e:
print(f"? {description} 異常: {e}")
return False
def force_clean_ios():
"""強(qiáng)制清理 iOS 構(gòu)建環(huán)境"""
print("?? 開始強(qiáng)制清理 iOS 構(gòu)建環(huán)境...")
# 獲取項(xiàng)目根目錄
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ios_dir = os.path.join(project_root, "ios")
if not os.path.exists(ios_dir):
print(f"? iOS 目錄不存在: {ios_dir}")
return False
os.chdir(project_root)
print(f"?? 當(dāng)前工作目錄: {os.getcwd()}")
# 1. 清理 Flutter 緩存
run_command("fvm flutter clean", "清理 Flutter 構(gòu)建緩存")
# 2. 刪除 pubspec.lock
pubspec_lock = os.path.join(project_root, "pubspec.lock")
if os.path.exists(pubspec_lock):
print(f"??? 刪除 pubspec.lock")
os.remove(pubspec_lock)
# 3. 刪除 .dart_tool 目錄
dart_tool_dir = os.path.join(project_root, ".dart_tool")
if os.path.exists(dart_tool_dir):
print(f"??? 刪除 .dart_tool 目錄")
shutil.rmtree(dart_tool_dir)
# 4. 刪除 iOS 構(gòu)建目錄
for dir_name in ["build", "Pods", ".symlinks"]:
dir_path = os.path.join(ios_dir, dir_name)
if os.path.exists(dir_path):
print(f"??? 刪除 {dir_name} 目錄")
shutil.rmtree(dir_path)
# 5. 刪除 Podfile.lock
podfile_lock = os.path.join(ios_dir, "Podfile.lock")
if os.path.exists(podfile_lock):
print(f"??? 刪除 Podfile.lock")
os.remove(podfile_lock)
# 6. 清理 CocoaPods 緩存
run_command("pod cache clean --all", "清理 CocoaPods 緩存")
# 7. 重新獲取 Flutter 依賴
run_command("fvm flutter pub get", "重新獲取 Flutter 依賴")
# 8. 重新安裝 Pods
os.chdir(ios_dir)
run_command("pod install --repo-update", "重新安裝 Pods")
print("\n?? 強(qiáng)制清理完成!")
print("?? 現(xiàn)在可以重新嘗試構(gòu)建 iOS 應(yīng)用了")
return True
if __name__ == "__main__":
force_clean_ios()
6. 郵箱授權(quán)測(cè)試工具
在配置郵件服務(wù)前,可以使用這個(gè)工具測(cè)試授權(quán)碼是否正確:
#!/usr/local/bin/python3
import smtplib
def test_email_auth(smtp_server, smtp_port, email, auth_code):
"""
測(cè)試郵箱授權(quán)碼是否正確
"""
try:
print(f"正在測(cè)試郵箱: {email}")
print(f"SMTP服務(wù)器: {smtp_server}:{smtp_port}")
# 連接SMTP服務(wù)器
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 嘗試登錄
server.login(email, auth_code)
server.quit()
print("? 授權(quán)碼驗(yàn)證成功!")
return True
except smtplib.SMTPAuthenticationError:
print("? 授權(quán)碼驗(yàn)證失?。≌?qǐng)檢查:")
print(" 1. 授權(quán)碼是否正確")
print(" 2. 是否已開啟SMTP服務(wù)")
print(" 3. 是否使用了郵箱密碼而非授權(quán)碼")
return False
except Exception as e:
print(f"? 連接失敗: {e}")
return False
if __name__ == "__main__":
# 常用郵箱SMTP配置
email_configs = {
'qq': ('smtp.qq.com', 587),
'163': ('smtp.163.com', 25),
'gmail': ('smtp.gmail.com', 587),
'outlook': ('smtp-mail.outlook.com', 587),
'wechat': ('smtp.exmail.qq.com', 587)
}
print("=== 郵箱授權(quán)碼測(cè)試工具 ===\n")
email_type = input("請(qǐng)選擇郵箱類型 (qq/163/gmail/outlook/wechat): ").lower()
if email_type not in email_configs:
print("不支持的郵箱類型")
exit(1)
email = input("請(qǐng)輸入郵箱地址: ")
auth_code = input("請(qǐng)輸入授權(quán)碼: ")
smtp_server, smtp_port = email_configs[email_type]
test_email_auth(smtp_server, smtp_port, email, auth_code)
使用方式
1. 環(huán)境準(zhǔn)備
首先確保安裝了必要的 Python 依賴:
pip install requests
2. 配置敏感信息
建議使用環(huán)境變量或配置文件管理敏感信息,不要硬編碼在腳本中:
# 設(shè)置環(huán)境變量 export PGYER_API_KEY="your_api_key" export PGYER_USER_KEY="your_user_key" export SENDER_EMAIL="your_email@example.com" export SENDER_PASSWORD="your_auth_code" export SMTP_SERVER="smtp.exmail.qq.com" export SMTP_PORT="587"
3. 執(zhí)行構(gòu)建
# 進(jìn)入 python 腳本目錄 cd python # 執(zhí)行構(gòu)建腳本 python3 build_app.py
腳本會(huì)依次提示:
- 選擇環(huán)境(dev/prod)
- 是否發(fā)送郵件通知
- 輸入測(cè)試內(nèi)容
- 是否上傳到 App Store(僅生產(chǎn)環(huán)境)
4. 清理 iOS 構(gòu)建環(huán)境
當(dāng)遇到 iOS 構(gòu)建問題時(shí),執(zhí)行:
python3 force_clean_ios.py
最佳實(shí)踐
1. 敏感信息管理
- 使用環(huán)境變量存儲(chǔ) API Key、密碼等敏感信息
- 不要將敏感信息提交到版本控制
- 可以使用
.env文件配合python-dotenv庫
2. 錯(cuò)誤處理
- 每個(gè)關(guān)鍵步驟都添加 try-except 處理
- 構(gòu)建失敗時(shí)輸出詳細(xì)錯(cuò)誤信息
- 記錄日志便于問題排查
3. 郵件發(fā)送策略
- 群發(fā)郵件時(shí)添加適當(dāng)延遲,避免被服務(wù)器限制
- 使用 BCC 方式保護(hù)收件人隱私
- 分批發(fā)送大量郵件
4. 構(gòu)建優(yōu)化
- 使用
--obfuscate參數(shù)進(jìn)行代碼混淆 - 使用
--split-debug-info分離調(diào)試信息 - 根據(jù)環(huán)境使用不同的配置文件
總結(jié)
通過 Python 腳本實(shí)現(xiàn) Flutter 項(xiàng)目的自動(dòng)化構(gòu)建,可以顯著提高開發(fā)效率:
- 一鍵完成:構(gòu)建、上傳、通知全流程自動(dòng)化
- 減少出錯(cuò):避免手動(dòng)操作帶來的失誤
- 節(jié)省時(shí)間:構(gòu)建期間可以專注于其他工作
- 規(guī)范流程:統(tǒng)一的構(gòu)建和發(fā)布流程
這套腳本已經(jīng)在我使用了一段時(shí)間,效果良好。希望這篇文章對(duì)有類似需求的開發(fā)者有所幫助。
相關(guān)技術(shù)棧:
- Python 3.x
- Flutter + FVM
- 蒲公英測(cè)試平臺(tái)
- SMTP 郵件服務(wù)
以上就是使用Python實(shí)現(xiàn)Flutter項(xiàng)目的自動(dòng)化構(gòu)建與發(fā)布的詳細(xì)內(nèi)容,更多關(guān)于Python Flutter自動(dòng)化構(gòu)建與發(fā)布的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
PyQt5學(xué)習(xí)之QThread類的使用詳解
QThread是Qt線程類中最核心的底層類。要使用QThrea開始一個(gè)線程,可以創(chuàng)建它的一個(gè)子類,然后覆蓋其QThread.run()函數(shù)。這篇文章就來和大家聊聊QThread類的使用,感興趣的可以學(xué)習(xí)一下2022-12-12
Python使用BeautifulSoup進(jìn)行頁面解析
在Python中,我們可以使用BeautifulSoup庫來解析網(wǎng)頁,BeautifulSoup提供了簡(jiǎn)單而強(qiáng)大的API,使得解析網(wǎng)頁變得輕松而高效,下面小編就來為大家詳細(xì)講講BeautifulSoup解析網(wǎng)頁的具體操作吧2023-09-09
Python Flask 和 Django 的區(qū)別與適用場(chǎng)景示例分析
Flask和Django是兩個(gè)流行的Python Web框架,但設(shè)計(jì)哲學(xué)、功能和用法有很大區(qū)別,Flask是一個(gè)輕量級(jí)框架,簡(jiǎn)單靈活,適合小型項(xiàng)目和快速原型開發(fā),本文給大家介紹Python Flask 和 Django 的區(qū)別與適用場(chǎng)景示例分析,感興趣的朋友跟隨小編一起看看吧2024-10-10
Python解析網(wǎng)頁源代碼中的115網(wǎng)盤鏈接實(shí)例
這篇文章主要介紹了Python解析網(wǎng)頁源代碼中的115網(wǎng)盤鏈接實(shí)例,主要采用了正則表達(dá)式re模塊來實(shí)現(xiàn)該功能,需要的朋友可以參考下2014-09-09
一篇文章帶你了解Python之Selenium自動(dòng)化爬蟲
這篇文章主要為大家詳細(xì)介紹了Python之Selenium自動(dòng)化爬蟲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
Python使用progressbar模塊實(shí)現(xiàn)的顯示進(jìn)度條功能
這篇文章主要介紹了Python使用progressbar模塊實(shí)現(xiàn)的顯示進(jìn)度條功能,簡(jiǎn)單介紹了progressbar模塊的安裝,并結(jié)合實(shí)例形式分析了Python使用progressbar模塊顯示進(jìn)度條的相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
Python腳本提取fasta文件單序列信息實(shí)現(xiàn)
這篇文章主要為大家介紹了Python腳本提取fasta文件單序列信息實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
python爬蟲beautifulsoup庫使用操作教程全解(python爬蟲基礎(chǔ)入門)
這篇文章主要介紹了python爬蟲beautifulsoup庫使用操作全解(python爬蟲基礎(chǔ)入門),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02

