最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)將Word文檔轉(zhuǎn)換為HTML

 更新時(shí)間:2025年12月23日 08:28:51   作者:東方佑  
在日常文檔處理中,我們經(jīng)常需要將Word文檔轉(zhuǎn)換為HTML格式以便在網(wǎng)頁(yè)上展示,本文將詳細(xì)解析一個(gè)使用純Python標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的Word到HTML轉(zhuǎn)換腳本,展示如何直接處理.docx文件的內(nèi)部結(jié)構(gòu),有需要的可以了解下

在日常文檔處理中,我們經(jīng)常需要將Word文檔轉(zhuǎn)換為HTML格式以便在網(wǎng)頁(yè)上展示。雖然存在多種第三方庫(kù)可以實(shí)現(xiàn)這一功能,但Python的標(biāo)準(zhǔn)庫(kù)同樣提供了強(qiáng)大而靈活的工具來(lái)處理.docx文件。本文將詳細(xì)解析一個(gè)使用純Python標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的Word到HTML轉(zhuǎn)換腳本,展示如何直接處理.docx文件的內(nèi)部結(jié)構(gòu)。

1. Word文檔格式基礎(chǔ):了解.docx文件本質(zhì)

.docx文件本質(zhì)上是一個(gè)ZIP壓縮包,其中包含多個(gè)XML文件和其他資源。這種格式基于Office Open XML標(biāo)準(zhǔn),使得我們可以通過(guò)解壓和解析XML來(lái)直接訪問(wèn)文檔內(nèi)容。與使用第三方庫(kù)如python-docx相比,直接使用標(biāo)準(zhǔn)庫(kù)處理.docx文件提供了更底層的控制能力,能夠?qū)崿F(xiàn)更精細(xì)的轉(zhuǎn)換控制。

腳本首先利用zipfile模塊解壓.docx文件,然后通過(guò)xml.etree.ElementTree解析XML結(jié)構(gòu)。這種方法不依賴任何外部依賴,保證了代碼的輕量性和可移植性。

2. 腳本核心功能解析

2.1 文檔結(jié)構(gòu)分析

腳本通過(guò)list_docx_contents函數(shù)列出.docx文件中的所有內(nèi)部文件,這些文件包括文檔主體、樣式、關(guān)系、媒體資源等。這種分析有助于理解Word文檔的復(fù)雜結(jié)構(gòu),為準(zhǔn)確提取內(nèi)容奠定基礎(chǔ)。

def list_docx_contents(filepath):
    """列出 Word 文檔中的所有文件"""
    with zipfile.ZipFile(filepath, 'r') as docx:
        return docx.namelist()

2.2 文本內(nèi)容提取

通過(guò)extract_text_from_docx函數(shù),腳本從word/document.xml中提取所有文本內(nèi)容。Word文檔使用特定的XML命名空間和結(jié)構(gòu)來(lái)存儲(chǔ)文本,腳本需要正確解析這些結(jié)構(gòu)才能準(zhǔn)確提取內(nèi)容。

2.3 文檔結(jié)構(gòu)解析

analyze_docx_structure函數(shù)提供了深入的文檔結(jié)構(gòu)分析,包括段落樣式、列表屬性、文本格式等。這一功能對(duì)于理解Word文檔的視覺(jué)層次結(jié)構(gòu)至關(guān)重要,是實(shí)現(xiàn)高質(zhì)量HTML轉(zhuǎn)換的基礎(chǔ)。

3. 轉(zhuǎn)換過(guò)程關(guān)鍵技術(shù)細(xì)節(jié)

3.1 段落處理與格式轉(zhuǎn)換

腳本的process_paragraph函數(shù)負(fù)責(zé)將Word段落轉(zhuǎn)換為相應(yīng)的HTML元素。它識(shí)別各種段落樣式(如標(biāo)題、正文、列表項(xiàng))并應(yīng)用對(duì)應(yīng)的HTML標(biāo)簽:

  • 標(biāo)題段落(Heading1-Heading6)轉(zhuǎn)換為<h1>-<h6>標(biāo)簽
  • 普通段落轉(zhuǎn)換為<p>標(biāo)簽
  • 列表項(xiàng)轉(zhuǎn)換為<li>標(biāo)簽,并自動(dòng)管理<ul><ol>父容器

此外,該函數(shù)還處理段落級(jí)格式設(shè)置,包括對(duì)齊方式、縮進(jìn)、行間距和背景色等。

3.2 文本格式處理

