
ブログだけを運営していても、なかなか集客に繋がらないなぁと感じます。統計やGoogle Analyticsなどを見ればアクセス数はわかるのですが、読者やファンの「生きた反応」みたいなものは分かりづらいです。
ゆえに、SNSの活用が重要だとつくづく思います。SNSはアクティブユーザーが多いので、そこで宣伝をすればフォロワーへのアピールに繋がりますし、あるいは同じような活動をしている誰かと「横の繋がり」ができます。自分以外の誰かが記事をシェアして、それが拡散されることもあるでしょう。
そこで今回は、ブログとSNSの連携を強化すべく、WordPressブログの過去記事へのリンクをBlueskyに半自動的に投稿するPythonスクリプトを組んでみました。
(※この記事は過去にメインブログに投稿していた記事を修正・再構成したものです。)
モチベーション
「今日以降の任意の日の決まった時間に、SNSにブログ過去記事へのリンクを含んだポストを自動投稿したい」の一言に尽きます。
ブログ記事投稿と同時にSNSへの共有を行うWordPressプラグインは、Jetpackを筆頭に多々存在しますが、過去記事をランダムで取得し定期的にSNSへの共有をしてくれるプラグインはあまりありません。
手動で過去記事への誘導ポストをできるならそれで良いのですが、仕事や移動中などで必ずしもできるとは限りません。なので、予約投稿のような感じで決まった時間に自動的に宣伝ポストを投稿してくれるとありがたいです。
今回の記事ではXでもThreadsでもなくBlueskyへの自動投稿スクリプトを作成しましたが、理由は2つあります。
1つ目の理由は、単純に、当時僕がBluesky経由でブログの過去記事に誘導したいと思っていたからです。
Blueskyは2024年2月に招待制だったのが一般開放され、ユーザー数を増やしました。SNSの新興勢力ということで、新規ユーザー同士の交流が特に盛んに行われたので「チャンスだ」と思い僕もブログの宣伝をしたかったのですが…。新顔すぎて、WordPressとの連携が既存のプラグインではできなかったのです。
一方で、AT Protocol SDKを使ってAPI経由でポストすることは当時から可能でした。ゆえに、Pythonにて自動投稿スクリプトを実装しました。
2つ目の理由は、当記事をリライトした2026年7月時点で、スクリプトによるAPI経由での自動投稿に適したSNSがBlueskyぐらいだからです。
Xはここ数年の仕様変更の影響で、API関係がかなり煩雑になってしまいましたし、Threadsについてもリンク付きのポストを自動投稿すると結構な頻度でMetaにスパム扱いされます(1敗)。その点、BlueskyはAT Protocol SDKのPythonラッパーであるatprotoを使えば比較的容易に自動投稿を実装できます。
準備
環境

