概要
Python requestsを使って,この記事とその画像をqiitaに自動投稿する.
ソースコード(参考用): https://github.com/6e5d/qiita_post.
自動投稿モデル
緑色ノード = クライエント保存情報.
A. regexなどを利用してローカルファイルリンクを検出.
B. 保存したS3 URLと更新時間(timestamp)より新しい,またはS3に存在しなかった画像を集める.
C. (下記参照)
D. ローカルファイルリンクをS3 URLに置き換える.
E. (下記参照)
C. 画像アップロード
注意: 公式APIに入られなかったので,破壊的な変更が行われる可能性がある.
参考: https://qiita.com/yuku_t/items/40b7daf018d3dab48974
Qiitaの投稿には2M文字上限があるので(参考: https://qiita.com/misohagi/items/39c4f9921fdc009817b5), HTMLの中に直接画像エンコード(base64など)は向いてない. qiita.com
は画像などのサーバーではなく, amazon S3をアップロードして生成したリンクを埋め込むということです.
まず,アップロードしたいの画像情報をqiitaのサーバーに提出した後, qiita返信からS3サーバーの目標URLなどを抽出. 具体的には,画像タイプ,名前,サイズを含むjsonデータを https://qiita.com/api/upload/policies
にPOST:
# stage1: qiita.comと通信
def stage1(token, img):
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
}
data = {"image": {
"content_type": "image/png",
"name": "test.png",
"size": len(img),
}}
resp = requests.post(
"https://qiita.com/api/upload/policies",
data = json.dumps(data),
headers = headers,
)
assert resp.status_code == 200
content = resp.content
resp.close()
j = json.loads(content.decode())
return j
返信dataj
のj["form"]
部分はmultipart/form-data filesを転換し (requestsを使えば,自動的にboundaryなどを生成する), 画像データを加えてj["upload_url"]
にPOST. 生成されたurl(Location)は返信Headersに含まれる:
# stage2: S3と通信
def stage2(j, img):
url = j["upload_url"]
files = j["form"]
for name, value in files.items():
files[name] = (None, value)
files["file"] = ("test.png", img, "image/png")
resp = requests.post(url, files = files)
# print(resp.status_code)
headers = resp.headers
resp.close()
loc = headers["Location"]
return loc
E. Markdownファイルを投稿
公式API: https://qiita.com/api/v2/docs#投稿
参考: https://qiita.com/kai_kou/items/663d3f7bbc4da4ccf62d.
-
投稿本文フォーマットは2種類Markdown(
body
)とHTML(rendered_body
)がある. -
POST=投稿,PATCH=更新,DELETE=削除
-
返信bodyから投稿URLを抽出して保存(更新にはURLが必要)
def post(prev_url, text, title, tags):
data = {
"body": text,
"private": True,
"title": title,
"tags": [{"name": tag, "versions": []} for tag in tags]
}
import requests
token = open("token.txt").read().strip()
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
}
if prev_url == None:
print(f"new post")
resp = requests.post(
"https://qiita.com/api/v2/items",
headers = headers,
data = json.dumps(data),
)
else:
print(f"update {prev_url}")
resp = requests.patch(
prev_url,
headers = headers,
data = json.dumps(data),
)
print(resp.status_code)
print(resp.headers)
j = json.loads(resp.content.decode())
# 毎回セーブするけど,記事IDは更新後も変更されない
with open("qiita/prev_url.txt", "w") as f:
print(j["url"], file = f)
画像の更新と削除について
画像削除API(ドキュメントなし)は複雑で不安定ですが, 100Mストレージは余裕があると思うので,いまのところ実装しない方がいい. クライエント側から管理するより, QiitaにはGarbage Collection機能 (リンクされていない画像は一定時間後に自動またはまとめて削除) を実現することを望む.
230916 更新
今は(Makefileみたいな)epoch比較ではなく, binaryファイルがVCSで管理されて安定的なハッシュ比較を実装している.