1. 程式人生 > >Python:讀取 .doc、.docx 兩種 Word 文件簡述及“Word 未能引發事件”錯誤

Python:讀取 .doc、.docx 兩種 Word 文件簡述及“Word 未能引發事件”錯誤

bug itext als htm 單獨 borde b+ compile http

Python 中可以讀取 word 文件的庫有 python-docx 和 pywin32。

優點 缺點
python-docx 跨平臺 只能處理 .docx 格式,不能處理.doc格式
pywin32 僅限 windows 平臺 .doc 和 .docx 都能處理

pywin32

這個庫很強大,不僅僅可以讀取 word,但是網上介紹用 pywin32 讀取 .doc 的文章真不多,因為,真心不好用。

以下是 pywin32 讀取 .doc 的代碼示例,但是讀取表格有問題,輸出全是空,原因不明,因為不打算用所以沒有深入研究。另外,如果表格中有縱向合並單元格,會報錯:“無法訪問此集合中單獨的行,因為表格有縱向合並的單元格。”

from win32com.client import Dispatch

word = Dispatch(Word.Application)     # 打開word應用程序
# word = DispatchEx(‘Word.Application‘) # 啟動獨立的進程
word.Visible = 0        # 後臺運行,不顯示
word.DisplayAlerts = 0  # 不警告

path = rE:\abc\test.doc
doc = word.Documents.Open(FileName=path, Encoding=gbk)

for para in
doc.paragraphs: print(para.Range.Text) for t in doc.Tables: for row in t.Rows: for cell in row.Cells: print(cell.Range.Text) doc.Close() word.Quit

但是 pywin32 有另外一個功能,就是將 .doc 格式另存為 .docx 格式,這樣我們就可以使用 python-docx 來處理了。

def doc2docx(path):
    w = win32com.client.Dispatch(
Word.Application) w.Visible = 0 w.DisplayAlerts = 0 doc = w.Documents.Open(path) newpath = os.path.splitext(path)[0] + .docx doc.SaveAs(newpath, 12, False, "", True, "", False, False, False, False) doc.Close() w.Quit() os.remove(path) return newpath

python-docx

import docx

fn = rE:\abc\test.docx
doc = docx.Document(fn)

for paragraph in doc.paragraphs:
        print(paragraph.text)

for table in doc.tables:
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)

對於縱向合並單元格,python-docx 的處理也很貼心。

技術分享圖片

技術分享圖片

Word 未能引發事件

我的爬蟲在爬取到 .doc 文件之後,就通過上面的方法將其轉為 .docx 格式,原本一切都好,下班掛機在跑,第二天來一看,報了這個錯:

技術分享圖片

我用報錯的文件單獨調試了 doc2docx 方法,並沒有報錯。網上查了這個錯誤,沒有啥收獲。

反復測試後發現總是那個網頁報錯,說明 bug 可以重現,問題是到底是哪裏報錯。

我將代碼一行行刪去,直到只留下執行到報錯所必須的代碼:

def get_winningbid_detail(url, name):
    r = requests.get(url)
    r.encoding = utf-8
    html = r.text
    soup = BeautifulSoup(html, lxml)

    ps = soup.find_all(text=re.compile(附件))
    if len(ps) > 0:
        os.makedirs(os.path.join(download_dir, name), exist_ok=True)
        for p in ps:
            a_tab = p.find_next_sibling(a)
            if a_tab is not None:
                link = homepage + a_tab[href]
                localfilename = os.path.join(download_dir, name, a_tab.text)
                # print(localfilename)
                with open(localfilename, wb+) as sw:
                    sw.write(requests.get(link).content)
                if localfilename.endswith(.doc):
                    doc2docx(localfilename)

反復讀這段代碼,並沒有發現什麽問題。

因為有些網頁的附件名稱是相同的,例如 公告.doc,所以我按每個網頁的標題(在總覽頁面爬到的)分文件夾放置下載的文件,所以方法中傳了一個 name 參數,而如果 name 參數傳空,則不會報錯。

其實由此已經可以發現 bug 所在了,但我卻沒想到,又反復折騰了很久才發現,原來是文件名太長了。

在windows下面,單個文件名的長度限制是255,完整的路徑長度(如 E:\abc\test.doc)這樣限制是260,一個漢字占2個字符。

路徑最後有一個字符串結束符 ‘\0‘ 要占掉一個字符,所以完整路徑實際限長是259。

Python:讀取 .doc、.docx 兩種 Word 文件簡述及“Word 未能引發事件”錯誤