ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English

Zenn記事の一括移転通知設定 — Playwrightと内部APIによる508記事の自動処理

Zennで公開していた900件以上の記事を自前のHugoブログ(tech.ldas.jp)に移行しました。移行後、Zenn側に元の本文が残っていると重複コンテンツとしてSEO上の問題が生じるため、Zenn側の記事を移転通知に置き換える必要がありました。 Zennの記事編集手段 Zennでは、GitHub連携を使っていない場合、記事のCRUD APIは公開されていないようです。そのため、Playwrightでブラウザ操作を自動化する方針にしました。 ただし、ZennのログインにはGoogleアカウントを使っており、Googleログインは自動化ブラウザ(Playwright等)からのアクセスをブロックします。そこで、通常のブラウザで手動ログインし、DevTools > Application > Cookies から _zenn_session と remember_user_token の2つのCookieを取得しました。 import json cookies = [ {"name": "_zenn_session", "value": "取得した値", "domain": "zenn.dev", "path": "/"}, {"name": "remember_user_token", "value": "取得した値", "domain": "zenn.dev", "path": "/"}, ] with open("zenn_cookies.json", "w") as f: json.dump(cookies, f) 編集ページの構造と内部API Zennの記事編集ページのURLは https://zenn.dev/articles/{slug}/edit です(ユーザー名は含まれません)。エディタにはCodeMirror(div.cm-content)が使われています。 当初はPlaywrightのキーボード入力で本文を書き換えようとしましたが、Zennの自動保存が発火しませんでした。DevToolsのNetworkタブを確認したところ、内部API PUT /api/articles/{slug} でリクエストボディ { "article": { "body_markdown": "..." } } を送信することで本文を直接更新できることがわかりました。 Playwrightのブラウザコンテキスト内から page.evaluate() を使ってこのAPIを呼び出す形にしました。Cookie認証がブラウザコンテキストに紐づいているため、fetch をページ内で実行する必要があります。 async def update_article(page, slug, new_body): result = await page.evaluate(""" async ({slug, body}) => { const res = await fetch(`/api/articles/${slug}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({article: {body_markdown: body}}) }); return {status: res.status, ok: res.ok}; } """, {"slug": slug, "body": new_body}) return result 一括処理の実行 Zennの公開API GET /api/articles?username=nakamura196&count=100&order=latest で記事一覧を取得できます。ページネーションで全件取得し、いいね数(liked_count)が0件の508記事を対象にしました。 ...