process_paragraph_formatting函數(shù)深入處理段落內(nèi)的豐富文本格式:

  • 字體樣式:識(shí)別粗體(<strong>)、斜體(<em>)、下劃線(<u>)和刪除線
  • 上下標(biāo):正確處理上標(biāo)(<sup>)和下標(biāo)(<sub>
  • 字體屬性:提取字體大小、顏色、高亮和字符間距
  • 特殊內(nèi)容:處理超鏈接、圖片、表單字段和修訂標(biāo)記(刪除和插入內(nèi)容)

這種精細(xì)的格式處理確保了轉(zhuǎn)換后的HTML能夠保留原文檔的視覺(jué)特征。

3.3 表格轉(zhuǎn)換

process_table函數(shù)處理Word表格的復(fù)雜結(jié)構(gòu),支持:

  • 基本表格結(jié)構(gòu)(<table><tr>、<td>
  • 合并單元格(跨行rowspan和跨列colspan)
  • 表頭識(shí)別(第一行自動(dòng)使用<th>標(biāo)簽)

該函數(shù)使用矩陣跟蹤法正確處理單元格合并關(guān)系,確保復(fù)雜表格結(jié)構(gòu)的準(zhǔn)確轉(zhuǎn)換。

3.4 圖片和超鏈接處理

腳本能夠提取文檔中的圖片并將其轉(zhuǎn)換為Base64編碼的HTML圖片標(biāo)簽,同時(shí)正確處理內(nèi)部和外部超鏈接:

def process_image(docx, image_id, doc_rels):
    """處理圖片"""
    # 查找圖片關(guān)系并轉(zhuǎn)換為Base64編碼
    # ...

def process_hyperlink(rel_id, text, doc_rels):
    """處理超鏈接"""
    # 查找關(guān)系中的目標(biāo)URL并生成<a>標(biāo)簽
    # ...

這種方法確保圖片和鏈接在HTML中正確顯示,即使圖片是嵌入在.docx文件中的。

4. 高級(jí)功能實(shí)現(xiàn)

4.1 頁(yè)眉頁(yè)腳處理

process_headers_footers函數(shù)單獨(dú)處理文檔的頁(yè)眉和頁(yè)腳內(nèi)容,將其轉(zhuǎn)換為具有特定樣式的HTML元素,保持文檔的完整結(jié)構(gòu)。

4.2 修訂標(biāo)記處理

腳本能夠識(shí)別和處理Word的修訂標(biāo)記(跟蹤更改),將刪除的內(nèi)容顯示為帶刪除線的文本,插入的內(nèi)容顯示為帶背景高亮,這在協(xié)作編輯環(huán)境中特別有用。

4.3 樣式應(yīng)用

生成的HTML包含完整的CSS樣式定義,確保轉(zhuǎn)換結(jié)果在瀏覽器中的顯示效果接近原Word文檔。腳本允許自定義樣式表,滿足不同的視覺(jué)需求。

5. 使用方法和輸出示例

運(yùn)行腳本非常簡(jiǎn)單,只需執(zhí)行主函數(shù)即可完成整個(gè)轉(zhuǎn)換過(guò)程:

def main():
    print("Word 文檔處理工具")
    print_docx_info('test_input.docx')  # 分析文檔結(jié)構(gòu)
    html_content = docx_to_html('test_input.docx')  # 轉(zhuǎn)換為HTML
    save_html('output.html', html_content)  # 保存結(jié)果

腳本會(huì)輸出文檔結(jié)構(gòu)分析、轉(zhuǎn)換進(jìn)度和結(jié)果文件路徑,提供完整的處理反饋。

6. 與其他方法的比較

與使用第三方庫(kù)(如python-docx、Apache POI等)相比,這種基于標(biāo)準(zhǔn)庫(kù)的方法具有獨(dú)特優(yōu)勢(shì):

  • 零依賴:僅使用Python標(biāo)準(zhǔn)庫(kù),無(wú)需安裝額外包
  • 深度控制:直接訪問(wèn).docx文件內(nèi)部結(jié)構(gòu),可實(shí)現(xiàn)高度定制化轉(zhuǎn)換
  • 學(xué)習(xí)價(jià)值:深入了解Word文檔的底層結(jié)構(gòu)和Open XML標(biāo)準(zhǔn)

然而,這種方法也需要更多的代碼量和對(duì)XML結(jié)構(gòu)的深入理解,在開(kāi)發(fā)效率上可能不如使用成熟的第三方庫(kù)。

7. 實(shí)際應(yīng)用場(chǎng)景

這種Word到HTML轉(zhuǎn)換技術(shù)在多個(gè)領(lǐng)域有廣泛應(yīng)用:

  • 內(nèi)容管理系統(tǒng):將Word格式的內(nèi)容轉(zhuǎn)換為網(wǎng)頁(yè)展示
  • 文檔自動(dòng)化:批量處理大量Word文檔并發(fā)布為網(wǎng)頁(yè)
  • 協(xié)作平臺(tái):保留修訂記錄和評(píng)論的文檔轉(zhuǎn)換
  • 電子郵件模板:將Word設(shè)計(jì)的模板轉(zhuǎn)換為HTML郵件格式

8. 總結(jié)與擴(kuò)展建議

本文詳細(xì)解析了使用Python標(biāo)準(zhǔn)庫(kù)將Word文檔轉(zhuǎn)換為HTML的完整實(shí)現(xiàn)。這種方法不依賴任何第三方庫(kù),通過(guò)直接解析.docx文件的ZIP和XML結(jié)構(gòu),實(shí)現(xiàn)了高質(zhì)量的文檔轉(zhuǎn)換。

腳本的主要優(yōu)點(diǎn)包括完整的格式支持、精細(xì)的轉(zhuǎn)換控制和零外部依賴。如果需要進(jìn)一步擴(kuò)展功能,可以考慮添加對(duì)更復(fù)雜元素(如圖表、公式、注釋)的支持,或優(yōu)化輸出HTML的樣式和響應(yīng)式設(shè)計(jì)。

通過(guò)深入理解這一實(shí)現(xiàn),開(kāi)發(fā)者可以更好地掌握Word文檔處理的底層原理,并根據(jù)具體需求定制更專業(yè)的文檔轉(zhuǎn)換解決方案。

9.完整代碼

# Word 文檔處理腳本
# 使用 Python 標(biāo)準(zhǔn)庫(kù)處理 .docx 文件并轉(zhuǎn)換為 HTML

import zipfile
import xml.etree.ElementTree as ET
import html
import base64
import os


def list_docx_contents(filepath):
    """列出 Word 文檔中的所有文件"""
    with zipfile.ZipFile(filepath, 'r') as docx:
        return docx.namelist()


def extract_text_from_docx(filepath):
    """從 Word 文檔中提取文本內(nèi)容"""
    with zipfile.ZipFile(filepath) as docx:
        # 讀取 document.xml
        xml_content = docx.read('word/document.xml')
        tree = ET.fromstring(xml_content)
        
        # 定義命名空間
        namespaces = {
            'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
        }
        
        # 提取所有段落文本
        paragraphs = tree.findall('.//w:p', namespaces)
        text_parts = []
        
        for para in paragraphs:
            # 提取段落中所有文本
            texts = para.findall('.//w:t', namespaces)
            para_text = ''.join([t.text for t in texts if t.text])
            if para_text:
                text_parts.append(para_text)
        
        return '\n'.join(text_parts)


def analyze_docx_structure(filepath):
    """分析 Word 文檔結(jié)構(gòu)"""
    with zipfile.ZipFile(filepath) as docx:
        # 讀取 document.xml
        xml_content = docx.read('word/document.xml')
        tree = ET.fromstring(xml_content)
        
        # 定義命名空間
        namespaces = {
            'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
        }
        
        # 分析段落
        paragraphs = tree.findall('.//w:p', namespaces)
        print(f"總共找到 {len(paragraphs)} 個(gè)段落")

        for i, para in enumerate(paragraphs[:20]):  # 分析前20個(gè)段落
            num_pr = para.find('.//w:numPr', namespaces)
            p_style_elem = para.find('.//w:pStyle', namespaces)
            p_style = p_style_elem.get(f'{{{namespaces["w"]}}}val') if p_style_elem is not None else None

            # 檢查段落中是否有加粗文本
            bold_runs = []
            for run in para.findall('.//w:r', namespaces):
                text_elem = run.find('.//w:t', namespaces)
                bold_elem = run.find('.//w:b', namespaces)
                if text_elem is not None and text_elem.text:
                    bold_runs.append((text_elem.text, bold_elem is not None))

            # 提取文本
            texts = [r.find('.//w:t', namespaces) for r in para.findall('.//w:r', namespaces)]
            para_text = ''.join([t.text for t in texts if t is not None and t.text])

            # 檢查是否有加粗文本
            has_bold = any(bold for _, bold in bold_runs)
            
            # 顯示哪些具體文本是加粗的
            bold_texts = [text for text, is_bold in bold_runs if is_bold]
            
            print(f"段落 {i}: 樣式={p_style}, 列表屬性={num_pr is not None}, 有加粗={has_bold}, 加粗文本={bold_texts}, 文本='{para_text[:50]}{'...' if len(para_text) > 50 else ''}'")


def process_hyperlink(rel_id, text, doc_rels):
    """處理超鏈接"""
    # 查找關(guān)系中的目標(biāo)URL
    target_url = None
    for rel in doc_rels.findall('.//Relationship'):
        if rel.get('Id') == rel_id:
            target_url = rel.get('Target')
            break
    
    if target_url:
        return f'<a href="{html.escape(target_url)}" rel="external nofollow" >{html.escape(text)}</a>'
    else:
        return html.escape(text)


def process_image(docx, image_id, doc_rels):
    """處理圖片"""
    # 查找圖片關(guān)系
    image_path = None
    for rel in doc_rels.findall('.//Relationship'):
        if rel.get('Id') == image_id:
            image_path = rel.get('Target')
            break
    
    if not image_path:
        return '<!-- 圖片未找到 -->'
    
    # 規(guī)范化路徑(處理類似"../media/image1.png"的相對(duì)路徑)
    if image_path.startswith('..'):
        image_path = image_path[3:]  # 移除 "../" 前綴
    
    try:
        # 從docx文件中讀取圖片數(shù)據(jù)
        image_data = docx.read(f'word/{image_path}')
        # 將圖片編碼為base64
        encoded_image = base64.b64encode(image_data).decode('utf-8')
        
        # 獲取圖片擴(kuò)展名以確定MIME類型
        ext = os.path.splitext(image_path)[1].lower()
        mime_type = 'image/png'  # 默認(rèn)
        if ext == '.jpg' or ext == '.jpeg':
            mime_type = 'image/jpeg'
        elif ext == '.gif':
            mime_type = 'image/gif'
        elif ext == '.bmp':
            mime_type = 'image/bmp'
        elif ext == '.svg':
            mime_type = 'image/svg+xml'
        
        return f'<img src="data:{mime_type};base64,{encoded_image}" alt="文檔圖片">'
    except Exception as e:
        return f'<!-- 圖片加載失敗: {str(e)} -->'


def process_paragraph_formatting(para, namespaces, docx=None, doc_rels=None):
    """處理段落內(nèi)的格式化文本(粗體、斜體、顏色等)"""
    html_text = ""
    last_text = ""  # 用于跟蹤上一個(gè)文本,避免重復(fù)
    
    # 遍歷段落中的所有文本元素
    for run in para.findall('.//w:r', namespaces):
        # 檢查是否有刪除線文本(修訂模式下的刪除內(nèi)容)
        del_text_elem = run.find('.//w:delText', namespaces)
        if del_text_elem is not None and del_text_elem.text is not None:
            # 處理刪除的文本(帶刪除線)
            del_text = del_text_elem.text
            escaped_text = html.escape(del_text)
            html_text += f'<span style="text-decoration:line-through;">{escaped_text}</span>'
            continue
            
        # 檢查是否有插入的文本(修訂模式下的新增內(nèi)容)
        ins_elem = run.find('.//w:ins', namespaces)
        if ins_elem is not None:
            # 獲取插入的文本
            ins_texts = ins_elem.findall('.//w:t', namespaces)
            for ins_text_elem in ins_texts:
                if ins_text_elem.text:
                    ins_text = ins_text_elem.text
                    escaped_text = html.escape(ins_text)
                    html_text += f'<span style="background-color:#ccffcc;">{escaped_text}</span>'
            continue
            
        # 提取文本
        text_elem = run.find('.//w:t', namespaces)
        if text_elem is None or text_elem.text is None:
            # 檢查是否是制表符
            if run.find('.//w:tab', namespaces) is not None:
                html_text += "&emsp;"  # 處理制表符
                continue
                
            # 檢查是否是圖片
            drawing_elem = run.find('.//w:drawing', namespaces)
            if drawing_elem is not None and docx is not None and doc_rels is not None:
                # 處理圖片
                blip_elem = drawing_elem.find('.//a:blip', {
                    'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'
                })
                if blip_elem is not None:
                    embed_id = blip_elem.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
                    if embed_id:
                        img_html = process_image(docx, embed_id, doc_rels)
                        html_text += img_html
                continue
                
            # 檢查是否是下劃線字符(空的文本但有下劃線格式)
            underline_elem = run.find('.//w:u', namespaces)
            if underline_elem is not None:
                # 獲取下劃線類型
                underline_val = underline_elem.get(f'{{{namespaces["w"]}}}val')
                # 添加表單字段占位符
                html_text += '<span class="form-field">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>'
            continue
            
        text = text_elem.text
        
        # 檢查是否是重復(fù)文本(有時(shí)候Word中會(huì)有重復(fù)的文本節(jié)點(diǎn))
        if text == last_text:
            continue
            
        last_text = text
        escaped_text = html.escape(text)
        
        # 檢查超鏈接
        hyperlink_elem = run.find('.//w:hyperlink', namespaces)
        if hyperlink_elem is not None and doc_rels is not None:
            rel_id = hyperlink_elem.get(f'{{{namespaces["w"]}}}anchor') or hyperlink_elem.get(f'{{{namespaces["w"]}}}id')
            if rel_id:
                hyperlink_html = process_hyperlink(rel_id, text, doc_rels)
                html_text += hyperlink_html
                continue
        
        # 檢查格式
        bold_elem = run.find('.//w:b', namespaces)
        bold = False
        if bold_elem is not None:
            # 檢查是否明確設(shè)置了val屬性為false
            val = bold_elem.get(f'{{{namespaces["w"]}}}val')
            if val is None or val != 'false':
                bold = True
        
        # 檢查字體,如果使用黑體等加粗字體,也應(yīng)視為加粗
        rpr_elem = run.find('.//w:rPr', namespaces)
        if rpr_elem is not None:
            # 檢查字體設(shè)置
            font_elem = rpr_elem.find('.//w:rFonts', namespaces)
            if font_elem is not None:
                # 獲取各種字體屬性
                ascii_font = font_elem.get(f'{{{namespaces["w"]}}}ascii')
                hansi_font = font_elem.get(f'{{{namespaces["w"]}}}hAnsi')
                east_asia_font = font_elem.get(f'{{{namespaces["w"]}}}eastAsia')
                
                # 如果使用黑體等加粗字體,視為加粗
                bold_fonts = ['黑體', 'Bold', 'Arial Black', 'Impact']
                if (ascii_font and any(bf in ascii_font for bf in bold_fonts)) or \
                   (hansi_font and any(bf in hansi_font for bf in bold_fonts)) or \
                   (east_asia_font and any(bf in east_asia_font for bf in bold_fonts)):
                    bold = True
                
        italic_elem = run.find('.//w:i', namespaces)
        italic = False
        if italic_elem is not None:
            val = italic_elem.get(f'{{{namespaces["w"]}}}val')
            if val is None or val != 'false':
                italic = True
                
        underline_elem = run.find('.//w:u', namespaces)
        underline = False
        if underline_elem is not None:
            val = underline_elem.get(f'{{{namespaces["w"]}}}val')
            if val is None or val != 'none':
                underline = True
                
        strike_elem = run.find('.//w:strike', namespaces)
        strike = False
        if strike_elem is not None:
            val = strike_elem.get(f'{{{namespaces["w"]}}}val')
            if val is None or val != 'false':
                strike = True
        
        # 檢查上下標(biāo)
        vert_align_elem = run.find('.//w:vertAlign', namespaces)
        vert_align = None
        if vert_align_elem is not None:
            align_val = vert_align_elem.get(f'{{{namespaces["w"]}}}val')
            if align_val == 'superscript':
                vert_align = 'sup'
            elif align_val == 'subscript':
                vert_align = 'sub'
        
        # 檢查字體大小
        sz_elem = run.find('.//w:sz', namespaces)
        font_size = None
        if sz_elem is not None:
            sz_val = sz_elem.get(f'{{{namespaces["w"]}}}val')
            if sz_val:
                # Word中的字號(hào)是半點(diǎn)為單位,需要轉(zhuǎn)換為pt
                font_size = f"{int(sz_val)/2}pt"
        
        # 檢查高亮色
        highlight_elem = run.find('.//w:highlight', namespaces)
        background_color = None
        if highlight_elem is not None:
            highlight_val = highlight_elem.get(f'{{{namespaces["w"]}}}val')
            # 需要將Word顏色名稱映射為CSS顏色值
            color_map = {
                'yellow': '#ffff00',
                'green': '#00ff00',
                'red': '#ff0000',
                'blue': '#0000ff',
                'cyan': '#00ffff',
                'magenta': '#ff00ff',
                'black': '#000000',
                'white': '#ffffff'
            }
            background_color = color_map.get(highlight_val, highlight_val)
        
        # 檢查顏色
        color_elem = run.find('.//w:color', namespaces)
        color = None
        if color_elem is not None:
            color_val = color_elem.get(f'{{{namespaces["w"]}}}val')
            if color_val and color_val != '000000':  # 忽略黑色(默認(rèn)顏色)
                # 處理自動(dòng)顏色
                if color_val.lower() == 'auto':
                    color = 'inherit'
                else:
                    color = f"#{color_val}"
        
        # 檢查字符間距
        spacing_elem = run.find('.//w:spacing', namespaces)
        letter_spacing = None
        if spacing_elem is not None:
            spacing_val = spacing_elem.get(f'{{{namespaces["w"]}}}val')
            if spacing_val:
                # Word中字符間距單位是 twentieths of a point (1/1440 inch)
                # 轉(zhuǎn)換為CSS的em單位
                spacing_em = int(spacing_val) / 120  # 120 = 20 * 6 (approximation)
                if spacing_em != 0:
                    letter_spacing = f"{spacing_em:.2f}em"
        
        # 檢查背景色
        shd_elem = rpr_elem.find('.//w:shd', namespaces) if rpr_elem is not None else None
        shading_color = None
        if shd_elem is not None:
            shd_fill = shd_elem.get(f'{{{namespaces["w"]}}}fill')
            if shd_fill and shd_fill != 'auto' and shd_fill != 'clear':
                shading_color = f"#{shd_fill}"
        
        # 構(gòu)建樣式字符串
        styles = []
        if font_size:
            styles.append(f"font-size:{font_size}")
        if background_color:
            styles.append(f"background-color:{background_color}")
        if color and color != 'inherit':
            styles.append(f"color:{color}")
        elif color == 'inherit':
            styles.append(f"color:{color}")
        if letter_spacing:
            styles.append(f"letter-spacing:{letter_spacing}")
        if shading_color:
            styles.append(f"background-color:{shading_color}")
        
        style_attr = f' style="{";".join(styles)}"' if styles else ''
        
        # 應(yīng)用格式
        if bold:
            escaped_text = f'<strong>{escaped_text}</strong>'
        if italic:
            escaped_text = f'<em>{escaped_text}</em>'
        if underline:
            escaped_text = f'<u>{escaped_text}</u>'  # 使用標(biāo)準(zhǔn)HTML下劃線標(biāo)簽
        if strike:
            escaped_text = f'<span style="text-decoration:line-through">{escaped_text}</span>'
        if vert_align:
            escaped_text = f'<{vert_align}>{escaped_text}</{vert_align}>'
        if style_attr:
            escaped_text = f'<span{style_attr}>{escaped_text}</span>'
            
        html_text += escaped_text
    
    return html_text


def process_paragraph(p, namespaces, docx=None, doc_rels=None):
    """處理段落"""
    # 獲取段落屬性
    p_pr = p.find('./w:pPr', namespaces)
    
    # 檢查段落樣式
    p_style_elem = p_pr.find('.//w:pStyle', namespaces) if p_pr is not None else None
    p_style = p_style_elem.get(f'{{{namespaces["w"]}}}val') if p_style_elem is not None else None
    
    # 如果是標(biāo)題樣式,直接返回標(biāo)題標(biāo)簽
    if p_style and p_style.startswith('Heading'):
        # 處理段落中的文本
        formatted_text = process_paragraph_formatting(p, namespaces, docx, doc_rels)
        
        # 根據(jù)樣式確定標(biāo)題級(jí)別
        heading_level = 1
        if p_style == 'Heading1':
            heading_level = 1
        elif p_style == 'Heading2':
            heading_level = 2
        elif p_style == 'Heading3':
            heading_level = 3
        elif p_style == 'Heading4':
            heading_level = 4
        elif p_style == 'Heading5':
            heading_level = 5
        elif p_style == 'Heading6':
            heading_level = 6
            
        return f'<h{heading_level}>{formatted_text}</h{heading_level}>'
    
    # 處理段落樣式
    style_attrs = []
    
    # 處理文本對(duì)齊
    jc_elem = p_pr.find('.//w:jc', namespaces) if p_pr is not None else None
    if jc_elem is not None:
        jc_val = jc_elem.get(f'{{{namespaces["w"]}}}val')
        if jc_val == 'center':
            style_attrs.append('text-align:center')
        elif jc_val == 'right':
            style_attrs.append('text-align:right')
        elif jc_val == 'both':
            style_attrs.append('text-align:justify')
        else:
            style_attrs.append('text-align:left')
    else:
        style_attrs.append('text-align:left')
    
    # 處理段落縮進(jìn)
    ind_elem = p_pr.find('.//w:ind', namespaces) if p_pr is not None else None
    if ind_elem is not None:
        # 左縮進(jìn)
        left_ind = ind_elem.get(f'{{{namespaces["w"]}}}left')
        if left_ind:
            # Word中單位為twips,1 twip = 1/1440 inch,約等于1/20 pt
            style_attrs.append(f'margin-left:{int(left_ind)/20}pt')
        
        # 首行縮進(jìn)
        first_line_ind = ind_elem.get(f'{{{namespaces["w"]}}}firstLine')
        if first_line_ind:
            style_attrs.append(f'text-indent:{int(first_line_ind)/20}pt')
        
        # 懸掛縮進(jìn)
        hanging_ind = ind_elem.get(f'{{{namespaces["w"]}}}hanging')
        if hanging_ind:
            # 懸掛縮進(jìn)需要特殊處理
            style_attrs.append(f'text-indent:-{int(hanging_ind)/20}pt;padding-left:{int(hanging_ind)/20}pt')
    
    # 處理行間距
    spacing_elem = p_pr.find('.//w:spacing', namespaces) if p_pr is not None else None
    if spacing_elem is not None:
        # 行間距
        line_val = spacing_elem.get(f'{{{namespaces["w"]}}}line')
        if line_val:
            # Word中的行距單位是twips (1/20 pt)
            # 通常240 twips = 12 pt = 單倍行距
            line_height = int(line_val) / 240
            if line_height > 0:
                # 避免重復(fù)添加line-height樣式
                existing_line_height = any(attr.startswith('line-height:') for attr in style_attrs)
                if not existing_line_height:
                    style_attrs.append(f'line-height:{line_height}em')
        
        # 行距規(guī)則
        line_rule = spacing_elem.get(f'{{{namespaces["w"]}}}lineRule')
        if line_rule == 'auto':
            # 自動(dòng)行距已在上面處理
            pass
        elif line_rule == 'atLeast':
            # 最小行距
            line_val = spacing_elem.get(f'{{{namespaces["w"]}}}line')
            if line_val:
                min_line_height = int(line_val) / 240
                # 避免重復(fù)添加line-height樣式
                existing_line_height = any(attr.startswith('line-height:') for attr in style_attrs)
                if not existing_line_height:
                    style_attrs.append(f'line-height:min({min_line_height}em)')
        elif line_rule == 'exact':
            # 固定行距
            line_val = spacing_elem.get(f'{{{namespaces["w"]}}}line')
            if line_val:
                fixed_line_height = int(line_val) / 240
                # 避免重復(fù)添加line-height樣式
                existing_line_height = any(attr.startswith('line-height:') for attr in style_attrs)
                if not existing_line_height:
                    style_attrs.append(f'line-height:{fixed_line_height}em')
    
    # 處理段落底紋/背景色
    shd_elem = p_pr.find('.//w:shd', namespaces) if p_pr is not None else None
    if shd_elem is not None:
        shd_fill = shd_elem.get(f'{{{namespaces["w"]}}}fill')
        if shd_fill and shd_fill != 'auto' and shd_fill != 'clear':
            style_attrs.append(f'background-color:#{shd_fill}')
    
    style_str = f' style="{";".join(style_attrs)}"' if style_attrs else ''
    
    # 處理段落中的文本
    formatted_text = process_paragraph_formatting(p, namespaces, docx, doc_rels)
    
    # 檢查是否為列表項(xiàng)
    num_pr = p_pr.find('.//w:numPr', namespaces) if p_pr is not None else None
    if num_pr is not None:
        # 獲取編號(hào)信息以確定是有序還是無(wú)序列表
        num_id_elem = num_pr.find('.//w:numId', namespaces)
        ilvl_elem = num_pr.find('.//w:ilvl', namespaces)
        
        # 默認(rèn)使用無(wú)序列表
        list_type = 'ul'
        # 如果有編號(hào)ID,則嘗試判斷是否為有序列表
        if num_id_elem is not None:
            num_id_val = num_id_elem.get(f'{{{namespaces["w"]}}}val')
            # 可以根據(jù)num_id_val的值來(lái)判斷是否為有序列表
            # 這里簡(jiǎn)化處理,默認(rèn)認(rèn)為所有列表都是無(wú)序列表
            # 實(shí)際項(xiàng)目中可以從word/numbering.xml中獲取詳細(xì)信息
            
        return f'<li{style_str}>{formatted_text}</li>'
    else:
        return f'<p{style_str}>{formatted_text}</p>'


def process_table(table, namespaces, docx=None, doc_rels=None):
    """處理表格,支持合并單元格等復(fù)雜結(jié)構(gòu)"""
    html_parts = ['<table>']
    
    # 處理表格行
    rows = table.findall('.//w:tr', namespaces)
    
    # 創(chuàng)建一個(gè)矩陣來(lái)跟蹤已被占用的單元格位置,用于處理跨行合并
    cell_matrix = [[False for _ in range(20)] for _ in range(len(rows))]  # 假設(shè)最多20列
    
    for i, row in enumerate(rows):
        html_parts.append('<tr>')
        
        # 處理單元格
        cells = row.findall('.//w:tc', namespaces)
        
        cell_index = 0  # 當(dāng)前行的實(shí)際單元格索引
        matrix_col = 0  # 在矩陣中的列位置
        
        # 找到下一個(gè)可用的列位置
        while matrix_col < len(cell_matrix[i]) and cell_matrix[i][matrix_col]:
            matrix_col += 1
            
        for cell in cells:
            # 跳過(guò)已經(jīng)被前面的跨行單元格占據(jù)的位置
            while matrix_col < len(cell_matrix[i]) and cell_matrix[i][matrix_col]:
                matrix_col += 1
                
            # 確定是表頭還是普通單元格
            cell_tag = 'th' if i == 0 else 'td'
            
            # 檢查單元格合并屬性
            tc_pr = cell.find('.//w:tcPr', namespaces)
            colspan = ''
            rowspan = ''
            
            if tc_pr is not None:
                # 檢查水平合并 (gridSpan)
                grid_span = tc_pr.find('.//w:gridSpan', namespaces)
                if grid_span is not None:
                    span_val = grid_span.get(f'{{{namespaces["w"]}}}val')
                    if span_val:
                        colspan = f' colspan="{span_val}"'
                        # 標(biāo)記這些列位置已被占用
                        span_count = int(span_val)
                        for col_offset in range(span_count):
                            if matrix_col + col_offset < len(cell_matrix[i]):
                                cell_matrix[i][matrix_col + col_offset] = True
                
                # 檢查垂直合并 (vMerge)
                v_merge = tc_pr.find('.//w:vMerge', namespaces)
                if v_merge is not None:
                    merge_val = v_merge.get(f'{{{namespaces["w"]}}}val')
                    # 只有當(dāng)是合并起點(diǎn)時(shí)才添加 rowspan
                    if merge_val == 'restart':
                        # 查找跨越的行數(shù)
                        rowspan_count = 1
                        # 計(jì)算需要合并多少行
                        for next_row_idx in range(i + 1, len(rows)):
                            next_row = rows[next_row_idx]
                            next_cells = next_row.findall('.//w:tc', namespaces)
                            
                            # 檢查下一行對(duì)應(yīng)位置是否有繼續(xù)合并的標(biāo)記
                            # 先找到下一行中對(duì)應(yīng)位置的單元格
                            next_cell_found = False
                            next_cell_index = 0
                            current_col_pos = 0
                            
                            # 計(jì)算在下一行中對(duì)應(yīng)列位置的單元格
                            for next_cell in next_cells:
                                next_tc_pr = next_cell.find('.//w:tcPr', namespaces)
                                if next_tc_pr is not None:
                                    next_grid_span = next_tc_pr.find('.//w:gridSpan', namespaces)
                                    next_colspan = 1
                                    if next_grid_span is not None:
                                        next_span_val = next_grid_span.get(f'{{{namespaces["w"]}}}val')
                                        if next_span_val:
                                            next_colspan = int(next_span_val)
                                    
                                    # 檢查當(dāng)前位置是否是我們尋找的位置
                                    if current_col_pos == matrix_col:
                                        # 檢查這個(gè)單元格是否是繼續(xù)合并的單元格
                                        next_v_merge = next_tc_pr.find('.//w:vMerge', namespaces)
                                        if next_v_merge is not None:
                                            next_merge_val = next_v_merge.get(f'{{{namespaces["w"]}}}val')
                                            if next_merge_val is None:  # 繼續(xù)合并
                                                rowspan_count += 1
                                                # 標(biāo)記這個(gè)位置被跨行單元格占據(jù)
                                                for col_offset in range(next_colspan):
                                                    if matrix_col + col_offset < len(cell_matrix[next_row_idx]):
                                                        cell_matrix[next_row_idx][matrix_col + col_offset] = True
                                                next_cell_found = True
                                                break
                                            else:
                                                next_cell_found = True
                                                break
                                        else:
                                            # 沒(méi)有vMerge標(biāo)記,說(shuō)明合并結(jié)束
                                            next_cell_found = True
                                            break
                                
                                current_col_pos += next_colspan
                            
                            if not next_cell_found:
                                # 如果沒(méi)找到對(duì)應(yīng)的單元格,可能是被跨列占據(jù)了位置
                                # 檢查這個(gè)位置是否被標(biāo)記為已占用
                                if matrix_col < len(cell_matrix[next_row_idx]) and not cell_matrix[next_row_idx][matrix_col]:
                                    # 位置未被占用,說(shuō)明合并結(jié)束
                                    break
                                else:
                                    # 位置被占用,繼續(xù)檢查下一行
                                    rowspan_count += 1
                                    # 標(biāo)記這個(gè)位置被跨行單元格占據(jù)
                                    if matrix_col < len(cell_matrix[next_row_idx]):
                                        cell_matrix[next_row_idx][matrix_col] = True
                            elif next_cell_found:
                                next_v_merge = None
                                if next_cell_index < len(next_cells):
                                    next_tc_pr = next_cells[next_cell_index].find('.//w:tcPr', namespaces)
                                    if next_tc_pr is not None:
                                        next_v_merge = next_tc_pr.find('.//w:vMerge', namespaces)
                                
                                if next_v_merge is not None:
                                    next_merge_val = next_v_merge.get(f'{{{namespaces["w"]}}}val')
                                    if next_merge_val is None:  # 繼續(xù)合并
                                        rowspan_count += 1
                                        # 標(biāo)記這個(gè)位置被跨行單元格占據(jù)
                                        if matrix_col < len(cell_matrix[next_row_idx]):
                                            cell_matrix[next_row_idx][matrix_col] = True
                                    else:
                                        break
                                else:
                                    break
                        
                        if rowspan_count > 1:
                            rowspan = f' rowspan="{rowspan_count}"'
                    else:
                        # 不是合并起點(diǎn),跳過(guò)這個(gè)單元格
                        cell_index += 1
                        continue
                else:
                    # 標(biāo)記當(dāng)前位置被使用
                    if matrix_col < len(cell_matrix[i]):
                        cell_matrix[i][matrix_col] = True
            
            # 如果沒(méi)有設(shè)置rowspan,也要標(biāo)記當(dāng)前位置被使用
            if not rowspan and matrix_col < len(cell_matrix[i]):
                cell_matrix[i][matrix_col] = True
            
            # 提取單元格中的段落
            cell_paragraphs = cell.findall('.//w:p', namespaces)
            cell_content = ""
            
            for para in cell_paragraphs:
                para_content = process_paragraph_formatting(para, namespaces, docx, doc_rels)
                if para_content:
                    cell_content += para_content + "<br>" if cell_content else para_content
            
            # 如果沒(méi)有內(nèi)容,添加空白字符以確保單元格可見(jiàn)
            if not cell_content:
                cell_content = "&nbsp;"
            
            html_parts.append(f'<{cell_tag}{colspan}{rowspan}>{cell_content}</{cell_tag}>')
            
            cell_index += 1
            matrix_col += 1
            
            # 調(diào)整matrix_col到下一個(gè)可用位置
            while matrix_col < len(cell_matrix[i]) and cell_matrix[i][matrix_col]:
                matrix_col += 1
        
        html_parts.append('</tr>')
    
    html_parts.append('</table>')
    return '\n'.join(html_parts)


def process_textbox(textBox, namespaces, docx=None, doc_rels=None):
    """處理文本框"""
    # 文本框本質(zhì)上是一個(gè)容器,包含段落或其他元素
    html_parts = ['<div class="textbox">']
    
    # 處理文本框中的所有段落
    paragraphs = textBox.findall('.//w:p', namespaces)
    for p in paragraphs:
        html_parts.append(process_paragraph(p, namespaces, docx, doc_rels))
    
    html_parts.append('</div>')
    return '\n'.join(html_parts)


def process_headers_footers(docx, namespaces, doc_rels):
    """處理頁(yè)眉和頁(yè)腳"""
    header_content = []
    footer_content = []
    
    # 嘗試讀取頁(yè)眉
    try:
        header_files = [f for f in docx.namelist() if f.startswith('word/header') and f.endswith('.xml')]
        for header_file in header_files:
            header_xml = docx.read(header_file)
            header_tree = ET.fromstring(header_xml)
            # 處理頁(yè)眉中的段落
            paragraphs = header_tree.findall('.//w:p', namespaces)
            for p in paragraphs:
                header_text = process_paragraph(p, namespaces, docx, doc_rels)
                if header_text.strip():
                    header_content.append(header_text)
    except:
        pass  # 頁(yè)眉處理失敗時(shí)忽略
    
    # 嘗試讀取頁(yè)腳
    try:
        footer_files = [f for f in docx.namelist() if f.startswith('word/footer') and f.endswith('.xml')]
        for footer_file in footer_files:
            footer_xml = docx.read(footer_file)
            footer_tree = ET.fromstring(footer_xml)
            # 處理頁(yè)腳中的段落
            paragraphs = footer_tree.findall('.//w:p', namespaces)
            for p in paragraphs:
                footer_text = process_paragraph(p, namespaces, docx, doc_rels)
                if footer_text.strip():
                    footer_content.append(footer_text)
    except:
        pass  # 頁(yè)腳處理失敗時(shí)忽略
    
    return header_content, footer_content


def process_revisions(tree, namespaces):
    """處理修訂(刪除和插入的內(nèi)容)"""
    revision_content = []
    
    # 處理刪除的內(nèi)容
    del_elements = tree.findall('.//w:del', namespaces)
    for del_elem in del_elements:
        del_texts = del_elem.findall('.//w:delText', namespaces)
        for del_text in del_texts:
            if del_text.text:
                revision_content.append(f'<span style="text-decoration:line-through;background-color:#ffcccc;">{html.escape(del_text.text)}</span>')
    
    # 處理插入的內(nèi)容
    ins_elements = tree.findall('.//w:ins', namespaces)
    for ins_elem in ins_elements:
        ins_texts = ins_elem.findall('.//w:t', namespaces)
        for ins_text in ins_texts:
            if ins_text.text:
                revision_content.append(f'<span style="background-color:#ccffcc;">{html.escape(ins_text.text)}</span>')
    
    return revision_content


def docx_to_html(filepath):
    """將 Word 文檔轉(zhuǎn)換為 HTML"""
    with zipfile.ZipFile(filepath) as docx:
        # 讀取 document.xml
        xml_content = docx.read('word/document.xml')
        tree = ET.fromstring(xml_content)
        
        # 讀取關(guān)系文件
        try:
            rels_content = docx.read('word/_rels/document.xml.rels')
            doc_rels = ET.fromstring(rels_content)
        except:
            doc_rels = None
        
        # 定義命名空間
        namespaces = {
            'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
            'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
            'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
        }
        
        # 創(chuàng)建 HTML 結(jié)構(gòu)
        html_output = ['<!DOCTYPE html>', '<html>', '<head>', '<meta charset="UTF-8">', 
                '<title>Word 文檔轉(zhuǎn)換</title>',
                '<style>',
                'body { font-family: sans-serif; margin: 40px auto; max-width: 800px; line-height: 1.6; }',
                'h1 { color: #333; border-bottom: 2px solid #eee; padding-bottom: 10px; }',
                'h2 { color: #666; border-bottom: 1px solid #eee; padding-bottom: 5px; }',
                'h3 { color: #999; }',
                'h4 { color: #333; }',
                'h5 { color: #666; }',
                'h6 { color: #999; }',
                'p { margin: 1em 0; }',
                'ul, ol { margin: 1em 0; padding-left: 40px; }',
                'li { margin: 0.5em 0; }',
                'table { border-collapse: collapse; width: 100%; margin: 1em 0; }',
                'td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }',
                'strong { font-weight: bold; }',
                'em { font-style: italic; }',
                '.bold { font-weight: bold; }',
                '.italic { font-style: italic; }',
                'u { text-decoration: underline; }',
                '.underline { text-decoration: underline; }',
                'img { max-width: 100%; height: auto; }',
                '.header, .footer { background-color: #f5f5f5; padding: 10px; margin: 10px 0; border: 1px solid #ddd; }',
                '.header { border-bottom: 2px solid #ccc; }',
                '.footer { border-top: 2px solid #ccc; }',
                '.textbox { border: 1px solid #ccc; padding: 10px; margin: 10px 0; }',
                '.floating-element { position: relative; float: right; margin: 10px 0 10px 10px; }',
                '.form-field { border-bottom: 1px solid #000; display: inline-block; min-width: 50px; text-align: center; margin: 0 2px; padding: 0 5px; }',
                '</style>',
                '</head>', '<body>']
        
        # 處理頁(yè)眉和頁(yè)腳
        header_footer_content = process_headers_footers(docx, namespaces, doc_rels)
        
        # 添加頁(yè)眉內(nèi)容到頁(yè)面頂部
        header_content = [item for item in header_footer_content if 'class="header"' in item]
        footer_content = [item for item in header_footer_content if 'class="footer"' in item]
        
        # 添加頁(yè)眉
        html_output.extend(header_content)
        
        # 處理所有元素
        # 注意:在Word XML中,內(nèi)容在<w:body>標(biāo)簽內(nèi)
        body = tree.find('.//w:body', namespaces)
        if body is not None:
            # 跟蹤列表狀態(tài)
            in_list = False
            list_type = None
            
            for elem in body:
                if elem.tag.endswith('p'):  # 段落
                    p_pr = elem.find('./w:pPr', namespaces)
                    p_style_elem = p_pr.find('.//w:pStyle', namespaces) if p_pr is not None else None
                    p_style = p_style_elem.get(f'{{{namespaces["w"]}}}val') if p_style_elem is not None else None
                    
                    num_pr = p_pr.find('.//w:numPr', namespaces) if p_pr is not None else None
                    
                    # 檢查是否為標(biāo)題
                    is_heading = p_style and p_style.startswith('Heading')
                    
                    # 檢查是否為列表項(xiàng)
                    is_list_item = num_pr is not None
                    
                    # 如果之前在列表中,但現(xiàn)在不是列表項(xiàng),則關(guān)閉列表
                    # 標(biāo)題也不應(yīng)在列表中,所以也要關(guān)閉列表
                    if in_list and (not is_list_item or is_heading):
                        html_output.append(f'</{list_type}>')
                        in_list = False
                        list_type = None
                    
                    # 如果之前不在列表中,但現(xiàn)在是列表項(xiàng),則開(kāi)啟列表
                    if not in_list and is_list_item and not is_heading:
                        # 嘗試區(qū)分有序和無(wú)序列表
                        # 簡(jiǎn)單處理:如果有編號(hào)信息則認(rèn)為是有序列表,否則為無(wú)序列表
                        list_type = 'ol' if num_pr is not None else 'ul'
                        html_output.append(f'<{list_type}>')
                        in_list = True
                    
                    html_output.append(process_paragraph(elem, namespaces, docx, doc_rels))
                    
                elif elem.tag.endswith('tbl'):  # 表格
                    # 如果在列表中,則先關(guān)閉列表
                    if in_list:
                        html_output.append(f'</{list_type}>')
                        in_list = False
                        list_type = None
                        
                    html_output.append(process_table(elem, namespaces, docx, doc_rels))
            
            # 如果循環(huán)結(jié)束后還在列表中,則關(guān)閉列表
            if in_list:
                html_output.append(f'</{list_type}>')
        else:
            # 如果找不到body標(biāo)簽,則直接處理tree的子元素
            for elem in tree:
                if elem.tag.endswith('p'):  # 段落
                    html_output.append(process_paragraph(elem, namespaces, docx, doc_rels))
                elif elem.tag.endswith('tbl'):  # 表格
                    html_output.append(process_table(elem, namespaces, docx, doc_rels))
                
        # 添加頁(yè)腳內(nèi)容到頁(yè)面底部
        html_output.extend(footer_content)
        
        html_output.extend(['</body>', '</html>'])
        return '\n'.join(html_output)


def print_docx_info(filepath):
    """打印 Word 文檔的基本信息"""
    print(f"正在分析文檔: {filepath}")
    
    # 列出文檔內(nèi)容
    contents = list_docx_contents(filepath)
    print("\n文檔包含以下文件:")
    for item in contents[:10]:  # 只顯示前10個(gè)
        print(f"  {item}")
    
    if len(contents) > 10:
        print(f"  ... 還有 {len(contents) - 10} 個(gè)文件")
    
    # 提取并顯示文本內(nèi)容
    print("\n文檔文本內(nèi)容預(yù)覽:")
    text = extract_text_from_docx(filepath)
    print(text[:500] + "..." if len(text) > 500 else text)


def save_html(filepath, html_content):
    """保存 HTML 到文件"""
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(html_content)
    print(f"HTML 文件已保存到: {filepath}")


def main():
    """主函數(shù)"""
    print("Word 文檔處理工具")
    print("==================")
    
    try:
        # 顯示文檔信息
        print_docx_info('test_input.docx')
        
        # 分析文檔結(jié)構(gòu)
        print("\n文檔結(jié)構(gòu)分析:")
        analyze_docx_structure('test_input.docx')
        
        # 轉(zhuǎn)換為 HTML
        print("\n正在將 Word 文檔轉(zhuǎn)換為 HTML...")
        html_content = docx_to_html('test_input.docx')
        
        # 保存 HTML 文件
        save_html('output.html', html_content)
        print("轉(zhuǎn)換完成!")
        
    except FileNotFoundError:
        print("未找到 test_input.docx 文件,請(qǐng)確保文件存在于項(xiàng)目根目錄中")
    except Exception as e:
        print(f"處理文檔時(shí)發(fā)生錯(cuò)誤: {e}")
        import traceback
        traceback.print_exc()


# 程序入口點(diǎn)
if __name__ == '__main__':
    main()

以上就是Python使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)將Word文檔轉(zhuǎn)換為HTML的詳細(xì)內(nèi)容,更多關(guān)于Python Word轉(zhuǎn)HTML的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Python時(shí)要注意的十大陷阱匯總

    使用Python時(shí)要注意的十大陷阱匯總

    Python雖然沒(méi)有C/C++的指針和各種數(shù)據(jù)類型轉(zhuǎn)換,但不代表它沒(méi)有一片坦途,對(duì)于初學(xué)者,再感嘆Python的簡(jiǎn)單和強(qiáng)大之時(shí),可能一不小心就掉到陷阱中去了,所以本文給大家盤(pán)點(diǎn)了使用Python時(shí)要注意的十大陷阱,需要的朋友可以參考下
    2025-06-06
  • Python內(nèi)存管理機(jī)制之垃圾回收與引用計(jì)數(shù)操作全過(guò)程

    Python內(nèi)存管理機(jī)制之垃圾回收與引用計(jì)數(shù)操作全過(guò)程

    SQLAlchemy是Python中最流行的ORM(對(duì)象關(guān)系映射)框架之一,它提供了高效且靈活的數(shù)據(jù)庫(kù)操作方式,本文將介紹如何使用SQLAlchemy?ORM進(jìn)行數(shù)據(jù)庫(kù)操作,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Python使用SymPy和Manim輕松搞定導(dǎo)數(shù)動(dòng)畫(huà)

    Python使用SymPy和Manim輕松搞定導(dǎo)數(shù)動(dòng)畫(huà)

    大家好,你有沒(méi)有試過(guò)在 Manim 里做導(dǎo)數(shù)定義的動(dòng)畫(huà),這篇文章小編就來(lái)和大家詳細(xì)介紹一下Python如何使用SymPy和Manim輕松搞定導(dǎo)數(shù)動(dòng)畫(huà)吧
    2026-05-05
  • Kali Linux安裝ipython2 和 ipython3的方法

    Kali Linux安裝ipython2 和 ipython3的方法

    今天小編就為大家分享一篇Kali Linux安裝ipython2 和 ipython3的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • Python實(shí)現(xiàn)修改阿里云DNS域名解析

    Python實(shí)現(xiàn)修改阿里云DNS域名解析

    當(dāng)公網(wǎng)IP是浮動(dòng)的時(shí)候,用一個(gè)域名去實(shí)時(shí)解析,才不會(huì)那么糟糕,本文將介紹如何使用python修改阿里云dns域名解析,感興趣的小伙伴可以了解一下
    2024-11-11
  • 使用python如何實(shí)現(xiàn)泛型函數(shù)

    使用python如何實(shí)現(xiàn)泛型函數(shù)

    這篇文章主要介紹了使用python如何實(shí)現(xiàn)泛型函數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Python腳本實(shí)現(xiàn)DNSPod DNS動(dòng)態(tài)解析域名

    Python腳本實(shí)現(xiàn)DNSPod DNS動(dòng)態(tài)解析域名

    這篇文章主要介紹了Python腳本實(shí)現(xiàn)DNSPod DNS動(dòng)態(tài)解析域名,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-02-02
  • Python虛擬機(jī)中描述器的王炸應(yīng)用分享

    Python虛擬機(jī)中描述器的王炸應(yīng)用分享

    本篇文章給大家介紹一下描述器在?python?語(yǔ)言當(dāng)中有哪些應(yīng)用,主要介紹如何使用?python?語(yǔ)言實(shí)現(xiàn)?python?內(nèi)置的?proterty?、staticmethod?和?class?method,需要的可以參考一下
    2023-05-05
  • Python黑帽編程 3.4 跨越VLAN詳解

    Python黑帽編程 3.4 跨越VLAN詳解

    VLAN(Virtual Local Area Network),是基于以太網(wǎng)交互技術(shù)構(gòu)建的虛擬網(wǎng)絡(luò),既可以將同一物理網(wǎng)絡(luò)劃分成多個(gè)VALN,也可以跨越物理網(wǎng)絡(luò)障礙,將不同子網(wǎng)中的用戶劃到同一個(gè)VLAN中。這篇文章主要介紹了Python黑帽編程 3.4 跨越VLAN 的相關(guān)資料,需要的朋友參考下
    2016-09-09
  • 關(guān)于jupyter lab安裝及導(dǎo)入tensorflow找不到模塊的問(wèn)題

    關(guān)于jupyter lab安裝及導(dǎo)入tensorflow找不到模塊的問(wèn)題

    這篇文章主要介紹了關(guān)于jupyter lab安裝及導(dǎo)入tensorflow找不到模塊的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03

最新評(píng)論

五华县| 兖州市| 清新县| 麟游县| 余姚市| 延寿县| 德钦县| 兰州市| 鄯善县| 祁阳县| 湖州市| 保亭| 富平县| 峡江县| 巴南区| 保亭| 曲阳县| 随州市| 龙州县| 宁夏| 伊宁县| 嘉兴市| 台中市| 长宁区| 绵阳市| 曲水县| 公安县| 株洲县| 巫山县| 靖江市| 沂南县| 永顺县| 福鼎市| 安多县| 丹东市| 苗栗市| 集贤县| 新晃| 延吉市| 板桥市| 新巴尔虎右旗|