使用 py 一键将 md 文档中 html 表格转思源表格

起因

Doc2X 识别 pdf 的时候,表格会变成 html,虽然保留了合并单元格但是无法修改。于是参考 Excel&Word↔ 思源表格互转 - by 浅沧 - 动作信息 - Quicker (getquicker.net) ,让 ai 写了 Python 修改(本来块转换工具也能用自定义粘贴转换,但我怎么也没法保存,一怒之下怒了一下)

代码

import re
from lxml import etree

def convert_html_table_to_markdown(html_table):
    # 解析 HTML 表格
    parser = etree.HTMLParser()
    root = etree.fromstring(html_table, parser)
  
    # 获取所有表格行
    thead_trs = root.xpath('//thead/tr')
    tbody_trs = root.xpath('//tbody/tr')
  
    # 如果没有 <tbody>,则获取所有 <tr> 元素
    if not tbody_trs:
        tbody_trs = root.xpath('//tr')
  
    # 合并 <thead> 和 <tbody> 的行
    all_trs = thead_trs + tbody_trs
  
    if all_trs:
        row_num = len(all_trs)
        col_num = 0
        divider_row = 1
  
        # 计算列数和最大跨行数
        for td in all_trs[0].xpath('./th|./td'):
            col_num += int(td.get('colspan', 1))
            row_span = int(td.get('rowspan', 1))
            if divider_row < row_span:
                divider_row = row_span
  
        # 创建一个二维列表来存放表格数据
        table_data = [['' for _ in range(col_num)] for _ in range(row_num)]
  
        # 用于填充合并单元格的字符串
        empty_data = '{: class=\'fn__none\'}'
  
        # 逐行解析表格
        for r in range(row_num):
            c = 0
            for td in all_trs[r].xpath('./th|./td'):
                gap = 0
        
                row_span = int(td.get('rowspan', 1))
                col_span = int(td.get('colspan', 1))
        
                # 使用 itertext() 获取文本内容
                content = ''.join(td.itertext()).replace('\n', '<br />')
        
                # 确保不会超出当前行的边界
                while c + gap < len(table_data[r]) and table_data[r][c + gap] == empty_data:
                    gap += 1
        
                if row_span == 1 and col_span == 1:
                    if c + gap < len(table_data[r]):
                        table_data[r][c + gap] = content
                else:
                    for i in range(row_span):
                        for j in range(col_span):
                            if r + i < len(table_data) and c + gap + j < len(table_data[r + i]):
                                table_data[r + i][c + gap + j] = empty_data
                    if c + gap < len(table_data[r]):
                        table_data[r][c + gap] = f"{{: colspan='{col_span}' rowspan='{row_span}'}}" + content
        
                c += gap + col_span
  
        # 将数组中的数据组合成 Markdown 表格模板
        template_str = ""
        for r in range(row_num):
            template_str += '|'
            for c in range(col_num):
                template_str += ' ' + table_data[r][c] + ' |'
            template_str += '\n'
    
            # 添加分隔线
            if r == divider_row - 1:
                template_str += '|' + '|'.join([' :---: ' for _ in range(col_num)]) + '|\n'
  
        return template_str
    else:
        return "未找到表格"
def replace_html_tables_with_markdown(filename):
    with open(filename, 'r', encoding='utf-8') as file:
        content = file.read()

    # 查找所有的 HTML 表格
    html_tables = re.findall(r'<table.*?>.*?</table>', content, re.DOTALL)

    # 为每一个 HTML 表格生成 Markdown 表格并替换
    for html_table in html_tables:
        markdown_table = convert_html_table_to_markdown(html_table)
        content = content.replace(html_table, markdown_table)

    # 写入更新后的内容到文件
    with open(filename, 'w', encoding='utf-8') as file:
        file.write(content)

# 替换指定文件中的 HTML 表格
filename = r'D:\1.md'
replace_html_tables_with_markdown(filename)

效果

大概就是下面这样

