SNSの自動投稿で日本語が文字化けした原因と防ぎ方
かつ
2026/09/29
SNSへの投稿を自動にしたら、日本語の本文が読めない記号の列になって公開されていました。2026年9月29日、Claude Sonnet 5.5の紹介動画をX・Threads・YouTubeに出したときのことです。動画はきれいに出ていて、文字だけが化けていました。
先に結論です。
- 原因は、投稿文をコマンドに渡す1行でした。Windowsでは、Pythonがパイプに出す文字はShift_JIS系(cp932)になります。それをUTF-8のつもりで受け取ったため、化けました
- SNS側はエラーを返しません。化けた本文もそのまま受け付けて公開します。画面を見るまで、誰も気づけませんでした
- 防ぎ方は2つです。本文はコマンドの引数ではなくUTF-8のファイルで渡すこと。そして、送る前に「化けた文字が混ざっていないか」を機械で確かめ、混ざっていたら止めることです
何が起きたか
カツプロでは、記事を紹介する短い動画を作り、各SNSへの投稿をAI(Claude Code)に任せています。投稿には各社の公式APIを使っています。
この日、投稿から約5時間後に、Xの画面で本文が化けているのに気づきました。

調べると、同じ動画を出したThreadsの本文と、YouTubeの説明欄も化けていました。YouTubeは、題名だけが正常でした。題名はコマンドに直接書いていて、後で説明する「ファイルから取り出す」経路を通っていなかったためです。


どの画面でも、英数字(Sonnet 5、30%、URLなど)とハッシュタグの英字は正しく出ています。化けたのは日本語だけです。これが原因を探す手がかりになりました。
原因:文字の「書き方」の取り違え
化けた文字から逆算する
XのAPIで化けた本文を取り直すと、先頭は次のようになっていました(2026年9月29日、カツプロ調べ)。
'Claude Sonnet 5.5 \x82ª\x8fo\x82܂µ\x82½\x81B'
「が」は、Shift_JIS系の文字コードcp932では 82 AA の2バイトで表します。「出」は 8F 6F です。上の文字列には、この2バイトが1バイトずつ別の文字として並んでいます。
そこで、化けた文字をバイトに戻し、cp932として読み直しました。すると「Claude Sonnet 5.5 が出ました。」に戻りました(カツプロ調べ)。日本語がcp932のバイトのまま、UTF-8のつもりで読まれていたことになります。
Threadsでは、化けた文字の一部が「�」(読めない文字の置き換え記号)で保存されていました。そのため、Xのように元の文へ戻すことはできませんでした。
問題の1行
投稿したときのコマンドを見返すと、原因はこの形でした。
TXT=$(python -c "print(投稿文.md から X の欄を取り出す)")
python xpost.py post "$TXT" --video 動画.mp4
1行目で、投稿文のファイルから本文を取り出して変数に入れます。2行目で、その変数を投稿の道具に渡します。
Pythonの公式ドキュメントには、Windowsでの出力の文字コードについてこう書かれています(Python ドキュメント「sys」の stdout の項)。
On Windows, UTF-8 is used for the console device. Non-character devices such as disk files and pipes use the system locale encoding (i.e. the ANSI codepage).
画面に出すときはUTF-8ですが、ファイルやパイプに出すときは、パソコンの地域設定の文字コードになるということです。日本語のWindowsでは、これがcp932です。手元のPython 3.11.4でパイプに出したときの文字コードを確かめると、cp932 と出ました(カツプロ調べ)。
1行目の $( … ) は、Pythonの出力をパイプで受け取ります。つまり本文は、cp932のバイトとして変数に入りました。2行目の投稿の道具は、それをUTF-8として受け取りました。ここで化けています。
手元で再現した
同じ形のコマンドを、投稿しない形で手元で試しました(2026年9月29日、Windows 11・Git Bash・Python 3.11.4、カツプロ調べ)。
TXT=$(python -c "print('Claude Sonnet 5.5 が出ました。')")
python -X utf8 -c "import sys; print(repr(sys.argv[1]))" "$TXT"
→ 'Claude Sonnet 5.5 \x82ª\x8fo\x82܂µ\x82½\x81B'
Xに出たものと、1文字も違わない化け方になりました。取り出す側に文字コードを指定すると、化けませんでした。
TXT=$(PYTHONIOENCODING=utf-8 python -c "print('Claude Sonnet 5.5 が出ました。')")
→ 'Claude Sonnet 5.5 が出ました。'
実は、投稿の道具を動かす2行目には、この PYTHONIOENCODING=utf-8 を付けていました。付け忘れていたのは、本文を取り出す1行目のほうでした。
どう直したか
直し方は、SNSごとに違いました。
| SNS | 直し方 |
|---|---|
| X | 正しい本文で動画ごと出し直し、化けた投稿はAPIで削除した |
| Threads | 正しい本文で出し直した。化けた投稿の削除はAPIで断られ、手で消した |
| YouTube | 動画はそのままにして、説明欄だけをAPIで書き換えた |
Threadsの投稿をAPIで消すには、threads_delete という権限が要ります(Threads API「Delete Posts」)。つないだときにこの権限をもらっていなかったため、「このアプリには権限がない」というエラーで断られました。
YouTubeの説明欄はAPIで書き換えられます。ただし公式ドキュメントに、指定しなかった項目は消えるという注意があります(YouTube Data API「Videos: update」)。
If you are submitting an update request, and your request does not specify a value for a property that already has a value, the property's existing value will be deleted.
説明欄だけを送ると、題名やタグが消えてしまいます。そこで、いまの題名・カテゴリ・タグを読み出してから、説明欄だけを差し替えて送りました。