自宅のNASなど、常時起動状態の端末での実行を想定しています。
自宅にそのような端末が無い場合でも、レンタルサーバーを契約してブログを公開しているならば、レンタルサーバー上に環境を作ることで実装できます。(後ほど詳述)
Pythonのバージョンは3.12.3で実行しています。
必要パッケージのインストール
annotated-types==0.7.0
anyio==4.14.2
atproto==0.0.69
beautifulsoup4==4.15.0
bs4==0.0.2
certifi==2026.6.17
cffi==2.1.0
charset-normalizer==3.4.9
click==8.4.2
colorama==0.4.6
cryptography==46.0.7
dnspython==2.8.0
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.18
libipld==3.4.1
numpy==2.5.1
pandas==3.0.3 #手癖でインストールしましたが別に要らなかったです
pycparser==3.0
pydantic==2.13.4
pydantic_core==2.46.4
python-dateutil==2.9.0.post0
pytz==2026.2
requests==2.34.2
six==1.17.0
soupsieve==2.8.4
typing-inspection==0.4.2
typing_extensions==4.16.0
tzdata==2026.3
urllib3==2.7.0
websockets==15.0.1
XMLサイトマップ
自分のサイトのXMLサイトマップから、誘導ポストを投稿したい記事のURLを取得します。
僕のサイトでは「XML Sitemap Generator for Google」というプラグインで生成したサイトマップから記事URLを取得しています。
そもそもこのプラグインはワードプレスの記事をGoogle検索にインデックスしたいときにはほぼ必須級のものなので、ワードプレスユーザーなら入れている方のほうが多いのではないかと思います。
過去記事誘導ポスト自動投稿Pythonスクリプト
必要なパッケージがインストールされたPython環境にて、以下のPythonスクリプト「bsky_autopost.py」を実行すると、設定した時間に自動的に過去記事への誘導ポストが投稿されます。
スクリプト解説
サイトマップから記事URLを取得
requests.get()で、サイトマップから記事URLを取得します。WordPressのXML Sitemap Generator for Googleの場合、サイトマップの記事URLが記載された箇所は「hogehoge.com/post-sitemap.xml」のような形になります。
r = requests.get("http://サイトマップ.xml", timeout = 30)
r.raise_for_status() # 404 / 500 などを例外にする
text=r.text
d = []
for line in text.split("\n"):
if "" in line:
p = line.strip()
p = p.replace("","")
p = p.replace(" ","")
d.append(p)
d = np.array(d)
print("記事URLを取得しました")
if len(d) < 4:
raise ValueError("投稿に必要な4件の記事URLを取得できませんでした")
取得した文字列からURLの部分のみを切り出してリストに格納します。
Blueskyへのログイン
try:
handle = os.environ["BLUESKY_HANDLE"]
password = os.environ["BLUESKY_APP_PASSWORD"]
client = Client()
client.login(handle, password)
print("Blueskyにログインしました")
except KeyError as e:
print(f"環境変数 {e.args[0]} が設定されていません")
sys.exit(1)
except Exception as e:
print(f"Blueskyへのログインに失敗しました: {e}")
sys.exit(1)
ここでBlueskyにログインします。client.login()メソッドの引数はそれぞれ自分のBlueskyユーザー名(○○.bsky.social)とパスワードを入力します。これらの重要な情報はスクリプトに直接書くべきではありません。あらかじめ、環境変数に登録しておきましょう。
$ $env:BLUESKY_HANDLE = "(ハンドルネーム).bsky.social"
$ $env:BLUESKY_APP_PASSWORD = "(Blueskyパスワード)"
リンクカード生成のための記事タイトル・概要・サムネイル取得関数
リンクカードとは、ポストにURLを張り付けたときに表示されるカード状のリンクです。記事のタイトル、サムネイル、概要が一度に表示されるため、どういう内容のリンクなのかが一目でわかります。
def get_title_and_description(url:str):
title : str = ''
description : str = ''
thumbnail : str = ''
response = None
try:
# requestsで対象のURLに対してGET
response = requests.get(url, timeout=60)
except:
return '', '', ''
if response.status_code != 200:
return '', '', ''
# responseに含まれるテキストデータを、HTMLパーサで処理
soup = BeautifulSoup(response.text, 'html.parser')
# titleタグ内のテキストを取得
result = soup.find('title')
if result != None:
title = result.text
# metaタグdescription内のテキストを取得
result = soup.find('meta', attrs={'name': 'description'})
if result != None:
description = result.get('content')
# metaからサムネイルを取得
result = soup.find('meta', attrs={'name': 'thumbnail'})
if result != None:
thumbnail = result.get('content')
# タプルでまとめて返す
return title, description, thumbnail
ブログ記事から記事タイトル、メタディスクリプション、サムネイル画像リンクを取得する関数を定義しました。この関数は参考リンクのMimaki SiON様の記事を参考に、サムネイル画像リンクを取得する処理を追加しています。
自動ポストの実行
rng = np.random.default_rng()
link_selected = d[rng.choice(len(d), 4, replace = False)]
posttime = ["11:00", "12:00", "13:00",
"14:00"]
このスクリプトでは、誘導ポストが一日に4回、posttimeリストにて指定した時間(今回は11:00、12:00、13:00、14:00)に自動投稿されます。(あまり頻繁にしても鬱陶しいです。1日4回でも多いぐらいかも…。)
投稿する記事のURLは、np.random.default_rng()でランダムに4つ選ばれます。
day = datetime.datetime.now(pytz.timezone("Asia/Tokyo")).date()
day = str(day)
datetime.datetime.now()から、今日の日付(日本時間)を取得します。
# タイトル等取得
title, description, thumbnail = get_title_and_description(link_selected[i])
# 画像アップロード(画像をバイナリに変換しアップロード)
response = requests.get(thumbnail)
img = io.BytesIO(response.content)
upload = client.com.atproto.repo.upload_blob(img)
先ほど定義した関数get_title_and_description()で、記事のタイトル、概要、サムネイルを取得します。
取得したバイナリデータのサムネイルをio.BytesIO()で仮想ファイルとし、client.com.atproto.repo.upload_blob()でBlueskyにアップロードします。
# リンクカード作成
embed_external = models.AppBskyEmbedExternal.Main(
external = models.AppBskyEmbedExternal.External(
title = title,
description = description,
thumb = upload.blob,
uri = link_selected[i]
)
)
models.AppBskyEmbedExternal.Main()は、Blueskyのポストに表示されるリンクカードを生成する関数です。先ほど取得した記事のタイトル、概要、サムネイルからリンクカードを生成します。
post = client_utils.TextBuilder().text('【ブログ過去記事】\n【自動投稿】\n\nたまには過去記事をどうぞ!
').tag('#ブログ', 'ブログ')
client_utils.TextBuilder()で、text()のカッコの中にはポストの本文を記入します。tag()でハッシュタグを付与できます。tag()の第1引数はポストに表示されるハッシュタグの文字列、第2引数はBlueskyに実際にハッシュタグとして登録される文字列です。
start_time = datetime.datetime.strptime(day + posttime[i] + "+0900", '%Y-%m-%d%H:%M%z')
time_second = (start_time - datetime.datetime.now(pytz.timezone('Asia/Tokyo')))
if time_second.days < 0:
client.send_post(post, embed = embed_external)
print(f"{i+1}本目のポストを投稿しました")
else:
time.sleep(time_second.seconds)
client.send_post(post, embed = embed_external)
print(f"{i+1}本目のポストを投稿しました")
client.send_post()は、client_utils.TextBuilder()で定義したポストの内容を実際に投稿する関数です。embed=embed_externalで、ポストにリンクカードが添付されます。
このスクリプトを動かした時間から、time.sleep()で投稿予定時刻まで待機したのち、client.send_post()が実行されます。


実際にスクリプトを動かし、挙動を確認してみました。想定通りに、指定した時刻にポストがBlueskyに自動投稿されました。
レンタルサーバーでの実装(エックスサーバー)
このスクリプトは一度実行したら、ポストが実際にBlueskyに投稿されるまで起動させ続けなければなりません。ゆえに、常時起動状態の端末で実行する必要があります。
WordPressのブログをレンタルサーバーで運営している場合は、レンタルサーバー上に実行環境を作るのが良いです。今回は僕がメインブログ用に契約しているエックスサーバーでの方法を解説します。

Pythonスクリプトはあらかじめ、ファイルマネージャでサーバーのホームディレクトリにアップロードしておきましょう。
レンタルサーバーにSSH接続

まずはエックスサーバーのサーバーパネルにアクセスし「アカウント」の「SSH設定」より、SSHをONにします。

続いて、任意のパスフレーズを入力し、秘密鍵ファイルを生成・ダウンロードします。
サーバーへのFTP接続に使用可能なターミナルソフトは様々ですが、今回はPuTTYを使用しました。

先ほどダウンロードした秘密鍵ファイルをPuTTYにて使用可能な.ppk形式に変換するため、PuTTYgenを起動します。「Load an existing private key file」の「Load」ボタンを押し、秘密鍵ファイルを選択すると、秘密鍵ファイル生成時に入力したパスフレーズを求められるので、それを入力します。
その後「Save the generated key」の「Save private key」ボタンを押すと.ppk形式の秘密鍵を保存できます。
PuTTYを起動し「Connection」⇒「Auth」⇒「Credentials」と開きます。

一番上の「Browse」をクリックし.ppk形式の秘密鍵ファイルをロードします。

「Session」に戻り「Host Name」に(サーバーID).xsrv.jpと入力します(サーバーIDは各自のもの)。ポートはデフォルトでは22と入っていますが、そこを10022とします。

「Open」をクリックすると、ターミナルが起動します。
login as: にサーバーIDを入力しエンターを押すと、今度は秘密鍵のパスフレーズを求められます。パスフレーズを入力して再びエンターを押すと、ここでようやくサーバーを操作できるようになるわけです。
なお、エックスサーバーの仕様上、入力を間違えすぎると一定時間アクセスが拒絶されターミナルを開けなくなるので気を付けてください(1敗)
Minicondaで環境構築
エックスサーバーにはデフォルトでPythonが入っていますが、バージョンは3.6です。そして、BlueskyのAPIであるatprotoパッケージを動かすにはPython3.7.1以降のバージョンが必須です。しかしながら、ユーザーはエックスサーバーにroot権限でアクセスできないため、何らかの手段で別のPythonを入れる必要があるわけです。
そこで、Minicondaをインストールし、仮想環境を構築します。
$ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
$ sh ./Miniconda3-latest-Linux-x86_64.sh
wgetコマンドでAnacondaインストーラーをダウンロードし、インストーラーのシェルスクリプトを読み込んでインストールします。
$ conda create -n bluesky
$ conda activate bluesky
$ conda install -c conda-forge numpy BeautifulSoup4 requests pytz atproto
この記事の初版時点ではatprotoパッケージはcondaでインストールできなかったため、pipでインストールしていましたが、2026年7月現在はatprotoもconda-forgeライブラリに追加されているのでcondaからインストール可能です。
Cronの設定

サーバーパネルの「アカウント」⇒「Cron設定」より、定期的に実行するプログラムを追加できます。
分、時間、日、月、曜日単位で設定ができます。「毎時」や「毎日」実行したい場合は、時間や日のところに「*」を入力します。今回は分に30、時間に7、それ以外に*を入力したことで「毎日朝7時30分にコマンドを実行する」という設定になっています。
コマンドのところには、指定したい時間に実行するコマンドを入力します。
#!/bin/bash
source /home/(サーバーID)/anaconda3/etc/profile.d/conda.sh
conda activate bluesky
python3 /home/(サーバーID)/bsky_autopost.py
今回は上記のシェルスクリプトを「autopost.sh」という名前で保存し、それをCronで毎日朝7時半に定期実行させています。このシェルスクリプトは仮想環境の読み込みと、先ほどの自動投稿用Pythonスクリプト「bsky_autopost.py」の実行の両方を行うスクリプトとなっています。
あとがき
ブログの過去記事は言うなれば「長い時間をかけて積み上げた財産」です。SNSから過去記事に誘導することで、改めて日の目を見るかもしれません。しかしながら、この用途に適したWordPressプラグインって意外と無いんですよね…。一応、Xで同じようなことのできる「Revie Social(旧名称はRevive Old Posts)」というプラグインはありますが、現在はこれを利用するにはXのAPIクレジットについても考慮する必要があります。
当記事初版の執筆は2024年4月でしたが、リライトした2026年7月時点では、当記事のスクリプトと同じようなことのできる「Revive To Sky」というプラグインがリリースされています。なのでそれをWordPressにインストールすれば一番手っ取り早い…とはいえ、いろいろと試行錯誤した記録をこのまま「お役御免」と破棄してしまうのも忍びなく感じたので、今回、最新版にリライトした次第です。