Table of Contents
手間なく投稿したい
メモ書きは全部Markdownで保存しているので、ブログ記事の下書きもMarkdownで書いている。
日記のうち「これだ!」と思ったものをちょっと書き換えてブログ記事にする感じ。
Markdownで書いたものを投稿画面からそのまま貼り付けることもできるが、
投稿状態をもっと一元化して管理したいっていう欲求と、
- (例によって)回線が激重すぎて投稿画面になかなか遷移できない
- 投稿画面の「公開」を押してもずっと固まった状態になることがある
- アイキャッチやスラッグをいちいち入れるのが面倒
などの面倒があって、WP REST APIを使って投稿することにした。
用意するもの
Application Passwordsから投稿用パスワードを取得
WordPressプラグインのApplication Passwordsを使って投稿用のパスワードを取得する(後で使うのでちゃんと手元にメモしておく)。
こちらの記事を参考にさせていただきました。
【WP REST API】Word Press APIのパスワード設定方法 | もじとばコム
投稿下書きを保存するディレクトリ(フォルダ)構成
まずはMarkdownメモをこんな感じでフォルダに入れる。
.├─done (投稿し終わったファイル)├─drafts (下書き)├─media (画像とか)└─stage (書き終わって投稿前のファイル.このフォルダに後述のpost.pyを入れる)下書きとなるMarkdown側の設定
ブログ下書きにするMarkdownファイルには下のようなyamlヘッダを先頭に書いておく。
- テンプレートに入れるか、スニペット(定型文)に登録しておくととても楽。
- タグIDカテゴリIDを控えておくと楽。
---title: # 表題date: # 日付tags: ["0", "1"]categories: ["2"]id: # 初回投稿のときは空でOKslug: #パーマリンクfeatured_media: #アイキャッチ画像# ここらへんに自分用メモ---### 本文ここらへんに本文...Python
比較的簡単なプログラミング言語。今のところこれしか書けないのでこれで書く。
持っていなければここの「Downloads」からダウンロードしてインストールする。
pandoc
多種のドキュメントを形式変換できるプログラム。 持っていなければここからダウンロードしてインストールする。(インストーラを使うのが楽)
投稿用のプログラムを書く
- 投稿については
- yamlヘッダ付きのMarkdownファイルをpandocでhtmlに変換して投稿内容として読み込み
- 元のMarkdownのyamlヘッダから投稿情報を作成
- 投稿し終わった記事を別フォルダへ移動
- 画像については
- フォルダ内にある画像をとりあえず順にアップ
だいたい上のような処理で書いてみた。
この内容をメモ帳にコピペしてpost.pyという名前でstageフォルダに保存する。
【ここを直す】のところは適宜修正要。
import requests, os, shutil, subprocess, yaml, jsonfrom datetime import date, datetime,time
def date_to_datetime(obj): # dateからdatetimeに変換 if isinstance(obj, (date)): obj = datetime.combine(obj, time()) return obj.isoformat()
class WPPost: def __init__(self): # WP投稿に必要な情報 self.user_id_ = "【ここを直す】" # ユーザーID self.password_ = 'xxxx xxxx xxxx xxxx xxxx xxxx' # 【ここを直す】APIパスワード self.baseurl_ = "http://【ここを直す】.com/" # 投稿先サイトアドレス # ローカル環境では"http://localhost:8000/"
def post(self,filename): # 記事idが付与されていれば更新、そうでなければ新規post end_point_url = self.baseurl_ + "wp-json/wp/v2/posts"
# 本文を指定(htmlから) with open (os.path.splitext(filename)[0] + ".html", encoding='utf-8') as f: content = f.read()
# 基本パラメータ設定 payload = { 'status' : 'draft', #下書き 'comment_status': 'closed', #コメント欄非表示 'content' : content, } # 本文以外(mdのyamlヘッダから) with open(os.path.splitext(filename)[0] + ".md", encoding='utf-8') as file: objs = yaml.safe_load_all(file)
for obj in objs: # 本当は最初のstreamだけ番号で取り出したかったができなかった print(obj) info = obj break
# パラメータをまとめる payload.update(info)
# featured_mediaが数字で指定されていなければidを探す if payload['featured_media'].isdecimal() == False: r = self.get_media(1,os.path.basename(os.path.splitext(payload['featured_media'])[0])) if r.status_code == requests.codes.ok: get_id =r.json() payload['featured_media'] = get_id[0]['id'] print("media hit:{}".format(payload['featured_media'])) else: # 上がってなかったらアップロード r = self.upload_media(payload['featured_media']) if r.status_code == requests.codes.ok: # print(get_id) get_id = r.json() payload['featured_media'] = get_id['id'] print("media not found:{}".format(payload['featured_media'])) else: print("media err:{}".format(payload['featured_media'])) payload.pop('featured_media')
# 記事の投稿 try: if info['id'] > 0: #既にIDのついている記事は上書き end_point_url += '/' +str(info['id'])
finally: # 投稿 headers_ = {'content-type': "Application/json"} r = requests.post( end_point_url, data=json.dumps(payload, default=date_to_datetime) , headers=headers_, auth=(self.user_id_, self.password_) )
# 確認 print(r.status_code) # 201:成功 return r
def upload_media(self, mediapath): end_point_url =self.baseurl_ + "wp-json/wp/v2/media" headers_ = { "Content-Disposition": "attachment; filename=" + os.path.basename(mediapath) , "Content-Type": "application/octet-stream"}
with open(mediapath, 'rb') as f: payload = f.read()
r = requests.post(end_point_url, data=payload, headers=headers_, auth=(self.user_id_, self.password_)) return r
def get_post(self,page,keyword): # 新しい投稿のidを表示 end_point_url =self.baseurl_ + "wp-json/wp/v2/posts" r = requests.get(end_point_url,params={'page': page,'search': keyword}) return r
def get_media(self,page,keyword): # 既にアップされたメディアを取得(name検索) end_point_url =self.baseurl_ + "wp-json/wp/v2/media" r = requests.get(end_point_url,params={'page': page,'search': keyword}) return r
def Main(): # 実行フォルダをこのファイルがあるフォルダに変更 os.chdir(os.path.dirname(os.path.abspath(__file__)))
wp_post = WPPost()
for filename in os.listdir(): print(os.path.join(os.path.abspath(__file__), filename)) if filename.endswith(".md") : # pandocでmdからhtmlへ変換 pandoc_path = r"C:\Users\【ここを直す】\AppData\Local\Pandoc\pandoc.exe" # pandocの場所
subprocess.run([pandoc_path,"-f", "markdown","-t","html", filename, "-o",os.path.splitext(filename)[0] + ".html"])
# 投稿処理 result = wp_post.post(filename)
if result.status_code == requests.codes.ok: # 最新postのidを取得 newpost = result.json() print("newpost id:{0}, featured_media: {1}".format(newpost['id'],newpost['featured_media']))
# 投稿完了ファイルをdoneに移動 shutil.move(filename, '../done/')
# htmlは不要なので削除 os.remove(os.path.splitext(filename)[0] + ".html")
elif (filename.endswith(".png")) or (filename.endswith(".jpg")): # 画像の投稿 result = wp_post.upload_media(filename) if result.status_code == requests.codes.ok: # idを取得 newmedia = result.json() print("newmedia id:{}".format(newmedia['id']))
# 投稿完了ファイルをmediaに移動 shutil.move(filename, '../media/') else: continue
if __name__ == '__main__': Main()投稿手順
- 書き終わった記事のMarkdownと、アップする画像をstageフォルダに移動する。
- stageフォルダのpost.pyを実行する。
Visual Studio Codeから実行することもできる。 アップした記事のidなどが標準出力に出てくるはず。 - 上記のプログラムだと下書き状態で投稿されるので、公開前にブラウザで内容確認。
もし公開ボタンを押せなくても、内容が問題なければ記事一覧に戻って「クイック編集」からタグをつけたり公開したりすることができる。
まとめ
もうちょっと洗練させたいのですが、完璧を目指してたら進まないので、今はこんなもんかな……
投稿画面が開くまで待たなくていいし、
記事を投稿したのかどうなのかフォルダの状態でわかるので管理も楽です。
参考リンク
こちらの記事を参考にさせていただきました。