同じことを起こさないために
1. 本文はファイルで渡す
日本語の本文を、コマンドの引数で渡すのをやめました。投稿の道具に「UTF-8のファイルから本文を読む」指定を足し、そちらを使います。
python xpost.py post --text-file X本文.txt --video 動画.mp4
ファイルを読むときに文字コードをUTF-8と決めておけば、途中でパイプや変数を通らないので、取り違えが起きません。
2. 送る前に機械で確かめ、化けていたら止める
X・Threads・YouTubeの3つの投稿の道具すべてに、送る直前の確認を入れました。本文に次の文字が1つでもあれば、投稿せずに止まります。
- U+0080〜U+009F の文字:ふつうの文章には出てこない制御用の文字です。今回の化け方では、ほぼ必ず混ざります
- 「�」(U+FFFD):読めなかった文字の置き換え記号です。Threadsで保存されていたものです
今回の化けた本文をそのまま渡して試すと、投稿せずに止まりました(カツプロ調べ)。
この確認は、今回と同じ種類の化け方を止めるためのものです。別の種類の化け方(たとえばUTF-8をcp932として読んだ「縺」のような漢字の列)は、この確認では止まりません。
3. Pythonの設定で、そもそもUTF-8にする
Pythonには、入出力をUTF-8にそろえる「UTF-8モード」があります。起動時の -X utf8 か、環境変数 PYTHONUTF8 でオンにできます。前に出た PYTHONIOENCODING も同じく、出力の文字コードを変える環境変数です(Python ドキュメント「sys」)。
UTF-8モードは、Python 3.15で最初からオンになります(PEP 686「Make UTF-8 mode default」)。ただし、3.14以前のPythonで動いている道具は、いまも今回と同じ化け方をします。
小さな会社・Web制作者にはどう効くか
今回いちばん困ったのは、化けたことより、約5時間だれも気づかなかったことです。SNSのAPIは、化けた本文を正しい投稿として受け付けます。エラーも警告も出ません。YouTubeの化けた説明欄は、見に行った時点で27回見られていました。
SNSの自動投稿やAIへの作業の丸投げは、手間を大きく減らしてくれます。その代わり、「送ったら終わり」になりやすく、人の目が入る場所がなくなります。
自動にするなら、次の2つをセットで入れておくのがおすすめです。
- 送る前の確認を、道具の中に入れる。人やAIが毎回気をつけるのではなく、化けていたら道具が止まる形にします
- 日本語は、ファイルで受け渡す。特にWindowsでは、コマンドの引数や
$( … )で日本語を運ばないようにします
WordPressのプラグインや業務の自動化でも、Windowsで動かすPythonのスクリプトが日本語を扱う場面は多いはずです。一度、日本語がどこを通っているかを見直してみてください。
この記事の著者・監修
カツ
カツプロ運営者のカツです! AI・Web関連から、日常のことなど、私目線の回答も交えながらお伝えします!
この記事をシェア
カツプロ(ホーム)
カツプロ AI SEO
サイト診断
MoteRaku
デザインギャラリー
画像ツール
カツプロPR