チュートリアル2 マークダウンレンダリングの変更
《 初回公開:2022/03/26 , 最終更新:未 》
原文は
【 目次 】
イントロダクション
Python-Markdownの多くの拡張機能は新しい構文を追加しますが、場合によっては、Markdownが既存の構文をレンダリングする方法を単純に変更したいことがあります。
または、一部の画像をインラインで表示したいが、外部でホストされている画像を単に画像を指すリンクにする必要がある場合があります。
次のMarkdownが提供されたとします。


Python-Markdownが次のHTMLを返すようにします。
<p><img alt="a local image" src="/path/to/image.jpg" /></p>
<p><a href="http://example.com/image.jpg">a remote image</a></p>
注:このチュートリアルは非常に一般的であり、基本的なPython3開発環境を想定しています。
Python開発の基本的な理解が必要です。
分析
私たちが利用できるオプションを考えてみましょう:
-
画像関連のインラインパターンを上書きします。
これは機能しますが、既存のパターンを変更する必要はありません。
パーサーは構文を正しく認識しています。
HTML出力を変更するだけです。また、インライン画像リンクと参照スタイルの画像リンクの両方をサポートする必要があります。これには、両方のインラインパターンを再定義して、作業を2倍にする必要があります。
-
既存のパターンをそのままにして、Treeprocessorを使用してHTMLを変更します。
これは、Markdown構文のトークン化を変更するものではありません。
他のサードパーティの拡張機能によって追加された新しい画像構文であっても、画像を表すものはすべて含まれると確信できます。
上記を前提として、オプション2を使用しましょう。
ソリューション
まず、新しいツリープロセッサを作成しましょう。
from markdown.treeprocessors import Treeprocessor
class InlineImageProcessor(Treeprocessor):
def run(self, root):
# Modify the HTML here
Treeprocessor
のrun
メソッドは、ElementTreeオブジェクトを含むroot
引数を受け取ります。
そのオブジェクト内のすべてのimg
要素を反復処理し、外部URLを含む要素を変更する必要があります。
したがって、runメソッドに次のコードを追加します。
# Iterate over img elements only
for element in root.iter('img'):
# copy the element's attributes for later use
attrib = element.attrib
# Check for links to external images
if attrib['src'].startswith('http'):
# Save the tail
tail = element.tail
# Reset the element
element.clear()
# Change the element to a link
element.tag = 'a'
# Copy src to href
element.set('href', attrib.pop('src'))
# Copy alt to label
element.text = attrib.pop('alt')
# Reassign tail
element.tail = tail
# Copy all remaining attributes to element
for k, v in attrib.items():
element.set(k, v)
上記のコードについて注意すべき点がいくつかあります。
- 後で
element.clear()
を使用して要素をリセットするときに属性が失われないように、要素の属性のコピーを作成します。
同じことがtail
にも当てはまります。
img
要素にはtext
がないため、それについて心配する必要はありません。 href
属性とelement.text
は、img
要素の要素の異なる属性名に割り当てられるため、明示的に設定します。
その際、attrib
からsrc
属性とalt
属性をpop
して、最後のステップで残りのすべての属性をコピーしたときにそれらが存在しないようにします。- 内部画像を指す
img
要素に変更を加える必要がないため、コードでそれらを参照する必要はありません(単にスキップされます)。 - 外部リンク (
startswith('http')
) のテストは改善することができるように、読者の演習として残されています。
次に、Extensionサブクラスを使用して新しいTreeprocessor
をMarkdown
に通知する必要があります。
from markdown.extensions import Extension
class ImageExtension(Extension):
def extendMarkdown(self, md):
# Register the new treeprocessor
md.treeprocessors.register(InlineImageProcessor(md), 'inlineimageprocessor', 15)
Treeprocessor
を優先度15
で登録します。これにより、すべてのインライン処理が完了した後に確実に実行されます。
Test 1
すべて一緒に見てみましょう:
ImageExtension.py
from markdown.treeprocessors import Treeprocessor
from markdown.extensions import Extension
class InlineImageProcessor(Treeprocessor):
def run(self, root):
for element in root.iter('img'):
attrib = element.attrib
if attrib['src'].startswith('http'):
tail = element.tail
element.clear()
element.tag = 'a'
element.set('href', attrib.pop('src'))
element.text = attrib.pop('alt')
element.tail = tail
for k, v in attrib.items():
element.set(k, v)
class ImageExtension(Extension):
def extendMarkdown(self, md):
md.treeprocessors.register(InlineImageProcessor(md), 'inlineimageprocessor', 15)
次に、拡張機能をMarkdownに渡します。
Test.py
import markdown
input = """


"""
from ImageExtension import ImageExtension
html = markdown.markdown(input, extensions=[ImageExtension()])
print(html)
また、python Test.py
を実行すると、次の出力が正しく返されます。
<p><img alt="a local image" src="/path/to/image.jpg" title="A title."/></p>
<p><a href="http://example.com/image.jpg" title="A title.">a remote image</a></p>
成功! 各画像にタイトルが含まれていることに注意してください。これも適切に保持されています。
コンフィグレーション設定の追加
ユーザーが既知のイメージホストのリストを提供できるようにしたいとします。
これらのホストの画像を指すimg
タグはインライン化できますが、他の画像は外部リンクである必要があります。
もちろん、内部(相対)リンクの既存の動作を維持したいと考えています。
まず、Extension
サブクラスに構成オプションを追加する必要があります。
class ImageExtension(Extension):
def __init__(self, **kwargs):
# Define a config with defaults
self.config = {'hosts' : [[], 'List of approved hosts']}
super(ImageExtension, self).__init__(**kwargs)
デフォルトで空のリストになるhosts
コンフィグレーション設定を定義しました。
次に、extendMarkdown
メソッドでそのオプションをtreeprocessor
に渡す必要があります。
def extendMarkdown(self, md):
# Pass host to the treeprocessor
md.treeprocessors.register(InlineImageProcessor(md, hosts=self.getConfig('hosts')), 'inlineimageprocessor', 15)
次に、新しい設定を受け入れるようにtreeprocessor
を変更する必要があります。
class InlineImageProcessor(Treeprocessor):
def __init__(self, md, hosts):
self.md = md
# Assign the setting to the hosts attribute of the class instance
self.hosts = hosts
次に、設定を使用してURLをテストするメソッドを追加できます。
from urllib.parse import urlparse
class InlineImageProcessor(Treeprocessor):
...
def is_unknown_host(self, url):
url = urlparse(url)
# Return False if network location is empty or an known host
return url.netloc and url.netloc not in self.hosts
最後に、runメソッドのif attrib['src'].startswith('http'):
行をif self.is_unknown_host(attrib['src']):
に置き換えることで、testメソッドを利用できます。
Test 2
最終結果は次のようになります。
ImageExtension.py
from markdown.treeprocessors import Treeprocessor
from markdown.extensions import Extension
from urllib.parse import urlparse
class InlineImageProcessor(Treeprocessor):
def __init__(self, md, hosts):
self.md = md
self.hosts = hosts
def is_unknown_host(self, url):
url = urlparse(url)
return url.netloc and url.netloc not in self.hosts
def run(self, root):
for element in root.iter('img'):
attrib = element.attrib
if self.is_unknown_host(attrib['src']):
tail = element.tail
element.clear()
element.tag = 'a'
element.set('href', attrib.pop('src'))
element.text = attrib.pop('alt')
element.tail = tail
for k, v in attrib.items():
element.set(k, v)
class ImageExtension(Extension):
def __init__(self, **kwargs):
self.config = {'hosts' : [[], 'List of approved hosts']}
super(ImageExtension, self).__init__(**kwargs)
def extendMarkdown(self, md):
md.treeprocessors.register(InlineImageProcessor(md, hosts=self.getConfig('hosts')), 'inlineimageprocessor', 15)
テストしてみましょう:
Test.py
import markdown
input = """



"""
from ImageExtension import ImageExtension
html = markdown.markdown(input, extensions=[ImageExtension(hosts=['example.com'])])
print(html)
そして、python Test.py
を実行すると、次の出力が返されます。
<p><img alt="a local image" src="/path/to/image.jpg"/></p>
<p><img alt="a remote image" src="http://example.com/image.jpg"/></p>
<p><a href="http://exclude.com/image.jpg">an excluded remote image</a></p>
上記の拡張機能を配布用のパッケージにまとめることは、読者の演習として残されています。