Mistune——更快的markdown解析器
在Python中有很多markdown解析器,以前我一直使用的是Python-markdown,一個純Python實現的markdown解析器,別的不說,慢的要死倒是真的。每次點擊保存后,都要響應很久,我開始一直以為是我的vps在國外導致的,后來還用了Mistune才知道,不是網速的問題,是解析器的速度問題。
沒有對比就沒有傷害,Mistune是所有純Python實現中最快的一個。在純Python環境中,幾乎比Python- markdown快4倍,在Cython的幫助下,幾乎快5倍。現在我使用了CPython,幾乎是點擊完保存的一瞬間,就解析完了,可以說感覺是很明顯的。
基礎用法
一個簡單的栗子:
import mistune
mistune.markdown('I am using **mistune markdown parser**')
# output: <p>I am using <strong>mistune markdown parser</strong></p>
如果關心性能,官方推薦使用對象實例化后在進行調用
import mistune
markdown = mistune.Markdown()
markdown('I am using **mistune markdown parser**')
選擇項
在使用mistune.Renderer
的時候會有一些選擇項提供修改
renderer = mistune.Renderer(escape=True, hard_wrap=True)
# use this renderer instance
markdown = mistune.Markdown(renderer=renderer)
markdown(text)
- escape: 如果設置為False,則不會轉義所有原始html標記。
- hard_wrap: 如果設置為True,它將具有GFM換行功能。所有新行都將替換為
<br>
標記。GFM跟標准MD一樣,行尾不允許直接回車換行,必須是\n\n
或者空格空格\n
- use_xhtml: 如果設置為True,則所有標記都將使用xhtml,例如:
<hr />
。 - parse_block_html:只解析block級別的。
- parse_inline_html: 只解析inline級別的。
Renderer(渲染器)
官方提供了渲染器,當然你也可以繼承官方的Renderer然后自己重寫或者添加一些方法,這里給了一個官網給出的栗子,是一個給所有代碼塊添加highlighting
標簽的栗子。
import mistune
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import html
class HighlightRenderer(mistune.Renderer):
def block_code(self, code, lang):
if not lang:
return '\n<pre><code>%s</code></pre>\n' % \
mistune.escape(code)
lexer = get_lexer_by_name(lang, stripall=True)
formatter = html.HtmlFormatter()
return highlight(code, lexer, formatter)
renderer = HighlightRenderer()
markdown = mistune.Markdown(renderer=renderer)
print(markdown('```python\nassert 1 == 1\n```'))
下面是關於解析內容的很關鍵的定義,一個是塊級解析(Block Level),一個是小跨度級解析(Span Level),也就是每句話中間出現的那種markdown解析,加粗什么的,還有一個是腳注(Footnotes)
Block Level
塊級的API如下:
block_code(code, language=None) #代碼塊
block_quote(text) #引用塊
block_html(html) #原生html?
header(text, level, raw=None) # 不知道是啥
hrule() # 不知道是啥
list(body, ordered=True) # 不知道是啥
list_item(text) # 不知道是啥
paragraph(text) # 段落
table(header, body) # 表格
table_row(content) # 表格行
table_cell(content, **flags) # 表格單元
Span Level
塊級的API如下:
autolink(link, is_email=False) #引用鏈接
codespan(text) #行級code
double_emphasis(text) #加粗
emphasis(text) #斜體
image(src, title, alt_text) #圖片
linebreak() #不知道
newline() #新的一行?
link(link, title, content) #引用鏈接
strikethrough(text) #不知道
text(text) #普通文字
inline_html(text) #內嵌HTML
Footnotes
腳注的API如下:
footnote_ref(key, index)
footnote_item(key, text)
footnotes(text)
自定義規則
下面就是自定義規則了,我們大概了解了每個API,官方源碼里面主要分了以下幾個類
- BlockGrammar:關於塊級的語法正則定義
- BlockLexer:塊級詞法分析器
- InlineGrammar:關於行級的語法正則定義
- InlineLexer:行級詞法分析器
- Markdown:Markdown解析
- Renderer:解析器
在這里,我舉一個真實的栗子來講解,首先拋出問題
問題:我在使用mathtype的時候,書寫公式,例如這個公式||x||_1= \sum_{i=0}^{n}|x_i|
,這是L1范數的公式,現在解析肯定沒有問題了,我已經修復了,但如果是以前,會由於||x||
后面的字符_
和sum
后面的字符_
,匹配到markdown中就是斜體的定義,那么我的公式就會變為
||x|| @1= \sum@ {i=0}^{n}|x_i|
這個樣子。所以為了解決這個問題,我要重寫有關於下划線_
和乘號*
的markdown語法。
解決辦法:
首先定位一下,主要引起這個的有兩個,一個是加粗,一個是斜體,所以我重寫他們。
- 定義自己的
InlineLexer
,繼承InlineLexer
; - 找到規則
double_emphasis
和emphasis
,我將加粗改寫為了只允許**
框住,斜體改寫為了@
框住; - 加入到
default_rules
這個list里面; - 重寫輸出函數
output_double_emphasis
和output_emphasis
。
class MyInlineLexer(InlineLexer):
def enable_delete_em(self):
self.rules.double_emphasis = re.compile(
r'\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
)
self.rules.emphasis = re.compile(
r'\@((?:\*\*|[^\*])+?)\@(?!\*)' # @word@
)
self.default_rules.insert(3, 'double_emphasis')
self.default_rules.insert(3, 'emphasis')
def output_double_emphasis(self, m):
text = m.group(1)
return self.renderer.double_emphasis(text)
def output_emphasis(self, m):
text = m.group(1)
return self.renderer.emphasis(text)
然后在解析markdown的時候
renderer = Renderer()
inline = MyInlineLexer(renderer)
inline.enable_delete_em()
markdown = mistune.Markdown(renderer=renderer, inline=inline)
- 實例化Renderer;
- 實例化自己的詞法分析器;
- 調用
enable_delete_em()
重寫對應的地方; - 最后,實例化markdown,傳入對應的renderer與inline即可。