偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

還在手動(dòng)復(fù)制粘貼到崩潰?這 20 個(gè) Python 代碼讓你提前下班

開發(fā)
本文通過 20個(gè)貼近實(shí)際工作場(chǎng)景的 Python 案例,系統(tǒng)性地展示了其在 文件與目錄管理、數(shù)據(jù)處理與分析、自動(dòng)化與交互 以及 數(shù)據(jù)可視化 等方面的強(qiáng)大能力和廣泛應(yīng)用。

本文旨在通過20個(gè)貼近日常工作需求的Python代碼案例,系統(tǒng)性地展示其在文件處理、數(shù)據(jù)分析、自動(dòng)化交互及可視化等方面的核心應(yīng)用。助大家快速上手并應(yīng)用于實(shí)際工作場(chǎng)景。

一、準(zhǔn)備工作與基礎(chǔ)

在開始探索具體案例之前,請(qǐng)確保已完成必要的環(huán)境配置。

1. 環(huán)境要求與庫安裝

Python 環(huán)境: 確保已安裝 Python 3.x 版本。

第三方庫: 本文部分案例依賴以下庫,請(qǐng)通過 pip 安裝:

pip install requests pandas openpyxl matplotlib
  • requests: 用于執(zhí)行 HTTP 網(wǎng)絡(luò)請(qǐng)求,是 API 交互和網(wǎng)頁數(shù)據(jù)獲取的基礎(chǔ)。
  • pandas: 提供高性能、易用的數(shù)據(jù)結(jié)構(gòu)(如 DataFrame)和數(shù)據(jù)分析工具。
  • openpyxl: pandas 讀寫 .xlsx 格式 Excel 文件所需的引擎。
  • matplotlib: 強(qiáng)大的數(shù)據(jù)可視化庫,用于繪制各種靜態(tài)、動(dòng)態(tài)、交互式的圖表。

二、文件與目錄管理

高效管理和操作各種格式的文件及目錄結(jié)構(gòu)是自動(dòng)化的基石。

1. 文本文件

案例 1: 讀取文本文件

場(chǎng)景: 讀取程序配置文件、日志記錄、純文本報(bào)告等。

示例數(shù)據(jù) (config.ini):

[Server]
Address=192.168.1.100
Port=8080
[User]
Username=admin

代碼:

import os

file_path = 'config.ini' # 定義文件路徑

# 檢查文件是否存在
if not os.path.exists(file_path):
    print(f"錯(cuò)誤: 文件 '{file_path}' 未找到。")
else:
    try:
        # 使用 'with open' 確保文件資源被正確釋放
        # 'r' 表示讀取模式, 'encoding='utf-8'' 支持中文等非ASCII字符
        with open(file_path, 'r', encoding='utf-8') as f:
            print(f"--- 文件 '{file_path}' 內(nèi)容 ---")
            # 逐行讀取并打印,使用 strip() 去除行尾的換行符和空白
            for line in f:
                print(line.strip())
            print("--- 讀取完成 ---")

            # # 或者一次性讀取所有行到列表
            # f.seek(0) # 指針移回文件開頭
            # lines = f.readlines()
            # print("\n讀取為列表:", [l.strip() for l in lines])

            # # 或者一次性讀取整個(gè)文件內(nèi)容為字符串
            # f.seek(0)
            # content = f.read()
            # print("\n讀取為字符串:\n", content)

    except IOError as e:
        print(f"讀取文件時(shí)發(fā)生 IO 錯(cuò)誤: {e}")
    except Exception as e:
        print(f"讀取文件時(shí)發(fā)生未知錯(cuò)誤: {e}")

圖片

注釋: with open() 是推薦的文件操作方式,自動(dòng)處理關(guān)閉。encoding='utf-8' 是處理多語言文本的標(biāo)準(zhǔn)實(shí)踐。strip() 方法對(duì)于清理每行數(shù)據(jù)非常有用。增加了文件存在性檢查和更具體的 IOError 捕獲。

案例 2: 寫入文本文件

場(chǎng)景: 保存程序輸出、生成文本格式的報(bào)告、創(chuàng)建新的配置文件。

output_file_path = 'system_report.log' # 定義輸出文件路徑
report_content = [
    "[INFO] 系統(tǒng)狀態(tài)報(bào)告",
    f"報(bào)告生成時(shí)間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", # 使用案例15的時(shí)間功能
    "CPU 使用率: 15%",
    "內(nèi)存占用: 2.5 GB / 8.0 GB",
    "[ERROR] 服務(wù) 'nginx' 狀態(tài)異常,請(qǐng)檢查。",
]

try:
    # 'w' 表示寫入模式 (覆蓋寫入); 若要追加內(nèi)容,使用 'a' (append)模式
    with open(output_file_path, 'w', encoding='utf-8') as f:
        # 遍歷內(nèi)容列表
        for line in report_content:
            # write() 方法寫入字符串,需手動(dòng)添加換行符 '\n'
            f.write(line + '\n')
    print(f"報(bào)告已成功寫入文件: '{output_file_path}'")

except IOError as e:
    print(f"寫入文件時(shí)發(fā)生 IO 錯(cuò)誤: {e}")
except Exception as e:
    print(f"寫入文件時(shí)發(fā)生未知錯(cuò)誤: {e}")

# (確保已導(dǎo)入: from datetime import datetime)

注釋: 寫入模式 'w' 會(huì)清空原文件內(nèi)容。需要追加時(shí)用 'a'。write() 不會(huì)自動(dòng)換行,必須顯式添加 \n。結(jié)合了日期時(shí)間模塊(需導(dǎo)入datetime)生成動(dòng)態(tài)內(nèi)容。

2. CSV 文件

案例 3: 讀取 CSV 文件 (Pandas)

場(chǎng)景: 處理由逗號(hào)(或其他分隔符)分隔的表格數(shù)據(jù),常用于數(shù)據(jù)導(dǎo)入。

示例數(shù)據(jù) (employees.csv):

ID,Name,Department,Salary,HireDate
101,Alice Smith,HR,65000,2022-08-15
102,Bob Johnson,Engineering,88000,2021-05-20
103,Charlie Brown,Sales,72000,2023-01-10
104,David Lee,Engineering,95000,2020-11-01
import pandas as pd
import os

csv_file_path = 'employees.csv'

if not os.path.exists(csv_file_path):
    print(f"錯(cuò)誤: CSV 文件 '{csv_file_path}' 未找到。")
