|
import xml.etree.ElementTree as ET |
|
import re |
|
import hatena_entries as HE |
|
import entry_util as eutil |
|
import code_lang as codeLang |
|
|
|
# 機能 |
|
# |
|
# SyntaxHighlighter によるコードハイライトを含む記事一覧の取得 |
|
# 指定されている言語コード一覧の取得 |
|
# SyntaxHighlighter によるコードハイライトを はてなブログの markdown に変換 |
|
|
|
|
|
# SyntaxHighlighter の記述を見つけるための正規表現 |
|
# |
|
# <pre> タグと </pre> タグに囲まれた部分を見つける |
|
# 典型的な pre タグは次のようなものを想定 |
|
# <pre class="brush: bash" data-unlink=""> |
|
# <pre class="brush: bash", data-unlink=""> |
|
# <pre class="brush: bash;"> |
|
# data-unlink 指定はない場合もある |
|
# |
|
# 以下をグループとして取得する |
|
# \1 : <pre> タグ |
|
# \2 : class 属性を指定する際のクォーテーション |
|
# \3 : 言語名 |
|
# \4 : コード本体 |
|
# \5 : </pre> タグ |
|
# |
|
# コード本体が複数行に渡るので、複数行でのマッチを行う |
|
pattern = re.compile('(<\s*pre\s+class\s*=\s*(?P<quot>[\"\']?)\s*brush\s*:\s*([\d\w]+)\s*;?\s*(?P=quot)\s*,?\s*(?:data-unlink\s*=\s*(?:\"\"|\'\'))?\s*>)(.+?)(</\s*pre\s*>)', re.MULTILINE | re.DOTALL) |
|
# 変換対象漏れチェック用 |
|
#pattern = re.compile('<\s*pre\s+class\s*=\s*[\"\']?\s*brush', re.MULTILINE | re.DOTALL) |
|
|
|
# 変換対象記事数 |
|
num_convert = 0 |
|
|
|
|
|
def replacer(matchObj): |
|
nl1 = "" if matchObj.group(4)[0] == "\n" else "\n" |
|
nl2 = "" if matchObj.group(4)[-1] == "\n" else "\n" |
|
# 問題再現 |
|
#nl1 = "" |
|
#nl2 = "" |
|
|
|
return "```" + codeLang.lang[matchObj.group(3)] + nl1 + matchObj.group(4) + nl2 + "```" |
|
|
|
def getConvertedContent(content_text): |
|
return re.sub(pattern, replacer, content_text) |
|
|
|
# 記事情報の取得 |
|
# isLang, true: 言語一覧, false: 記事タイトル一覧 |
|
def listup(entry, isLang): |
|
global num_convert |
|
|
|
content = entry.find('def:content', HE.ns) |
|
title = entry.find('def:title', HE.ns) |
|
|
|
resultSet = set() |
|
# content に syntaxhighlighter の記述 があるか |
|
if content is not None and content.text is not None: |
|
m = pattern.findall(content.text) |
|
|
|
if len(m) > 0: |
|
num_convert += 1 |
|
if isLang: |
|
for v in m: |
|
resultSet.add(v[2]) # #3 lang |
|
else: |
|
resultSet.add(title.text) |
|
|
|
return resultSet |
|
|
|
def convert(entry): |
|
global num_convert |
|
|
|
content = entry.find('def:content', HE.ns) |
|
|
|
# content に syntaxhighlighter の記述 があるか |
|
if content is not None and content.text is not None: |
|
m = pattern.findall(content.text) |
|
|
|
if len(m) > 0: |
|
num_convert += 1 |
|
# ある場合の処理 |
|
# syntaxhighlighter -> markdown に変換 |
|
converted_text = getConvertedContent(content.text) |
|
# 更新用の xml 文字列を生成 |
|
xml_str = eutil.getConvertedXml(entry, converted_text) |
|
# 記事更新 |
|
#print('-------------------------') |
|
#print(xml_str) |
|
#print('-------------------------') |
|
# entry_id を取得 |
|
entry_id = eutil.getEntryId(entry) |
|
HE.updateEntry(entry_id, xml_str) |
|
|
|
def printResultSet(resultSet): |
|
for r in sorted(resultSet): |
|
print(r) |
|
|
|
# メイン処理 |
|
def proc(args): |
|
|
|
entries = HE.HatenaEntries() |
|
|
|
resultSet = set() |
|
for entry in entries: |
|
|
|
if args.list: |
|
resultSet.update(listup(entry, False)) |
|
elif args.lang: |
|
resultSet.update(listup(entry, True)) |
|
elif args.convert: |
|
convert(entry) |
|
|
|
|
|
print("\nnum of articles: {}".format(entries.getCurrentArticles())) |
|
print("num of articles to convert: {}\n".format(num_convert)) |
|
if resultSet: |
|
printResultSet(resultSet) |
|
|
|
if __name__ == "__main__": |
|
|
|
import argparse |
|
|
|
parser = argparse.ArgumentParser(description="convert syntaxhighlighter to markdown") |
|
group = parser.add_mutually_exclusive_group(required=True) |
|
group.add_argument("-l", "--list", action="store_true", help="list article titles that has syntaxhighliter code") |
|
group.add_argument("-g", "--lang", action="store_true", help="list language name") |
|
group.add_argument("-c", "--convert", action="store_true", help="convert syntaxhighlighter to markdown") |
|
args = parser.parse_args() |
|
|
|
num_convert = 0 |
|
proc(args) |