将markdown文件解析为html文件

发布于 2024-05-01
更新于 2024-05-20

任务需求

现在我有一些markdown文件记录的笔记,我希望将它转换为html格式上传到我的个人博客网站上,由于我的专业背景的原因,markdown文件中有很多数学公式需要渲染,还有一些代码块需要渲染,我希望在解析过程中原封不动地保留数学公式块的内容,在网页中调用katex进行渲染,将代码块的内容也原封不动地进行保留,在网页中使用highlight.js进行渲染。
将markdown转换为html,可以使用JavaScript也可以使用python,但是显然python的功能更强大,我可以先在本地将markdown内容解析完成然后填充到html网页中去。

python解析markdown文件

txt
<div class="skip-marked">$$ ... $$</div> 整段公式
$ ... $行内公式

公式内部有美元符号的公式公式内部有美元符号的公式

  1. markdown文件开头有用"---"包围起来的meta信息,需要将meta信息与正文信息分离。

  2. 数学公式包括两种:两端均有两个美元符号包裹的整段数学公式和两端仅由一个美元符号包裹的行内公式。此外还有少量公式内部包含美元符号。解析时必须充分考虑这几种情况。此外,在解析时,时常有部分特殊字符的含义与html文本冲突,例如下划线和星号,我们需要保证这些在公式内的字符不被错误地解析。

  3. 图片,需要为图片指定合适的alt文本和css类。

  4. 代码高亮,并且代码块还需要有显示代码语言类型和一键复制的功能。

python
import markdown
from markdown.preprocessors import Preprocessor
from markdown.extensions import Extension
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
import re
import html

class HighlightExtension(Extension):
    def extendMarkdown(self, md):
        md.preprocessors.register(CodeBlockPreprocessor(md), 'highlight_code_block', 25)
        md.preprocessors.register(ImagePreprocessor(md), 'image_preprocessor', 15)

class CodeBlockPreprocessor(Preprocessor):
    def run(self, lines):
        new_lines = []
        code_block = False
        code_lang = 'plaintext'  # 默认为纯文本
        for line in lines:
            if line.strip().startswith('```'):
                code_block = not code_block
                if code_block:
                    lang = line.strip().lstrip('```')
                    code_lang = lang if lang else 'plaintext'
                    new_lines.append(f'<div class="code-container"><button class="copy-button" onclick="copyToClipboard(this)">复制</button><pre><code class="hljs {code_lang}">')
                else:
                    new_lines.append('</code></pre></div>')
            elif code_block:
                new_lines.append(html.escape(line))  # 使用html.escape确保HTML字符被正确转义
            else:
                new_lines.append(line)
        return new_lines

class ImagePreprocessor(Preprocessor):
    def run(self, lines):
        new_lines = []
        for line in lines:
            image_match = re.match(r'!\[(.*?)\]\((.*?)\)', line)
            if image_match:
                alt_text = image_match.group(1) if image_match.group(1) else image_match.group(2).split('/')[-1]
                image_url = image_match.group(2)
                new_line = f'<img src="{image_url}" alt="{alt_text}" class="responsive"><span class="picstext">"{alt_text}"</span>'
                new_lines.append(new_line)
            else:
                new_lines.append(line)
        return new_lines

def wrap_math_with_div(content):
    content = re.sub(r'\$\$([\s\S]+?)\$\$', lambda m: f'<div class="skip-marked">{"<div class="skip-marked">$$" + m.group(1).replace("&#36;", "&#36;") + "$$</div>"}</div>', content)
    content = re.sub(r'(?<!\\)\$(?!\$)(.*?)(?<!\\)\$(?!\$)', lambda m: f'{"$" + m.group(1).replace("&#95;", "&#95;").replace("&#42;", "&#42;") + "$"}', content)
    return content


with open('mymarkdown.md', 'r', encoding='utf8') as file:
    markdown_text = file.read()

preprocessed_content = wrap_math_with_div(markdown_text)
html_content = markdown.markdown(preprocessed_content, extensions=[HighlightExtension(), 'markdown.extensions.extra'])
with open('output.txt', 'w', encoding='utf8') as file:
    file.write(html_content)

print('Markdown 已成功解析并保存为 output.txt 文件')

html中解析数学公式和代码高亮

在html头部的 <head> 中添加下面的内容即可。

数学公式

数学公式,参阅katex官网

html
<!DOCTYPE html>
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
<html>
  <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">

    <!-- The loading of KaTeX is deferred to speed up page rendering -->
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>

    <!-- To automatically render math in text elements, include the auto-render extension: -->
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
        onload="renderMathInElement(document.body);"></script>
  </head>
  ...
</html>

代码高亮

代码高亮,参阅 https://highlightjs.org/#usage 和 https://cdnjs.com/libraries/highlight.js

代码高亮示例代码高亮示例

html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>

<!-- and it's easy to individually load additional languages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>

<script>hljs.highlightAll();</script>

参阅