else:
    try:
        # 使用 pandas 讀取 CSV,自動(dòng)推斷數(shù)據(jù)類型,header=0表示第一行為表頭
        df = pd.read_csv(csv_file_path, encoding='utf-8')

        print(f"--- 從 '{csv_file_path}' 讀取的 DataFrame ---")
        # display 完整 DataFrame (在 Jupyter 等環(huán)境中效果更佳)
        # 在普通腳本中,print(df) 即可
        # display(df) # 取消注釋以在支持的環(huán)境中使用
        print(df)

        print("\n--- DataFrame 基本信息 ---")
        df.info() # 顯示列名、非空值數(shù)量、數(shù)據(jù)類型、內(nèi)存占用

        print("\n--- DataFrame 描述性統(tǒng)計(jì) (數(shù)值列) ---")
        print(df.describe()) # 計(jì)算均值、標(biāo)準(zhǔn)差、最小值、最大值等

        print("\n--- 前 3 行數(shù)據(jù) ---")
        print(df.head(3)) # 顯示頭幾行

    except pd.errors.EmptyDataError:
        print(f"錯(cuò)誤: CSV 文件 '{csv_file_path}' 為空。")
    except pd.errors.ParserError:
        print(f"錯(cuò)誤: 解析 CSV 文件 '{csv_file_path}' 失敗,請(qǐng)檢查格式。")
    except Exception as e:
        print(f"使用 pandas 讀取 CSV 時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: pandas.read_csv() 是讀取 CSV 的首選方法,功能強(qiáng)大。df.info() 和 df.describe() 用于快速了解數(shù)據(jù)概況。增加了對(duì)空文件和解析錯(cuò)誤的捕獲。

案例 4: 寫入 CSV 文件 (Pandas)

場(chǎng)景: 將數(shù)據(jù)分析結(jié)果、處理后的數(shù)據(jù)導(dǎo)出為 CSV 格式,便于共享或后續(xù)使用。

import pandas as pd
from datetime import date

# 假設(shè)我們有一些處理后的數(shù)據(jù),存儲(chǔ)在 DataFrame 中
processed_data = {
    'ProductID': ['P001', 'P002', 'P003'],
    'Category': ['Electronics', 'Home Goods', 'Electronics'],
    'Revenue': [1200.50, 85.00, 699.99],
    'LastUpdate': [date(2024, 7, 26), date(2024, 7, 25), date(2024, 7, 26)]
}
df_to_write = pd.DataFrame(processed_data)

output_csv_path = 'processed_products.csv'

try:
    # 將 DataFrame 寫入 CSV 文件
    # index=False: 不將 DataFrame 的索引寫入文件列
    # encoding='utf-8-sig': 使用帶 BOM 的 UTF-8,有助于 Excel 正確識(shí)別中文
    df_to_write.to_csv(output_csv_path, index=False, encoding='utf-8-sig', date_format='%Y-%m-%d')
    print(f"DataFrame 已成功寫入 CSV 文件: '{output_csv_path}'")

except Exception as e:
    print(f"使用 pandas 寫入 CSV 時(shí)發(fā)生錯(cuò)誤: {e}")
# (確保已導(dǎo)入: from datetime import date)

注釋: DataFrame.to_csv() 方法非常方便。index=False 是常用選項(xiàng),避免引入無意義的索引列。encoding='utf-8-sig' 解決 Excel 打開 UTF-8 CSV 文件可能出現(xiàn)的中文亂碼問題。date_format 控制日期對(duì)象的輸出格式。

3. Excel 文件 (XLSX)

案例 5: 讀取 Excel 文件 (Pandas)

場(chǎng)景: 處理 .xlsx 格式的電子表格,這是商業(yè)環(huán)境中非常常見的數(shù)據(jù)格式。

示例數(shù)據(jù) (sales_data.xlsx): (假設(shè)包含一個(gè)名為 Q4_Sales 的工作表,結(jié)構(gòu)類似 CSV 示例)

import pandas as pd
import os

excel_file_path = 'sales_data.xlsx'
sheet_name_to_read = 'Q4_Sales' # 指定要讀取的工作表名稱

if not os.path.exists(excel_file_path):
    print(f"錯(cuò)誤: Excel 文件 '{excel_file_path}' 未找到。")
else:
    try:
        # 使用 pandas 讀取 Excel 文件,需指定 sheet_name
        # engine='openpyxl' 是讀取 .xlsx 格式所必需的
        df_excel = pd.read_excel(excel_file_path, sheet_name=sheet_name_to_read, engine='openpyxl')

        print(f"--- 從 '{excel_file_path}' (Sheet: '{sheet_name_to_read}') 讀取的 DataFrame ---")
        # display(df_excel) # 在支持的環(huán)境中使用
        print(df_excel)

        print("\n--- 數(shù)據(jù)類型 ---")
        print(df_excel.dtypes)

    except FileNotFoundError: # redundant check but good practice within try
         print(f"錯(cuò)誤: 文件 '{excel_file_path}' 未找到。")
    except ValueError as e:
         # Handles cases like sheet name not found
         print(f"讀取 Excel 文件時(shí)發(fā)生值錯(cuò)誤: {e}")
    except ImportError:
        print("錯(cuò)誤: 需要安裝 'openpyxl' 庫來讀取 .xlsx 文件。請(qǐng)運(yùn)行: pip install openpyxl")
    except Exception as e:
        print(f"讀取 Excel 文件時(shí)發(fā)生未知錯(cuò)誤: {e}")

注釋: pandas.read_excel() 是核心函數(shù)。sheet_name 參數(shù)可以指定工作表名稱或索引 (0-based)。必須安裝 openpyxl 才能處理 .xlsx 文件。添加了對(duì) ImportError 的捕獲提示用戶安裝庫。

案例 6: 寫入 Excel 文件 (Pandas)

場(chǎng)景: 將多個(gè)數(shù)據(jù)分析結(jié)果或報(bào)告分別存儲(chǔ)到同一個(gè) Excel 文件的不同工作表中。

import pandas as pd

# 假設(shè)有兩個(gè) DataFrame 需要寫入
df_summary = pd.DataFrame({'Metric': ['Total Sales', 'Avg Order Value'], 'Value': [150000, 75.50]})
df_details = pd.DataFrame({ # Reuse data from Case 3 for details example
    'ID': [101, 102, 103, 104],
    'Name': ['Alice Smith', 'Bob Johnson', 'Charlie Brown', 'David Lee'],
    'Salary': [65000, 88000, 72000, 95000]
})

output_excel_path = 'financial_report.xlsx'

try:
    # 使用 pd.ExcelWriter 作為上下文管理器,可以寫入多個(gè) sheet
    with pd.ExcelWriter(output_excel_path, engine='openpyxl') as writer:
        # 將第一個(gè) DataFrame 寫入名為 'Summary' 的 sheet
        df_summary.to_excel(writer, sheet_name='Summary', index=False)
        # 將第二個(gè) DataFrame 寫入名為 'Employee Details' 的 sheet
        df_details.to_excel(writer, sheet_name='Employee Details', index=False, startrow=1, startcol=0) # 示例:指定起始行列

        # 可以添加更多 df.to_excel(...) 調(diào)用來寫入更多 sheet

    print(f"數(shù)據(jù)已成功寫入 Excel 文件 '{output_excel_path}' 的多個(gè) Sheet 中。")

except ImportError:
     print("錯(cuò)誤: 需要安裝 'openpyxl' 庫來寫入 .xlsx 文件。請(qǐng)運(yùn)行: pip install openpyxl")
except Exception as e:
    print(f"寫入 Excel 文件時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: pd.ExcelWriter 是寫入多個(gè)工作表的標(biāo)準(zhǔn)方法。在 with 塊內(nèi),對(duì)每個(gè) DataFrame 調(diào)用 to_excel() 并指定不同的 sheet_name。index=False 同樣常用。startrow, startcol 等參數(shù)可用于更精細(xì)的布局控制。

4. 目錄與文件系統(tǒng)操作

案例 7: 列出目錄內(nèi)容

場(chǎng)景: 需要程序化地獲取指定文件夾下的所有文件和子文件夾列表。

import os

target_dir = '.' # '.' 代表當(dāng)前腳本所在的目錄, 可替換為其他路徑
# target_dir = '/path/to/your/directory'

if not os.path.isdir(target_dir):
    print(f"錯(cuò)誤: 目錄 '{target_dir}' 不存在或不是一個(gè)有效的目錄。")
else:
    try:
        print(f"--- '{target_dir}' 目錄內(nèi)容列表 ---")
        # os.listdir() 返回目錄中所有條目的名稱列表 (字符串)
        items = os.listdir(target_dir)

        for item_name in items:
            # 使用 os.path.join() 安全地構(gòu)建完整路徑,跨平臺(tái)兼容
            full_path = os.path.join(target_dir, item_name)

            # 判斷是文件還是目錄
            if os.path.isfile(full_path):
                # 獲取文件大小 (可選)
                file_size = os.path.getsize(full_path)
                print(f"  [文件] {item_name} ({file_size} bytes)")
            elif os.path.isdir(full_path):
                print(f"  [目錄] {item_name}/")
            else:
                print(f"  [其他] {item_name}") # 如符號(hào)鏈接等

    except OSError as e:
        print(f"訪問目錄 '{target_dir}' 時(shí)發(fā)生 OS 錯(cuò)誤: {e}")
    except Exception as e:
        print(f"列出目錄內(nèi)容時(shí)發(fā)生未知錯(cuò)誤: {e}")

注釋: os.listdir() 獲取基本列表。os.path.join() 構(gòu)造路徑是最佳實(shí)踐。os.path.isfile() 和 os.path.isdir() 用于區(qū)分類型。增加了目錄有效性檢查和 OSError 捕獲。

案例 8: 創(chuàng)建新目錄

場(chǎng)景: 在執(zhí)行文件寫入操作前,確保所需的輸出目錄層級(jí)結(jié)構(gòu)存在。

import os

# 定義要?jiǎng)?chuàng)建的目錄路徑,可以是多層嵌套的
new_directory_path = 'output/reports/2024/Q3'

try:
    # os.makedirs() 會(huì)創(chuàng)建所有不存在的父目錄
    # exist_ok=True: 如果目標(biāo)目錄已存在,不會(huì)拋出 FileExistsError 異常
    os.makedirs(new_directory_path, exist_ok=True)
    print(f"目錄 '{new_directory_path}' 已成功創(chuàng)建或已存在。")

    # # 如果只想創(chuàng)建單層目錄,且要求父目錄必須存在,使用 os.mkdir()
    # # os.mkdir('single_level_folder') # 如果存在會(huì)報(bào)錯(cuò)

except OSError as e:
    print(f"創(chuàng)建目錄時(shí)發(fā)生 OS 錯(cuò)誤: {e}")
except Exception as e:
    print(f"創(chuàng)建目錄時(shí)發(fā)生未知錯(cuò)誤: {e}")

注釋: os.makedirs() 是創(chuàng)建目錄(尤其是嵌套目錄)的推薦方法。exist_ok=True 參數(shù)極大提升了腳本的健壯性,避免因目錄已存在而中斷。

案例 9: 重命名或移動(dòng)文件/目錄

場(chǎng)景: 批量調(diào)整文件名,或?qū)⑽募?目錄遷移到新的位置。

import os

# 假設(shè)我們有一個(gè)文件需要處理
source_path = 'system_report.log' # 源文件 (來自案例 2)
# 目標(biāo)1: 重命名 (在同一目錄下)
rename_target_path = 'system_report_final.log'
# 目標(biāo)2: 移動(dòng)并重命名 (到新目錄)
move_target_path = 'output/logs/system_report_archive.log' # 移動(dòng)到 output/logs/ 目錄下

# --- 執(zhí)行重命名 ---
try:
    if os.path.exists(source_path):
        os.rename(source_path, rename_target_path)
        print(f"文件 '{source_path}' 已成功重命名為 '{rename_target_path}'")
        source_path = rename_target_path # 更新源路徑,以便后續(xù)移動(dòng)操作
    else:
         print(f"警告: 源文件 '{source_path}' 不存在,跳過重命名。")

except OSError as e:
    print(f"重命名文件時(shí)發(fā)生 OS 錯(cuò)誤: {e}")
except Exception as e:
    print(f"重命名文件時(shí)發(fā)生未知錯(cuò)誤: {e}")

# --- 執(zhí)行移動(dòng) ---
try:
    if os.path.exists(source_path):
        # 移動(dòng)前,確保目標(biāo)目錄存在
        target_dir = os.path.dirname(move_target_path)
        os.makedirs(target_dir, exist_ok=True) # 使用案例 8 的方法

        # os.rename() 也可以用于移動(dòng)文件/目錄
        os.rename(source_path, move_target_path)
        print(f"文件 '{source_path}' 已成功移動(dòng)到 '{move_target_path}'")
    else:
        print(f"警告: 源文件 '{source_path}' 不存在,跳過移動(dòng)。")

except OSError as e:
    print(f"移動(dòng)文件時(shí)發(fā)生 OS 錯(cuò)誤: {e}")
except Exception as e:
    print(f"移動(dòng)文件時(shí)發(fā)生未知錯(cuò)誤: {e}")

注釋: os.rename(src, dst) 函數(shù)同時(shí)處理重命名和移動(dòng)。移動(dòng)操作前,使用 os.path.dirname() 獲取目標(biāo)目錄,并用 os.makedirs() 確保其存在,這是健壯代碼的關(guān)鍵。增加了對(duì)源文件存在性的檢查。

案例 10: 檢查路徑是否存在

場(chǎng)景: 在執(zhí)行讀取、寫入或刪除等操作前,驗(yàn)證目標(biāo)文件或目錄是否確實(shí)存在。

import os

paths_to_check = [
    'employees.csv',         # 假設(shè)存在的文件 (來自案例 3)
    'output/reports/2024',   # 假設(shè)存在的目錄 (來自案例 8)
    'non_existent_file.tmp', # 假設(shè)不存在的文件
    '.'                      # 當(dāng)前目錄 (一定存在)
]

print("--- 路徑存在性檢查 ---")
for path in paths_to_check:
    exists = os.path.exists(path)
    print(f"路徑: '{path}'")
    if exists:
        is_file = os.path.isfile(path)
        is_dir = os.path.isdir(path)
        path_type = "文件" if is_file else ("目錄" if is_dir else "其他類型")
        print(f"  狀態(tài): 存在 (類型: {path_type})")
    else:
        print(f"  狀態(tài): 不存在")

注釋: os.path.exists() 是最通用的存在性檢查。結(jié)合 os.path.isfile() 和 os.path.isdir() 可以進(jìn)一步判斷路徑的具體類型。

三、數(shù)據(jù)處理與分析

利用 Python 的強(qiáng)大庫對(duì)結(jié)構(gòu)化和半結(jié)構(gòu)化數(shù)據(jù)進(jìn)行解析、轉(zhuǎn)換和洞察。

1. JSON 數(shù)據(jù)解析

案例 11: 解析 JSON 字符串與文件

場(chǎng)景: 處理來自 Web API 的響應(yīng)、讀取 JSON 格式的配置文件或數(shù)據(jù)存儲(chǔ)。

示例數(shù)據(jù) (字符串):

json_string = '{"name": "Alice", "age": 30, "city": "New York", "isStudent": false, "courses": ["Math", "Physics"]}'

示例數(shù)據(jù) (product_info.json):

{
  "productId": "XYZ-123",
  "details": {
    "name": "Wireless Mouse",
    "brand": "Logi",
    "price": 29.99
  },
  "inventory": {
    "stock": 150,
    "locations": ["Warehouse A", "Warehouse B"]
  },
  "tags": ["electronics", "computer accessories", "wireless"]
}
import json
import os

# --- 從字符串解析 JSON ---
print("--- 解析 JSON 字符串 ---")
try:
    data_from_string = json.loads(json_string) # loads: load string
    print("解析結(jié)果 (Python Dict):", data_from_string)
    print("姓名:", data_from_string['name'])
    print("第一個(gè)課程:", data_from_string['courses'][0])
except json.JSONDecodeError as e:
    print(f"解析 JSON 字符串失敗: {e}")
except Exception as e:
    print(f"處理 JSON 字符串時(shí)發(fā)生錯(cuò)誤: {e}")

print("\n--- 從文件解析 JSON ---")
json_file_path = 'product_info.json'

if not os.path.exists(json_file_path):
     print(f"錯(cuò)誤: JSON 文件 '{json_file_path}' 未找到。")
else:
    try:
        with open(json_file_path, 'r', encoding='utf-8') as f:
            data_from_file = json.load(f) # load: load from file object
        print("解析結(jié)果 (Python Dict):")
        # 使用 json.dumps 美化打印輸出
        print(json.dumps(data_from_file, indent=4, ensure_ascii=False))
        print("\n產(chǎn)品名稱:", data_from_file['details']['name'])
        print("庫存地點(diǎn):", data_from_file['inventory']['locations'])
    except json.JSONDecodeError as e:
        print(f"解析 JSON 文件 '{json_file_path}' 失敗: {e}")
    except IOError as e:
        print(f"讀取 JSON 文件時(shí)發(fā)生 IO 錯(cuò)誤: {e}")
    except Exception as e:
        print(f"處理 JSON 文件時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: json.loads() 用于從字符串解析,json.load() 用于從文件對(duì)象解析。兩者都將 JSON 數(shù)據(jù)轉(zhuǎn)換為相應(yīng)的 Python 對(duì)象(通常是字典和列表)。json.dumps() 可用于將 Python 對(duì)象序列化回格式化的 JSON 字符串,indent=4 實(shí)現(xiàn)美觀縮進(jìn),ensure_ascii=False 保證非 ASCII 字符(如中文)正確顯示。

2. 使用 Pandas 進(jìn)行數(shù)據(jù)篩選

案例 12: 基于條件篩選 DataFrame 行

場(chǎng)景: 從龐大的數(shù)據(jù)集中(如加載自 CSV 或 Excel 的 DataFrame)提取滿足特定業(yè)務(wù)規(guī)則的子集。

示例數(shù)據(jù): 使用案例 3 中的 employees.csv 加載的 df。

import pandas as pd
import os

csv_file_path = 'employees.csv' # 確保此文件存在且內(nèi)容如案例3所示

if not os.path.exists(csv_file_path):
    print(f"錯(cuò)誤: CSV 文件 '{csv_file_path}' 未找到。")
else:
    try:
        df = pd.read_csv(csv_file_path, parse_dates=['HireDate']) # 讀取時(shí)解析日期列
        print("--- 原始 DataFrame (部分) ---")
        print(df.head())

        # --- 條件篩選示例 ---
        print("\n--- 篩選: 部門為 'Engineering' 的員工 ---")
        # 使用布爾索引: df[<boolean_series>]
        eng_dept = df[df['Department'] == 'Engineering']
        print(eng_dept)

        print("\n--- 篩選: 薪水大于 80000 的員工 ---")
        high_salary = df[df['Salary'] > 80000]
        print(high_salary)

        print("\n--- 篩選: 組合條件 - 'Engineering' 部門且薪水大于 90000 ---")
        # 使用 & (與) 連接條件,每個(gè)條件需用括號(hào)包裹
        eng_high_salary = df[(df['Department'] == 'Engineering') & (df['Salary'] > 90000)]
        print(eng_high_salary)

        print("\n--- 篩選: 部門為 'HR' 或 'Sales' 的員工 ---")
        # 使用 | (或) 連接條件
        hr_or_sales = df[(df['Department'] == 'HR') | (df['Department'] == 'Sales')]
        # 或者使用 .isin() 方法,更適合多個(gè) '或' 條件
        # hr_or_sales = df[df['Department'].isin(['HR', 'Sales'])]
        print(hr_or_sales)

        print("\n--- 篩選: 2022年之后入職的員工 ---")
        # 利用日期類型進(jìn)行比較 (前提是 HireDate 已被解析為 datetime 對(duì)象)
        hired_after_2022 = df[df['HireDate'].dt.year >= 2022]
        print(hired_after_2022)

    except Exception as e:
        print(f"使用 pandas 篩選數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: Pandas 的布爾索引是核心篩選機(jī)制。通過比較列與值生成布爾 Series (True/False)。使用 & (與) 和 | (或) 組合條件,注意運(yùn)算符優(yōu)先級(jí),務(wù)必用括號(hào)包裹各個(gè)條件。.isin() 對(duì)于多個(gè)“或”條件的檢查更簡(jiǎn)潔。對(duì)于日期/時(shí)間列,可以利用 .dt 訪問器進(jìn)行年、月、日等屬性的比較。parse_dates 參數(shù)在讀取時(shí)轉(zhuǎn)換日期列,非常重要。

3. 使用 Pandas 進(jìn)行分組聚合

案例 13: GroupBy 與聚合計(jì)算

場(chǎng)景: 對(duì)數(shù)據(jù)按類別進(jìn)行分組,并對(duì)每個(gè)組執(zhí)行統(tǒng)計(jì)計(jì)算(如求和、均值、計(jì)數(shù)等),常用于生成匯總報(bào)告。

示例數(shù)據(jù): 使用案例 3 中的 employees.csv 加載的 df。

import pandas as pd
import os

csv_file_path = 'employees.csv'

if not os.path.exists(csv_file_path):
    print(f"錯(cuò)誤: CSV 文件 '{csv_file_path}' 未找到。")
else:
    try:
        df = pd.read_csv(csv_file_path)
        print("--- 原始 DataFrame (部分) ---")
        print(df.head())

        # --- 分組聚合示例 ---
        print("\n--- 按 'Department' 分組,計(jì)算各部門員工人數(shù) ---")
        # .groupby('列名') 創(chuàng)建分組對(duì)象,.size() 計(jì)算每組大小 (行數(shù))
        dept_counts = df.groupby('Department').size()
        # .reset_index(name='...') 將結(jié)果轉(zhuǎn)回 DataFrame,并指定計(jì)數(shù)列的名稱
        dept_counts_df = dept_counts.reset_index(name='EmployeeCount')
        print(dept_counts_df)

        print("\n--- 按 'Department' 分組,計(jì)算各部門平均薪水 ---")
        # 選擇要聚合的列 ['Salary'],然后調(diào)用聚合函數(shù) .mean()
        avg_salary_by_dept = df.groupby('Department')['Salary'].mean()
        avg_salary_by_dept_df = avg_salary_by_dept.reset_index(name='AverageSalary')
        # 可以格式化輸出
        avg_salary_by_dept_df['AverageSalary'] = avg_salary_by_dept_df['AverageSalary'].map('{:,.2f}'.format)
        print(avg_salary_by_dept_df)

        print("\n--- 按 'Department' 分組,計(jì)算多項(xiàng)聚合統(tǒng)計(jì) (人數(shù), 平均/最高薪水) ---")
        # 使用 .agg() 方法可以同時(shí)計(jì)算多個(gè)聚合指標(biāo)
        agg_stats = df.groupby('Department').agg(
            NumberOfEmployees=('ID', 'count'),      # 對(duì) ID 列計(jì)數(shù) (假設(shè) ID 非空)
            AverageSalary=('Salary', 'mean'),     # 計(jì)算 Salary 列的平均值
            MaxSalary=('Salary', 'max'),          # 計(jì)算 Salary 列的最大值
            MinHireDate=('HireDate', 'min')     # 計(jì)算 HireDate 的最早日期 (需先轉(zhuǎn)為日期類型)
        )
        # .reset_index() 將分組鍵 (Department) 轉(zhuǎn)回列
        agg_stats_df = agg_stats.reset_index()
        # 重命名列 (可選,如果 agg 中未使用元組指定名稱)
        # agg_stats_df.columns = ['Department', 'EmployeeCount', 'AvgSalary', 'MaxSalary']
        print(agg_stats_df)

    except Exception as e:
        print(f"使用 pandas 分組聚合時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: df.groupby('分組列') 是分組操作的起點(diǎn)。后接選擇要聚合的列(如 ['Salary'])和聚合函數(shù)(如 .mean(), .sum(), .count(), .size(), .max(), .min() 等)。.agg() 方法提供了更靈活的多重聚合能力,可以使用元組 (列名, 聚合函數(shù)) 并直接命名結(jié)果列。.reset_index() 常用于將分組結(jié)果從 Series 或帶有多級(jí)索引的 DataFrame 轉(zhuǎn)換回標(biāo)準(zhǔn)的 DataFrame。

四、自動(dòng)化與交互

利用 Python 與外部系統(tǒng)、API 和文本數(shù)據(jù)進(jìn)行交互,實(shí)現(xiàn)任務(wù)自動(dòng)化。

1. 網(wǎng)絡(luò)數(shù)據(jù)獲取 (HTTP GET)

案例 14: 發(fā)送 GET 請(qǐng)求獲取 API 數(shù)據(jù)

場(chǎng)景: 調(diào)用 Web API 獲取實(shí)時(shí)數(shù)據(jù)、服務(wù)信息或執(zhí)行查詢。

import requests
import json

# 使用 JSONPlaceholder 作為示例 API (提供虛擬數(shù)據(jù))
api_endpoint_url = 'https://jsonplaceholder.typicode.com/users/1' # 獲取 ID 為 1 的用戶信息

print(f"--- 向 '{api_endpoint_url}' 發(fā)送 GET 請(qǐng)求 ---")
try:
    # 發(fā)送 GET 請(qǐng)求,設(shè)置超時(shí)時(shí)間 (timeout 秒) 防止無限等待
    response = requests.get(api_endpoint_url, timeout=10)

    # 檢查 HTTP 狀態(tài)碼,對(duì)于非 2xx (成功) 的狀態(tài)碼拋出異常
    response.raise_for_status()

    # 請(qǐng)求成功,解析返回的 JSON 數(shù)據(jù)
    user_data = response.json() # requests 自動(dòng)處理 JSON 解析

    print("--- API 響應(yīng)成功 (狀態(tài)碼:", response.status_code, ") ---")
    print("解析后的用戶數(shù)據(jù) (Python Dict):")
    # 美化打印 JSON
    print(json.dumps(user_data, indent=4))

    # 訪問具體數(shù)據(jù)
    print(f"\n用戶名: {user_data.get('username')}") # 使用 .get() 訪問字典更安全,鍵不存在時(shí)返回 None
    print(f"公司名稱: {user_data.get('company', {}).get('name')}") # 嵌套訪問也用 .get()

except requests.exceptions.Timeout:
    print("錯(cuò)誤: 請(qǐng)求超時(shí)。服務(wù)器未在指定時(shí)間內(nèi)響應(yīng)。")
except requests.exceptions.HTTPError as e:
    # raise_for_status() 拋出的異常
    print(f"錯(cuò)誤: HTTP 請(qǐng)求失敗,狀態(tài)碼: {e.response.status_code}")
    print(f"  響應(yīng)內(nèi)容: {e.response.text[:200]}...") # 打印部分響應(yīng)內(nèi)容幫助調(diào)試
except requests.exceptions.RequestException as e:
    # 其他網(wǎng)絡(luò)相關(guān)錯(cuò)誤 (如 DNS 解析失敗、連接錯(cuò)誤)
    print(f"錯(cuò)誤: 網(wǎng)絡(luò)請(qǐng)求異常: {e}")
except json.JSONDecodeError:
    # 如果響應(yīng)不是有效的 JSON 格式
    print("錯(cuò)誤: 無法解析 API 返回的 JSON 數(shù)據(jù)。")
    print(f"  原始響應(yīng)文本 (部分): {response.text[:200]}...")
except Exception as e:
    print(f"處理 API 請(qǐng)求時(shí)發(fā)生未知錯(cuò)誤: {e}")

注釋: requests.get(url, timeout=...) 發(fā)送 GET 請(qǐng)求。response.raise_for_status() 是檢查請(qǐng)求是否成功的關(guān)鍵一步。response.json() 將 JSON 響應(yīng)體直接轉(zhuǎn)換為 Python 字典或列表。必須做好異常處理,覆蓋超時(shí)、HTTP 錯(cuò)誤、網(wǎng)絡(luò)問題和 JSON 解析錯(cuò)誤等常見情況。使用 .get() 訪問字典層級(jí)可以避免因鍵不存在而引發(fā) KeyError。

2. 文本模式處理 (正則表達(dá)式)

案例 15: 使用 Regex 查找文本模式

場(chǎng)景: 從非結(jié)構(gòu)化文本(如日志文件、郵件內(nèi)容、網(wǎng)頁源碼)中精確提取符合特定格式的信息(郵箱、URL、IP 地址、特定 ID 等)。

示例數(shù)據(jù):

log_data = """
INFO: Processing request ID: REQ-12345 from user@example.com at 2024-07-26 10:00:00.
WARN: Slow response time for session SID-ABCDEF. IP: 192.168.1.100.
ERROR: Failed login attempt for admin@test.net. Request ID: REQ-67890. See details at https://internal.corp/errors/123.
"""
import re

print("--- 使用正則表達(dá)式查找模式 ---")

# 1. 查找所有郵箱地址
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, log_data)
print("找到的郵箱地址:", emails)

# 2. 查找所有請(qǐng)求 ID (格式: REQ- followed by digits)
request_id_pattern = r'\bREQ-\d+\b'
request_ids = re.findall(request_id_pattern, log_data)
print("找到的請(qǐng)求 ID:", request_ids)

# 3. 查找所有 IP 地址 (簡(jiǎn)易版 IPv4)
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b' # 使用非捕獲組 (?:...)
ip_addresses = re.findall(ip_pattern, log_data)
print("找到的 IP 地址:", ip_addresses)

# 4. 查找第一個(gè) URL
url_pattern = r'https?://[^\s/$.?#].[^\s]*' # 查找 http 或 https 開頭的 URL
first_url_match = re.search(url_pattern, log_data) # search() 查找第一個(gè)匹配項(xiàng)
if first_url_match:
    print("找到的第一個(gè) URL:", first_url_match.group(0)) # group(0) 或 group() 返回整個(gè)匹配
else:
    print("未找到 URL。")

注釋: re.findall(pattern, string) 返回所有非重疊匹配項(xiàng)的列表。re.search(pattern, string) 返回第一個(gè)匹配的 Match 對(duì)象,若無匹配則返回 None。Match 對(duì)象通過 .group() 方法獲取匹配的文本。正則表達(dá)式語法 (r'...') 是關(guān)鍵,需要根據(jù)目標(biāo)模式精心構(gòu)造。\b 表示單詞邊界,對(duì)于精確匹配很有用。

案例 16: 使用 Regex 替換文本內(nèi)容

場(chǎng)景: 清洗數(shù)據(jù)、脫敏(隱藏敏感信息如密碼、手機(jī)號(hào))、統(tǒng)一文本格式(如日期格式)。

示例數(shù)據(jù):

report_text = "User 'john.doe' (john.doe@company.com) accessed sensitive data. Phone: +1-123-456-7890. Password hint: 'johndoe123'."
import re

print("--- 使用正則表達(dá)式替換文本 ---")
print("原始文本:", report_text)

# 1. 替換郵箱地址為 [REDACTED_EMAIL]
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
redacted_text_email = re.sub(email_pattern, '[REDACTED_EMAIL]', report_text)
print("替換郵箱后:", redacted_text_email)

# 2. 替換電話號(hào)碼 (簡(jiǎn)易模式) 為 [REDACTED_PHONE]
phone_pattern = r'\+?\d[\d -]{8,}\d' # 匹配包含數(shù)字、空格、連字符的電話號(hào)碼
redacted_text_phone = re.sub(phone_pattern, '[REDACTED_PHONE]', redacted_text_email) # 在上一步結(jié)果上繼續(xù)替換
print("替換電話后:", redacted_text_phone)

# 3. 替換密碼提示 (單引號(hào)內(nèi)的內(nèi)容) 為 [REDACTED_HINT]
# 使用捕獲組 '([^']+)' 來匹配單引號(hào)內(nèi)的內(nèi)容,但不替換單引號(hào)本身
hint_pattern = r"Password hint: '([^']+)'"
# 使用 re.sub 的 repl 參數(shù)為函數(shù),可以進(jìn)行更復(fù)雜的替換邏輯
def replace_hint(match):
    # match.group(1) 獲取第一個(gè)捕獲組的內(nèi)容 (密碼提示)
    hint_content = match.group(1)
    return f"Password hint: '[REDACTED_{len(hint_content)}_CHARS]'" # 返回替換后的字符串

redacted_text_final = re.sub(hint_pattern, replace_hint, redacted_text_phone)
print("替換密碼提示后:", redacted_text_final)

注釋: re.sub(pattern, replacement, string) 是核心替換函數(shù)。replacement 可以是固定字符串,也可以是一個(gè)函數(shù)。當(dāng) replacement 是函數(shù)時(shí),它接收一個(gè) Match 對(duì)象作為參數(shù),其返回值將用于替換匹配到的文本。這使得可以基于匹配到的內(nèi)容進(jìn)行動(dòng)態(tài)替換(如此處根據(jù)密碼提示長(zhǎng)度生成替換文本)。

3. 日期與時(shí)間處理

案例 17: 獲取當(dāng)前日期時(shí)間與格式化

場(chǎng)景: 為日志、報(bào)告、文件名添加時(shí)間戳,或進(jìn)行基于當(dāng)前時(shí)間的計(jì)算。

from datetime import datetime

print("--- 獲取與格式化當(dāng)前日期時(shí)間 ---")

# 獲取當(dāng)前本地日期和時(shí)間
now = datetime.now()
print(f"當(dāng)前完整時(shí)間對(duì)象: {now}")

# 獲取 UTC 時(shí)間 (世界協(xié)調(diào)時(shí)間)
utc_now = datetime.utcnow()
print(f"當(dāng)前 UTC 時(shí)間對(duì)象: {utc_now}")

# 獲取日期部分
today_date = now.date()
print(f"當(dāng)前日期: {today_date}")

# 獲取時(shí)間部分
current_time = now.time()
print(f"當(dāng)前時(shí)間: {current_time}")

# --- 格式化輸出 (strftime: object to string) ---
# 常用格式代碼:
# %Y: 4位年份 (2024)
# %m: 月份 (01-12)
# %d: 日期 (01-31)
# %H: 24小時(shí)制小時(shí) (00-23)
# %M: 分鐘 (00-59)
# %S: 秒 (00-59)
# %f: 微秒 (000000-999999)
# %A: 星期幾全稱 (Monday)
# %B: 月份全稱 (July)
# %Y-%m-%d %H:%M:%S 是非常標(biāo)準(zhǔn)的格式
formatted_standard = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"標(biāo)準(zhǔn)格式化: {formatted_standard}")

formatted_filename = now.strftime("%Y%m%d_%H%M%S") # 適合用作文件名
print(f"文件名適用格式: {formatted_filename}")

formatted_readable = now.strftime("%A, %B %d, %Y - %I:%M:%S %p") # %I: 12小時(shí)制, %p: AM/PM
print(f"易讀格式化: {formatted_readable}")

注釋: datetime.now() 獲取本地當(dāng)前時(shí)間。datetime.utcnow() 獲取 UTC 時(shí)間。.date() 和 .time() 分別提取日期和時(shí)間部分。strftime() 方法是關(guān)鍵,它根據(jù)指定的格式代碼將 datetime 對(duì)象轉(zhuǎn)換為字符串。

案例 18: 計(jì)算時(shí)間差 (Timedelta)

場(chǎng)景: 計(jì)算任務(wù)執(zhí)行耗時(shí)、事件間隔、項(xiàng)目周期、判斷是否超時(shí)等。

from datetime import datetime, timedelta
import time

print("--- 計(jì)算時(shí)間差 ---")

# 記錄開始時(shí)間
start_time = datetime.now()
print(f"操作開始時(shí)間: {start_time.strftime('%H:%M:%S.%f')}")

# 模擬一個(gè)耗時(shí)操作 (例如,暫停1.2秒)
time.sleep(1.2)

# 記錄結(jié)束時(shí)間
end_time = datetime.now()
print(f"操作結(jié)束時(shí)間: {end_time.strftime('%H:%M:%S.%f')}")

# 兩個(gè) datetime 對(duì)象相減得到一個(gè) timedelta 對(duì)象
duration = end_time - start_time
print(f"\n操作耗時(shí) (timedelta 對(duì)象): {duration}")

# 從 timedelta 對(duì)象獲取具體數(shù)值
total_seconds = duration.total_seconds() # 獲取總秒數(shù) (包含小數(shù))
print(f"總耗時(shí)秒數(shù): {total_seconds:.3f} s")

days = duration.days # 獲取完整的天數(shù)
seconds_part = duration.seconds # 獲取不足一天部分的秒數(shù) (0-86399)
microseconds_part = duration.microseconds # 獲取微秒部分 (0-999999)
print(f"耗時(shí)構(gòu)成: {days} 天, {seconds_part} 秒, {microseconds_part} 微秒")

# --- 時(shí)間點(diǎn)加減 ---
# 創(chuàng)建一個(gè) timedelta 對(duì)象,例如表示 3 天 5 小時(shí)
time_delta_example = timedelta(days=3, hours=5)
print(f"\n時(shí)間增量示例: {time_delta_example}")

# 計(jì)算未來時(shí)間
future_time = start_time + time_delta_example
print(f"從開始時(shí)間算起,3天5小時(shí)后是: {future_time.strftime('%Y-%m-%d %H:%M:%S')}")

# --- 字符串解析與比較 (strptime: string to object) ---
date_str1 = "2023-01-15 09:00:00"
date_str2 = "2024-07-26 18:30:00"
try:
    dt1 = datetime.strptime(date_str1, "%Y-%m-%d %H:%M:%S")
    dt2 = datetime.strptime(date_str2, "%Y-%m-%d %H:%M:%S")
    diff_between_dates = dt2 - dt1
    print(f"\n'{date_str2}' 與 '{date_str1}' 相差: {diff_between_dates}")
    print(f"  即相差天數(shù): {diff_between_dates.days}")
except ValueError as e:
    print(f"解析日期字符串失敗: {e}")

注釋: datetime 對(duì)象相減產(chǎn)生 timedelta 對(duì)象。timedelta 表示時(shí)間間隔,擁有 days, seconds, microseconds 屬性和 total_seconds() 方法。datetime 對(duì)象可以與 timedelta 對(duì)象進(jìn)行加減運(yùn)算。datetime.strptime() 用于將符合特定格式的字符串解析回 datetime 對(duì)象,其格式代碼與 strftime() 相同。

4. 自動(dòng)化郵件通知 (SMTP)

案例 19: 發(fā)送簡(jiǎn)單郵件 (配置說明)

場(chǎng)景: 在任務(wù)完成、發(fā)生異?;蜻_(dá)到特定條件時(shí),自動(dòng)發(fā)送郵件通知相關(guān)人員。

注意: 此代碼為模板,必須根據(jù)您使用的郵箱服務(wù)商(如 QQ郵箱、163郵箱、Gmail、企業(yè)郵箱等)提供的 SMTP 服務(wù)器地址、端口號(hào),并使用授權(quán)碼(而非郵箱登錄密碼)進(jìn)行配置。直接運(yùn)行會(huì)失敗。請(qǐng)務(wù)必開啟郵箱的 SMTP 服務(wù)。

import smtplib
import ssl # 用于安全的 SSL/TLS 連接
from email.mime.text import MIMEText # 構(gòu)建郵件正文
from email.header import Header      # 處理郵件頭中的非 ASCII 字符

# --- SMTP 服務(wù)器配置 (必須替換為實(shí)際值) ---
smtp_server = 'smtp.example.com'       # 例如: smtp.qq.com, smtp.163.com, smtp.gmail.com
smtp_port = 465                        # SSL 端口 (常用) 或 587 (TLS 常用)
sender_email = 'your_account@example.com' # 發(fā)件人郵箱賬號(hào)
sender_password = 'YOUR_APP_PASSWORD'   # 郵箱授權(quán)碼 (不是登錄密碼!)
receiver_emails = ['recipient1@example.net', 'recipient2@example.org'] # 收件人列表
# -------------------------------------------

# --- 郵件內(nèi)容 ---
mail_subject = '【自動(dòng)化通知】Python 腳本執(zhí)行報(bào)告'
mail_body = f"""
尊敬的用戶,

本次 Python 自動(dòng)化任務(wù)已于 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 執(zhí)行完畢。

狀態(tài): 成功
處理記錄數(shù): 150

詳情請(qǐng)查閱附件或日志文件。

此郵件為系統(tǒng)自動(dòng)發(fā)送,請(qǐng)勿回復(fù)。
""" # 可以使用 f-string 動(dòng)態(tài)生成內(nèi)容
# ----------------

# 構(gòu)建郵件對(duì)象
message = MIMEText(mail_body, 'plain', 'utf-8') # 內(nèi)容, 類型(plain/html), 編碼
message['From'] = Header(f"自動(dòng)化監(jiān)控平臺(tái) <{sender_email}>", 'utf-8') # 發(fā)件人顯示名
message['To'] = Header(", ".join(receiver_emails), 'utf-8') # 收件人列表顯示
message['Subject'] = Header(mail_subject, 'utf-8') # 郵件主題

print("--- 準(zhǔn)備發(fā)送郵件 ---")
print(f"SMTP 服務(wù)器: {smtp_server}:{smtp_port}")
print(f"發(fā)件人: {sender_email}")
print(f"收件人: {', '.join(receiver_emails)}")

context = ssl.create_default_context() # 創(chuàng)建安全的 SSL 上下文
server = None # 初始化 server 變量

try:
    if smtp_port == 465:
        # 使用 SMTP_SSL (Implicit SSL/TLS)
        print("正在建立 SSL 連接...")
        server = smtplib.SMTP_SSL(smtp_server, smtp_port, cnotallow=context, timeout=15)
    elif smtp_port == 587:
        # 使用 SMTP + starttls (Explicit TLS)
        print("正在建立普通連接...")
        server = smtplib.SMTP(smtp_server, smtp_port, timeout=15)
        print("升級(jí)到 TLS 加密連接...")
        server.starttls(cnotallow=context)
    else:
        raise ValueError("不支持的 SMTP 端口配置。請(qǐng)使用 465 (SSL) 或 587 (TLS)。")

    print("連接成功,正在登錄...")
    # 登錄 SMTP 服務(wù)器
    server.login(sender_email, sender_password)
    print("登錄成功。")

    # 發(fā)送郵件
    print("正在發(fā)送郵件...")
    # sendmail(發(fā)件人地址, 收件人地址列表, 郵件對(duì)象的字符串表示)
    server.sendmail(sender_email, receiver_emails, message.as_string())
    print("郵件已成功發(fā)送!")

except smtplib.SMTPAuthenticationError:
    print("錯(cuò)誤: SMTP 認(rèn)證失?。≌?qǐng)檢查發(fā)件人郵箱和授權(quán)碼是否正確,以及 SMTP 服務(wù)是否已開啟。")
except smtplib.SMTPConnectError:
    print(f"錯(cuò)誤: 無法連接到 SMTP 服務(wù)器 {smtp_server}:{smtp_port}。請(qǐng)檢查地址和端口。")
except smtplib.SMTPServerDisconnected:
    print("錯(cuò)誤: SMTP 服務(wù)器意外斷開連接。")
except socket.timeout: # 需要 import socket
    print("錯(cuò)誤: 連接或操作超時(shí)。")
except ssl.SSLError as e:
    print(f"錯(cuò)誤: SSL/TLS 握手失敗: {e}。請(qǐng)檢查端口和服務(wù)器配置。")
except Exception as e:
    print(f"發(fā)送郵件時(shí)發(fā)生未知錯(cuò)誤: {e}")
finally:
    # 無論成功與否,都嘗試關(guān)閉連接
    if server:
        try:
            server.quit()
            print("已斷開與 SMTP 服務(wù)器的連接。")
        except Exception as e:
            print(f"關(guān)閉 SMTP 連接時(shí)出錯(cuò): {e}")
# (確保已導(dǎo)入: from datetime import datetime, import socket, import ssl)

注釋: 郵件發(fā)送涉及網(wǎng)絡(luò)和安全,是相對(duì)復(fù)的自動(dòng)化任務(wù)。

  • 正確配置 SMTP 服務(wù)器、端口、發(fā)件人郵箱。
  • 使用授權(quán)碼而非登錄密碼。
  • 啟用郵箱的 SMTP 服務(wù)。
  • 根據(jù)端口選擇 smtplib.SMTP_SSL (465) 或 smtplib.SMTP + server.starttls() (587)。
  • 使用 email.mime.text.MIMEText 構(gòu)建郵件正文,email.header.Header 處理含中文的標(biāo)題/發(fā)件人/收件人。
  • 完善的異常處理至關(guān)重要。
  • 切勿在代碼中硬編碼密碼/授權(quán)碼,應(yīng)使用環(huán)境變量、配置文件或密鑰管理服務(wù)。

五、數(shù)據(jù)可視化

將數(shù)據(jù)分析結(jié)果以圖形方式呈現(xiàn),更直觀地傳遞信息和洞察。

1. 使用 Matplotlib 繪圖

案例 20: 繪制基礎(chǔ)圖表 (柱狀圖、直方圖)

場(chǎng)景: 將 Pandas 分析得到的匯總數(shù)據(jù)(如部門人數(shù)、平均工資)或原始數(shù)據(jù)分布(如工資分布)進(jìn)行可視化展示。

依賴: matplotlib 庫,通常與 pandas 結(jié)合使用。

示例數(shù)據(jù): 使用之前的案例 生成的 dept_counts_df, avg_salary_by_dept_df 和原始 df。

代碼:

import pandas as pd
import matplotlib.pyplot as plt
import os

# --- 準(zhǔn)備數(shù)據(jù) (假設(shè)已運(yùn)行案例 19 的代碼得到 df, dept_counts_df, avg_salary_by_dept_df) ---
# 為保證獨(dú)立運(yùn)行,這里重新加載并計(jì)算一次
csv_file_path = 'employees.csv'
if not os.path.exists(csv_file_path):
    print(f"錯(cuò)誤: 數(shù)據(jù)文件 '{csv_file_path}' 未找到。無法進(jìn)行可視化。")
    # exit() # 在腳本中可以考慮退出
else:
    try:
        df = pd.read_csv(csv_file_path)
        dept_counts_df = df.groupby('Department').size().reset_index(name='EmployeeCount')
        avg_salary_by_dept_df = df.groupby('Department')['Salary'].mean().reset_index(name='AverageSalary')

        # --- 開始繪圖 ---
        # 設(shè)置 Matplotlib 支持中文顯示 (選擇一種方式)
        # 方式一: 指定支持中文的字體
        plt.rcParams['font.sans-serif'] = ['SimHei'] # 例如: SimHei (黑體)
        plt.rcParams['axes.unicode_minus'] = False   # 解決負(fù)號(hào)顯示為方塊的問題

        # 方式二: (如果安裝了 specific font)
        # from matplotlib.font_manager import FontProperties
        # font = FontProperties(fname='/path/to/your/chinese.ttf') # 指定字體文件路徑

        # --- 1. 繪制部門人數(shù)柱狀圖 ---
        plt.figure(figsize=(8, 5)) # 創(chuàng)建圖表畫布, 設(shè)置尺寸 (寬, 高) in inches

        # plt.bar(x軸數(shù)據(jù), y軸數(shù)據(jù), ...)
        plt.bar(dept_counts_df['Department'], dept_counts_df['EmployeeCount'], color='skyblue', alpha=0.8)

        plt.title('各部門員工人數(shù)分布', fnotallow=14) # 圖表標(biāo)題
        plt.xlabel('部門 (Department)', fnotallow=12) # X 軸標(biāo)簽
        plt.ylabel('員工人數(shù) (Number of Employees)', fnotallow=12) # Y 軸標(biāo)簽
        plt.xticks(rotatinotallow=30, ha='right') # 旋轉(zhuǎn) X 軸標(biāo)簽以便閱讀
        plt.grid(axis='y', linestyle='--', alpha=0.6) # 添加水平網(wǎng)格線
        plt.tight_layout() # 自動(dòng)調(diào)整子圖參數(shù),使之填充整個(gè)圖像區(qū)域,防止標(biāo)簽重疊
        # plt.savefig('department_counts.png') # 保存圖表到文件 (可選)
        plt.show() # 顯示圖表窗口

        # --- 2. 繪制各部門平均薪水條形圖 (水平) ---
        plt.figure(figsize=(9, 5))

        # plt.barh(y軸數(shù)據(jù), x軸數(shù)據(jù), ...) # 注意 barh 的參數(shù)順序
        bars = plt.barh(avg_salary_by_dept_df['Department'], avg_salary_by_dept_df['AverageSalary'], color='lightcoral')

        # 在條形圖上添加數(shù)值標(biāo)簽
        for bar in bars:
            width = bar.get_width()
            plt.text(width + 500, bar.get_y() + bar.get_height()/2, f'${width:,.0f}', # x, y, text
                     va='center', ha='left', fnotallow=10)

        plt.title('各部門平均薪水對(duì)比', fnotallow=14)
        plt.xlabel('平均薪水 (Average Salary)', fnotallow=12)
        plt.ylabel('部門 (Department)', fnotallow=12)
        plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}')) # 格式化 X 軸刻度為美元
        plt.tight_layout()
        # plt.savefig('average_salary_by_dept.png')
        plt.show()

        # --- 3. 繪制全體員工薪水分布直方圖 ---
        plt.figure(figsize=(8, 5))

        # plt.hist(數(shù)據(jù)列, bins=柱子數(shù)量/邊界, ...)
        plt.hist(df['Salary'], bins=6, color='lightgreen', edgecolor='black', alpha=0.7)

        plt.title('員工薪水分布直方圖', fnotallow=14)
        plt.xlabel('薪水區(qū)間 (Salary Range)', fnotallow=12)
        plt.ylabel('頻數(shù) (Frequency)', fnotallow=12)
        plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
        plt.grid(axis='y', linestyle=':', alpha=0.5)
        plt.tight_layout()
        # plt.savefig('salary_distribution_hist.png')
        plt.show()

    except ImportError:
        print("錯(cuò)誤: 需要安裝 matplotlib 庫來進(jìn)行可視化。請(qǐng)運(yùn)行: pip install matplotlib")
    except Exception as e:
        print(f"繪制圖表時(shí)發(fā)生錯(cuò)誤: {e}")

注釋: 

  • matplotlib.pyplot (通常別名為 plt) 是主要的繪圖接口。
  • plt.figure() 創(chuàng)建畫布。
  • plt.bar() (垂直柱狀圖)。
  • plt.barh() (水平條形圖)。
  • plt.hist() (直方圖) 是常用繪圖函數(shù)。
  • plt.title(), plt.xlabel(), plt.ylabel() 設(shè)置標(biāo)題和軸標(biāo)簽。
  • plt.xticks()/plt.yticks() 控制刻度。
  • plt.grid() 添加網(wǎng)格線。
  • plt.tight_layout() 自動(dòng)優(yōu)化布局。
  • plt.show() 顯示圖表,
  • plt.savefig() 保存圖表到文件。
  •  中文顯示需要額外配置字體 (plt.rcParams)。
  •  在條形圖/柱狀圖上添加數(shù)值標(biāo)簽可以增強(qiáng)可讀性。

六、總結(jié)

本文通過 20個(gè)貼近實(shí)際工作場(chǎng)景的 Python 案例,系統(tǒng)性地展示了其在 文件與目錄管理、數(shù)據(jù)處理與分析、自動(dòng)化與交互 以及 數(shù)據(jù)可視化 等方面的強(qiáng)大能力和廣泛應(yīng)用。

掌握這些基礎(chǔ)模塊是邁向更高級(jí)自動(dòng)化的第一步。持續(xù)學(xué)習(xí)和實(shí)踐,將 Python 的能力融入您的日常工作流,定能發(fā)掘出更多提升效率、創(chuàng)造價(jià)值的可能性。另外,大家在使用代碼時(shí)注意代碼格式(因文章排版原因,部分代碼需注意左對(duì)齊。)

責(zé)任編輯:趙寧寧 來源: Python數(shù)智工坊
相關(guān)推薦

2025-05-21 02:00:00

JavaScript前端

2020-11-05 14:28:07

Shell腳本SQL

2012-07-03 15:04:22

程序員

2020-08-27 19:30:39

Chrome瀏覽器

2021-11-24 07:47:06

安全

2025-04-21 17:55:25

2020-10-29 15:17:49

代碼開發(fā)工具

2021-06-06 05:58:45

應(yīng)用PasteEx工具

2018-07-03 13:17:00

2019-11-25 10:20:54

CSS代碼javascript

2019-12-25 14:19:21

Python編程語言Java

2019-11-06 14:13:55

開發(fā)者技能工具

2011-08-31 13:43:10

windows8

2020-07-29 10:55:07

數(shù)據(jù)庫工具技術(shù)

2021-09-24 09:59:59

復(fù)制粘貼PythonPDF

2025-04-30 05:58:20

2018-09-03 14:49:27

Python實(shí)戰(zhàn)項(xiàng)目

2017-06-07 15:51:50

AndroidAndroid Stuapt

2023-09-26 07:39:21

2019-07-12 14:00:55

xclipLinux命令行
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)