# Python 進階實戰:深入 Word 核心,挖出那一坨 BLOB (含自省 Debug 技巧)
我們在處理自動化文件時,常會遇到一個需求:**把 Word 裡的圖片通通抓出來**。
這看似簡單,但如果你深入 `python-docx` 的底層,會發現這牽涉到對 Office Open XML (OOXML) 結構的理解、二進位資料 (BLOB) 的處理,以及如何優雅地處理錯誤。
本文將以 `extract_image_bytes` 這個函式為例,解析三個 Python 進階技巧:
1. **OOXML 關係遍歷** (Relationship Traversal)
2. **BLOB 的概念與命名哲學**
3. **Python 自省機制** (Introspection) 來優化 Log
—
## 1. 核心程式碼賞析
這是我們今天要解構的函式:
# %%
def extract_image_bytes(doc: DocxDocument, rid: str):
"""從 doc.part.rels 取得圖片 bytes"""
try:
# 1. 透過 Relationship ID (rId) 找到目標 Part
part = doc.part.rels[rid].target_part
# 2. 判斷它是不是圖片,是的話回傳 BLOB (一坨史萊姆)
# BLOB (Binary Large Object) - 也就是「一坨」像史萊姆的無定形團塊
return part.blob if "ImagePart" in type(part).__name__ else None
except (KeyError, AttributeError) as e:
# 3. 使用自省機制動態取得函式名稱
func_name = sys._getframe().f_code.co_name
print(f" [Warn] At {func_name}: Failed to extract image for rid={rid}\n Error: {e}")
return None## 2. 深入解析
### A. 什麼是 BLOB?為什麼叫它「史萊姆」?
在程式碼中,我們看到這行回傳:
return part.blob`blob` 是 **Binary Large Object** 的縮寫,在電腦科學中指「一塊大型的二進位資料」。
這段資料對人類來說是不可讀的亂碼,沒有固定的形狀。
因此,工程師門常戲稱它為 **”The Blob”** —— 就像 1958 年恐怖電影裡的黏液怪物,或 RPG 遊戲裡的**史萊姆**。它就只是一坨 bytes,直到你賦予它副檔名 (如 `.jpg`, `.png`),它才會變成你能理解的圖片。
### A.2. 逐步拆解:如何找到這坨 BLOB?
你可能會好奇 `doc.part.rels[rid].target_part` 這行程式碼這麼長,中間到底發生了什麼事?我們可以把它拆解成四個步驟:
1. **`doc.part` (Document Part)**:
`doc` 只是最外層的包裝,`doc.part` 才是真正的 OOXML 文件主體 (Main Document Part)。
2. **`.rels` (Relationships)**:
OOXML 是一個「關聯式」的結構。文件主體不會直接包含圖片,而是紀錄一張「藏寶圖」,告訴你「rId1 連接到圖片A」、「rId2 連接到樣式表」。`.rels` 就是這張藏寶圖。
3. **`[rid]` (Lookup)**:
我們拿著從 XML 標籤 (`<w:drawing r:id=”rId1″>`) 找到的 ID (`rId1`),去查這張藏寶圖,得到一個「關聯物件 (Relationship Object)」。這是一條線,連接著起點與終點。
4. **`.target_part` (The Treasure)**:
這條線的終點 (`target`),就是我們要找的寶藏——圖片本身 (`ImagePart`)。
### B. Pythonic 的一行流寫法 (One-liner)
return part.blob if "ImagePart" in type(part).__name__ else None這行展示了 Python 簡潔的 **Ternary Operator (三元運算子)**:
* **邏輯**:如果是圖片 -> 給你資料;如果不是 -> 給你 None。
* **字串檢查**:透過 `type(part).__name__` 我們可以拿到類別名稱字串 (例如 `’ImagePart’`),這比 `isinstance` 有時更適合處理在動態生成類別或不確定 import路徑的情況。
### C. 自省機制:讓程式知道「我是誰」
func_name = sys._getframe().f_code.co_name這是這段程式碼最精華的 Debug 技巧。通常我們寫 Log 會這樣寫:
`print(“Error in extract_image_bytes: …”)`
但如果以後函式改名了,Log 裡的字串通常會忘記改,造成誤導。
透過 `sys._getframe()` (取得當前 Stack Frame) -> `.f_code` (程式碼物件) -> `.co_name` (名稱),程式可以**動態抓取自己的名稱**。
不管你把函式名稱改成 `get_photo` 還是 `fetch_blob`,這行 Log 永遠都是正確的!
—
## 3. 實戰應用場景
這個函式通常配合 **Xpath** 一起使用。Word 的 XML 結構中,圖片藏在 `<w:drawing>` 標籤裡,裡面會有一個 `r:embed=”rId1″`。
我們的工作流程如下:
1. 用 Xpath 找出所有 `r:embed` 的 ID。
2. 丟進 `extract_image_bytes(doc, “rId1”)`。
3. 函式幫我們去 `.rels` 關係表中查找,抓出那一坨 **BLOB**。
4. 最後寫入硬碟,變成真正的圖檔。
—
## 總結
一段不到 10 行的 Python 程式碼,其實蘊含了對檔案結構的理解 (OOXML)、對資料本質的幽默 (BLOB/史萊姆),以及對程式維護性的堅持 (Introspection)。
下次當你處理二進位資料時,不妨也想像自己正在處理一坨可愛的史萊姆吧!
推薦hahow線上學習python: https://igrape.net/30afN