项目著作权专利权商标权
管辖1. 原则上由中级以上人民法院管辖。 2. 北京、上海、广州、海南自由贸易港设立知识产权法院。 3. 最高院设立知识产权法庭,主要审理专利等专业技术性较强的知识产权上诉案件
损害 赔偿1. 权利人的实际损失或侵 权人的违法所得; 2. 参照该权利使用费给予 赔偿; 3. 惩罚性赔偿:对故意侵 犯著作权或者与著作权有 关的权利,情节严重的, 可以在按照上述方法确定 数额的 1 倍以上 5 倍以下 给予赔偿; 4. 法定数额:500元-500万元1. 权利人的实际损失或侵权人 的侵权所得利益; 2. 参照该专利许可使用费的 倍数; 3. 惩罚性赔偿:对故意侵犯专 利权,情节严重的,可以在按 照上述方法确定数额的 1 倍以 上 5 倍以下确定赔偿数额: 4. 法定数额:3 万元-500 万元1. 权利人的实际损失; 2. 侵权人因侵权所获得的利益; 3. 参照该商标许可使用费的倍 数合理确定; 4. 惩罚性赔偿:对恶意侵犯商 标专用权,情节严重的,可以 在按照上述方法确定数额的 1 倍以上 5 倍以下确定赔偿数额; 5. 法定数额:500 万元以下
[特别提示] 1. 赔偿数额, 计算时要依上述顺序确定, 前一顺位无法确定时, 才可适用后一顺位。 2. 权利人还可以要求赔偿为制止侵权所支付的合理开支,如律师费、诉讼费等
原告1. 权利人:即著作权人及邻接权人、专利权人、商标权人。 2. 许可合同的被许可人:
比较项目权利人被许可人备注
独占许可有权起诉有权起诉合同另有约定除外
排他许可1. 与权利人共同起诉; 有权起诉 2. 权利人不起诉的 情况下单独起诉合同另有约定除外
普通许可有权起诉无权起诉合同另有约定或者经权 利人书面授权可单独提 起诉讼的除外
项目 著作权 专利权 商标权
项目 著作权 专利权 商标权
管辖 1. 原则上由中级以上人民法院管辖。 2. 北京、上海、广州、海南自由贸易港设立知识产权法院。 3. 最高院设立知识产权法庭,主要审理专利等专业技术性较强的知识产权上诉案件
损害 赔偿 1. 权利人的实际损失或侵 权人的违法所得; 2. 参照该权利使用费给予 赔偿; 3. 惩罚性赔偿:对故意侵 犯著作权或者与著作权有 关的权利,情节严重的, 可以在按照上述方法确定 数额的 1 倍以上 5 倍以下 给予赔偿; 4. 法定数额:500 元-500 万元 1. 权利人的实际损失或侵权人 的侵权所得利益; 2. 参照该专利许可使用费的 倍数; 3. 惩罚性赔偿:对故意侵犯专 利权,情节严重的,可以在按 照上述方法确定数额的 1 倍以 上 5 倍以下确定赔偿数额: 4. 法定数额:3 万元-500 万元 1. 权利人的实际损失; 2. 侵权人因侵权所获得的利益; 3. 参照该商标许可使用费的倍 数合理确定; 4. 惩罚性赔偿:对恶意侵犯商 标专用权,情节严重的,可以 在按照上述方法确定数额的 1 倍以上 5 倍以下确定赔偿数额; 5. 法定数额:500 万元以下
[特别提示] 1. 赔偿数额, 计算时要依上述顺序确定, 前一顺位无法确定时, 才可适用后一顺位。 2. 权利人还可以要求赔偿为制止侵权所支付的合理开支,如律师费、诉讼费等
原告 1. 权利人:即著作权人及邻接权人、专利权人、商标权人。 2. 许可合同的被许可人:
比较项目 权利人被许可人 备注
独占许可 有权起诉有权起诉 合同另有约定除外
排他许可 1. 与权利人共同起诉; 有权起诉 2. 权利人不起诉的 情况下单独起诉 合同另有约定除外
普通许可 有权起诉无权起诉 合同另有约定或者经权 利人书面授权可单独提 起诉讼的除外

其实还可以搞一个逆向的,或是转 excel,不过我没那能力

建议

还是希望思源官方能提供一键转表格的功能,我看论坛之前也有人提,代码也有现成的了

最后留一下 Doc2X 的邀请码 JRH0E7/66RYCA,这个网站对 md 笔记用户帮助还是挺大的,对扫描 pdf 也能识别,估计以后会收费啥的,早用早享受

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23019 引用 • 92593 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...