【问题标题】:How can I convert a Markdown string to a DocX in Python?如何在 Python 中将 Markdown 字符串转换为 DocX?
【发布时间】:2022-01-18 14:44:40
【问题描述】:

我从我的 API 中获取降价文本,如下所示:

{
    name:'Onur',
    surname:'Gule',
    biography:'## Computers
    I like **computers** so much.
    I wanna *be* a computer.',
    membership:1
}

biography 列包括上面的降价字符串。

## Computers
I like **computers** so much.
I wanna *be* a computer.

我想将此降价文本转换为我的报告的 docx 字符串。

在我的 docx 模板中:

{{markdownText|mark2html}}

{{simpleText}}

我正在使用 python3 docxtpl 包来创建 docx,它适用于简单的文本。

  • 我尝试使用 BeautifulSoup 将 markdown 转换为 docx 文本,但它不适用于样式(粗体、斜体等)。
  • 我尝试了 pandoc,它可以工作,但它只是创建了一个 docx 文件,我想将呈现的 markdown 文本添加到现有的 docx(在创建时)。

我当前的代码:

import docx
from docxtpl import DocxTemplate, RichText
import markdown
import jinja2
import markupsafe
from bs4 import BeautifulSoup
import pypandoc

def safe_markdown(text):
    return markupsafe.Markup(markdown.markdown(text))

def mark2html(value):
    html = markdown.markdown(value)
    soup = BeautifulSoup(html, features='html.parser')
    output = pypandoc.convert_text(value,'rtf',format='md')
    return RichText(value) #tried soup and pandoc..

def from_template(template):
    template = DocxTemplate(template)
    context = {
        'simpleText':'Simple text test.',
        'markdownText':'Markdown **text** test.'
    } 
    jenv = jinja2.Environment()
    jenv.filters['markdown'] = safe_markdown
    jenv.filters["mark2html"] = mark2html
    template.render(context,jenv)
    template.save('new_report.docx')

那么,我如何将渲染的 Markdown 添加到现有的 docx 或创建时,也许使用 jinja2 过滤器?

【问题讨论】:

标签: python html jinja2 markdown docx


【解决方案1】:

我没有任何捷径就解决了。我使用 beautifulSoup 将 markdown 转换为 html,然后通过检查它们的标签名称来处理每个段落。

在我的word模板中:

{% if markdownText != None %}
    {% for mt in markdownText|mark2html %} 
        {{mt}}
    {% endfor %}
{% endif %}

我的模板标签:

def mark2html(value):
    if value == None:
        return '-'
    html = markdown.markdown(value)
    soup = BeautifulSoup(html, features='html.parser')
    paragraphs = []
    global doc
    for tag in soup.findAll(True):
        if tag.name in ('p','h1','h2','h3','h4','h5','h6'):
            paragraphs.extend(parseHtmlToDoc(tag))  
    return paragraphs

我插入 docx 的代码:

def parseHtmlToDoc(org_tag):
    contents = org_tag.contents
    pars= []
    for con in contents:
        if str(type(con)) == "<class 'bs4.element.Tag'>":
            tag = con
            if tag.name in ('strong',"h1","h2","h3","h4","h5","h6"):
                source = RichText("")
                if len(pars) > 0 and str(type(pars[len(pars)-1])) == "<class 'docxtpl.richtext.RichText'>":
                    source = pars[len(pars)-1]
                    source.add(con.contents[0], bold=True)
                else:
                    source.add(con.contents[0], bold=True)
                    pars.append(source) 
            elif tag.name == 'img':
                source = tag['src']
                imagen = InlineImage(doc, settings.MEDIA_ROOT+source)
                pars.append(imagen)
            elif tag.name == 'em':
                source = RichText("")
                source.add(con.contents[0], italic=True)
                pars.append(source)
        else:
            source = RichText("")
            if len(pars) > 0 and str(type(pars[len(pars)-1])) == "<class 'docxtpl.richtext.RichText'>":
                    source = pars[len(pars)-1]
                    pars.add(con)
            else:
                if org_tag.name == 'h2':
                    source.add(con,bold=True,size=40)
                else:
                    source.add(con)
                pars.append(source) # her zaman append?
    return pars

它处理 html 标签,如 b、i、img、标题。您可以添加更多标签进行处理。 我就这样解决了,它不需要像 html2docx 等任何额外的文件转换。

【讨论】:

    【解决方案2】:

    我遵循了一种懒惰、效率不高但有用的策略。由于处理docx 不如html 灵活,我先将markdown md 转换为html,然后从html 移动到docx,如下所示:

    from jinja2 import FileSystemLoader, Environment
    from pypandoc import convert_file, convert_text
    
    def md2html(md):
      return convert_text(md, 'html', format='md')
    
    def html2docx(file):
      return convert_file(f'{file}.html', 'docx', format='html', outputfile=f'{file}.docx')
    
    def from_template(template_file, f_out):
      context = {
          'simpleText': 'Simple text test.',
          'markdownText': 'Markdown **text** test.'
      }
      ldr = FileSystemLoader(searchpath='./')
      jenv = Environment(loader=ldr)
      jenv.filters["md2html"] = md2html
      template = jenv.get_template(template_file)
      html = template.render(context)
      print(html)
      with open(f'{f_out}.html', 'w') as fout:
        fout.write(html)
        fout.close()
      html2docx(f_out)
    
    if __name__ == '__main__':
      from_template('template.html.jinja', 'new_report')
    

    至于模板的内容,应该取自基于html的这样一个:

    <!DOCTYPE html>
    <html xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
      <head></head>
      <body>
        {{markdownText|md2html}}
        {{simpleText}}
      </body>
    </html>
    

    我把它保存为template.html.jinja

    我很想研究 @Mahrkeenerh 的贡献,那里提到的 API 似乎有很多需要学习和理解的项目。

    【讨论】:

    • 很好,但我的模板是 docx,我正在使用这样的方法。我转换为html并通过处理html标签逐行实现到docx。
    • 如果您想将save as 您的docx 模板从Word 转换为html。然后,编辑 html 并插入您在(此处的文档)[jinja.palletsprojects.com/en/3.0.x/templates/]: - {% ... %} 用于语句 - {{ ... }} 用于表达式以打印到模板输出 - {# ... #} 用于评论中提到的 jinja 字段不包含在模板输出中 - # ... ## 用于行语句您可以在导出之前在Word 文件中编写疯狂的标记,如REPLACE_THIS_LATER,以便在导出后在文本编辑器中快速找到它们。您不必逐行执行!
    猜你喜欢
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2017-01-14
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多