この記事では WordPress から Miyadaiku へ記事を持ってくるときにやったことを記録として残します。
Miyadaiku 自体のセットアップは Miyadaiku を始めよう を参照ください。
備考
この記事では 現時点での最新である 0.0.51 のバージョンを使います。
WordPressでの作業
警告
※WordPress のバージョンによっては UI が違うかもしれません
メニューからエクスポート画面に移動します。

今回は すべての投稿を移動するだけなので 投稿 にチェックを入れ エクスポートファイルをダウンロード ボタンをクリック。

これで完了です。 wordpress.2018-02-27.xml がダウンロードされ、当該記事では このファイルを使うので、各自読み替えてください。
記事ファイルの一括生成
下準備
ダウンロードしたファイル を xml ライブラリ(ビルトイン)で読み出そうとすると、
>>> import xml.etree.ElementTree as ET >>> tree = ET.parse('wordpress.2018-02-27.xml') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 1182, in parse tree.parse(source, parser) File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/xml/etree/ElementTree.py", line 594, in parse self._root = parser._parse_whole(source) xml.etree.ElementTree.ParseError: unbound prefix: line 42, column 0
エラーがでました。
仕方がないので XML を直接書き換えます。
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/> <atom:link rel="hub" href="http://pubsubhubbub.superfeedr.com"/>
を消したらパースできました。
pubsubhubbub というプラグイン を導入していたために発生したと考えられます。 導入しているプラグインによっては別のエラーが発生する可能性があります。
変換
パースする準備が整ったので XML (内のHTML) を reST に変換していきます。
Miyadaiku 自体が HTML フォーマットの記事をサポートしていますし、
.. raw:: html
を使うことで reST 内に HTML を書けるんですが、全部きれいな reST にしてやります。表示が崩れるのが怖かったのです。
というわけで pandoc を使い HTML から reST へ変換します。
- インストール
-
$ brew install pandoc
(Mac なら) $ pip install python-dateutil pypandoc
-
XML の /channel/item が各記事で、 item から 情報を抜き出して reST を作成します。
title |
記事のタイトル |
---|---|
pubDate |
記事の投稿日 |
link |
記事のURL |
content:encoded |
|
自分の場合、コードは以下のような感じになりました。
(定数が import より上に来てるけど突っ込まないでください。他の人が利用するときに書き換えやすいためです)
XML = 'wordpress.2018-02-27.xml' META_TEMPLATE = ''' .. article:: :date: {date} :title: {title} :canonical_url: {path} :tags: ''' import io import os from urllib.parse import urlparse from datetime import datetime import xml.etree.ElementTree as ET import pypandoc import dateutil.parser as parser tree = ET.parse(XML) root = tree.getroot() channel = root.find('channel') for item in channel.findall('item'): meta = { 'title': item.find('title').text, 'date': parser.parse(item.find('pubDate').text).strftime('%Y-%m-%d'), 'path': urlparse(item.find('link').text).path, } html = item.find('{http://purl.org/rss/1.0/modules/content/}encoded').text if html is None: continue try: os.makedirs(meta['path'].lstrip('/')) except OSError: pass input_path = 'temp.html' output_path = os.path.join(meta['path'].lstrip('/'), 'index.rst') with io.open(input_path, mode='w', encoding='utf-8') as f: f.write(html) rst = pypandoc.convert_file(input_path, 'rst') with io.open(output_path, mode='w', encoding='utf-8') as f: f.write(META_TEMPLATE.format(**meta) + rst)
警告
上記のコードを利用する場合
XML 変数は 自分が DL した xml ファイルのパスに書き換えてください。
-
自分の場合、タグをすべてつけ直すつもりだったので tags はすべて空で出力しました。
引き継ぎたい場合は、自力でどこかから抽出してください。
とりあえず出力できたようです。
$ ls -R . 2014 2016 temp.html 2015 2017 wordpress.2018-02-27.xml ./2014: ansible-practice python-decorator backup-sitefiles-to-bitbucket python-logging django-aggregate python-module github-bitbucket-public-key-regist vagrant-error github-dotfiles windows7-window-transparent-off google-2step-auth-setting wordpress-admin-common-ssl microsoft-skype-unification wordpress-image-insert-modify public-key-basic-config wordpress-publish-confirm pypi-debut wordpress-revision-clean python-argument-intro wordpress-syntax-highlighter python-blogger-api wordpress-tinymce-disable-actual-reference ./2014/ansible-practice: index.rst ./2014/backup-sitefiles-to-bitbucket: index.rst ./2014/django-aggregate: index.rst ./2014/github-bitbucket-public-key-regist: index.rst ./2014/github-dotfiles: index.rst ./2014/google-2step-auth-setting: index.rst ./2014/microsoft-skype-unification: index.rst ./2014/public-key-basic-config: index.rst ./2014/pypi-debut: index.rst ./2014/python-argument-intro: index.rst ./2014/python-blogger-api: index.rst ./2014/python-decorator: index.rst ./2014/python-logging: index.rst ./2014/python-module: index.rst ./2014/vagrant-error: index.rst ./2014/windows7-window-transparent-off: index.rst ./2014/wordpress-admin-common-ssl: index.rst ./2014/wordpress-image-insert-modify: index.rst ./2014/wordpress-publish-confirm: index.rst ./2014/wordpress-revision-clean: index.rst ./2014/wordpress-syntax-highlighter: index.rst ./2014/wordpress-tinymce-disable-actual-reference: index.rst ./2015: django-form mikutter-install django-genericview python-cannot-see-history-by-direction-keys flets-ocn-diversion python-colander get-aws-access-key python-mock github-pull-request python-s3-boto gnu-screen-study python-unixdomainsocket-exclusion-control mariadb-install-with-ansible rundeck-basic mercurial-history-modification ./2015/django-form: index.rst ./2015/django-genericview: index.rst ./2015/flets-ocn-diversion: index.rst ./2015/get-aws-access-key: index.rst ./2015/github-pull-request: index.rst ./2015/gnu-screen-study: index.rst ./2015/mariadb-install-with-ansible: index.rst ./2015/mercurial-history-modification: index.rst ./2015/mikutter-install: index.rst ./2015/python-cannot-see-history-by-direction-keys: index.rst ./2015/python-colander: index.rst ./2015/python-mock: index.rst ./2015/python-s3-boto: index.rst ./2015/python-unixdomainsocket-exclusion-control: index.rst ./2015/rundeck-basic: index.rst ./2016: circieci-tox-pytest python-iterator-generator-and-tshirt-me git-conflict python-metaclass git-image python-pytest git-rebase python-tox mercurial-to-git python-tox-vagrant ./2016/circieci-tox-pytest: index.rst ./2016/git-conflict: index.rst ./2016/git-image: index.rst ./2016/git-rebase: index.rst ./2016/mercurial-to-git: index.rst ./2016/python-iterator-generator-and-tshirt-me: index.rst ./2016/python-metaclass: index.rst ./2016/python-pytest: index.rst ./2016/python-tox: index.rst ./2016/python-tox-vagrant: index.rst ./2017: nekoatsume-data-migration python-posix-ipc-systemv-ipc rundeck-docker python-multiprocessing python-scope rundeck-git python-openpyxl-excel recursive-function ssh-tunnel ./2017/nekoatsume-data-migration: index.rst ./2017/python-multiprocessing: index.rst ./2017/python-openpyxl-excel: index.rst ./2017/python-posix-ipc-systemv-ipc: index.rst ./2017/python-scope: index.rst ./2017/recursive-function: index.rst ./2017/rundeck-docker: index.rst ./2017/rundeck-git: index.rst ./2017/ssh-tunnel: index.rst
画像ダウンロード
このままだと画像とかが WordPress にあるものを参照してるので 手元に持ってきて src も書き換えます。
URL = 'https://note.crohaco.net' import re import os from urllib import request, parse for d, _, fs in os.walk('.'): for f in fs: if not f.endswith('.rst'): continue rst = os.path.join(d, f) txt = open(rst, encoding='utf-8').read() for img in reversed(list(re.finditer(r'image:: ({}.*)'.format(URL), txt))): url = img.group(1) image_name = os.path.basename(parse.urlparse(url).path) image_path = os.path.join(d, image_name) res = request.urlopen(url) open(image_path, 'wb').write(res.read()) txt = txt[:img.start()] + 'image:: ' + image_name + txt[img.end():] open(rst, 'w', encoding='utf-8').write(txt)
警告
上記のコードを利用する場合 URL を自分のブログのドメインに書き換えてください。
これで reST ファイル中の画像がローカルにDLしたものに差し替わりました。
仕上げは手動で..
で、このディレクトリたちを contents/ に移動すれば概ね完了なんですが ..
docutils.utils.SystemMessage: /root/cronote/contents/2016/python-pytest/index.rst:565: (SEVERE/4) Unexpected section title.
やっぱり死にました。
どうやらブログ内に多用しまくってた table タグ内コードの変換がうまく行かずエラーになってしまったようです。
+-----------------------------------+-----------------------------------+ | test_skipif.py | py.test test_skipif.py | +===================================+===================================+ | :: | :: | | | | | # coding: utf-8 | test_skipif.py sF | | from unittest import TestCase | | | import pytest | ============================= | | | = FAILURES ====================== | | | ======== |
list-table に変換されてほしかったんですが table で出力してくれましたね..
一旦テーブル内のコードをすべて消して保存。 これでとりあえず記事のビルドが通りました。
テーブル構造部分は手動調整でがんばるしかなさそうです。鞍替えする人は一緒に頑張りましょう。(記事が多いと一日じゃ終わらないかもね..