WordPress:pythonのAPIサーバーでスクレイピング

WordPress:pythonのAPIサーバーでスクレイピング

Amazon の PA−API の利用条件が厳しくなり商品広告を出す手間が大変になりましたのでスクレイピングを考えようと思います。事情は過去記事をご覧ください。

01プログラムの構想

これまで使っていた PA−API 仕様の「Amazonアソシエイトリンク作成プラグイン」と同様にショートコード[aal (ASIN または ISBN)] から呼ぶようにします。

プログラムの流れは、

  1. ショートコードのコールバック関数で引数の asin を受け取り、キャッシュがあればデータベースから商品データを読み出して出力する
  2. キャッシュがなければ PA−API で商品データを取得して出力し、データベースに保存する
  3. PA−API からデータが返ってこなければ python に asin を送る
  4. python でのスクレイピング作業は重くなるので WordPress は結果を待たずに非同期で処理を進める
  5. python では asin をもとに商品ページのスクレイピングをし、データベースに保存して終了する

という構想でいきます。

WordPress 関連のプログラムは python にデータを投げる部分以外はすでにあるプラグインと同じですので、今回は 3, 4, 5 を作成します。

AI を使ってコードを書いてみる

コード生成 AI という専用のものもあるようですが、とりあえずは Google の Gemini に質問しながらやってみようと思います。

02exec() 関数か python の API サーバーか

まず、WordPress から python にどうやってデータを送るかを決めます。それによって python 環境の構築方法が変わります。

質問は、

WordPress から python にデータを送ってスクレイピングする場合、送り方は exec() 関数か、API サーバーか、どちらがいいですか

Gemini の答は、

WordPress から Python にデータを送ってスクレイピングを行う場合、API サーバー(HTTP 通信)経由での実行を推奨します。
(略)
3. 実装のヒント

  • API サーバーの構築: Python 側で FastAPI や Flask を使い、エンドポイントを作成します。WordPress からは wp_remote_post() 関数などを使用して HTTP リクエストを送ります。
  • スクレイピングの注意: Python 側では Requests や BeautifulSoup、Playwright などがよく使われます。対象サイトの利用規約を確認し、サーバーへの過度な負荷を避けるように設計してください。 

結論: 開発の初期段階で手間はかかりますが、API サーバー方式を選択することで、将来的な安全面と運用のしやすさが格段に向上します。

さらに python のフレームワークに FastAPI がいいか、Flask がいいかを尋ねてみたところ、非同期処理が実装されていることやスピードや安全性の面で FasAPI を推奨してきます。もちろん Flask に利点があることも記載されています。

03Xserver に python の API サーバーを立てる

Gemini には高速モード、思考モード、Pro の3パターンの AI があるようですので Pro に聞いてみました。Pro を無料で使うには制限があるみたいです。

質問は、

Xserverの共用サーバーにMinicondaをインストールし、仮想環境を作成し、FastAPIサーバーを立て、WordpressからそのAPIサーバーにデータを送って非同期で動かすための手順とコードを教えてください

Miniconda は Python 本体とパッケージ管理および環境管理ツールである conda をパッケージにしたもので Anaconda の軽量版です。Xserver には標準で python3.6 がインストールされていますが、pip が使えませんの必要なライブラリを入れることが出来ません。同様のツールは他にもありますが、情報も多い Anaconda 系がいいと思います。

そして、Gemini Pro の答は、

Xserver(エックスサーバー)のような共用レンタルサーバー環境で、Minicondaを用いてFastAPIを動かし、WordPressと連携させるのは少し高度ですが、非常に強力な構成です。

重要な前提と注意点:

  • ポート制限: Xserverのファイアウォールにより、外部(インターネット)から直接 http://your-domain.com:8000 にアクセスすることはできません。しかし、**サーバー内部(localhost)**であれば通信可能です。つまり、WordPress(PHP)からFastAPIへの通信は成功します。
  • プロセスの永続化: 共用サーバーには systemd などの管理者権限が必要なツールがないため、nohup コマンドを使ってバックグラウンド実行させます。サーバーのメンテナンスやリソース過多でプロセスがキルされる可能性があるため、本格運用の際はプロセス監視(Cronで生存確認して再起動など)が必要です。

以下にステップバイステップの手順とコードを示します。

  1. SSH接続とMinicondaのインストール
  2. 仮想環境の作成とFastAPIの準備
  3. FastAPIサーバーのコード作成
  4. サーバーの起動(バックグラウンド実行)
  5. WordPress側のコード (PHP)
  6. 運用とメンテナンス

という概要とともに、この後に具体的な手順が表示されます。かなり詳しいです。その手順に従ってやってみましょう。

手順 1: SSH接続とMinicondaのインストール

Xserver に SSH でログインし、ホームディレクトリで作業します。Gemini はインストーラー Miniconda3-latest-Linux-x86_64.sh をダウンロードするように言ってきますが、インストーラーを実行しますと、

$ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
$ bash Miniconda3-latest-Linux-x86_64.sh
Installer requires GLIBC >=2.28, but system has 2.17.

と、GLIBC の 2.28 以上が必要となりインストールできません。現在の Xserver の GLIBC は 2.17 です。Miniconda の過去のバージョンを使うしか手がありません。

上のサイトから Miniconda3-py39_25.7.0-2-Linux-x86_64.sh をダウンロードしてインストールします。

$ wget https://repo.anaconda.com/miniconda/Miniconda3-py39_25.7.0-2-Linux-x86_64.sh
$ bash Miniconda3-py39_25.7.0-2-Linux-x86_64.sh
(略)すべて yes またはエンターで進む

$ source ~/.bashrc
(base) [*]$ python --version
Python 3.9.23
(base) [*]$ conda --version
conda 25.7.0

手順 2: 仮想環境の作成とFastAPIの準備

FastAPI 用の仮想環境を作成します。

(base) [*]$ conda create -n fastapi-env python=3.9 -y
CondaToSNonInteractiveError: Terms of Service have not been accepted for the following channels. Please accept or remove them before proceeding:
    - https://repo.anaconda.com/pkgs/main
    - https://repo.anaconda.com/pkgs/r
(略)

エラーが出ます。「以下のチャンネルの利用規約に同意していません」と言っています。エラーメッセージでググりますと、環境変数に CONDA_PLUGINS_AUTO_ACCEPT_TOS=true を追加すればエラーは出なくなるそうです。

export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true

これで仮想環境を作成できるようになりました。途中で conda のバージョンを 25.11.1 にしなさいと言ってきますがインストールできないんですからどうしようもないですね。

次に仮想環境 fastapi-env を有効化して必要なライブラリをインストールします。

(base) [*]$ conda activate fastapi-env
(fastapi-env) [*]$ pip install fastapi uvicorn
(略)

(fastapi-env) [*]$ which python
~/miniconda3/envs/fastapi-env/bin/python

仮想環境を有効化しますとプロンプトの先頭に環境名 (fastapi−env) が入ります。最後の行はその環境下の python のパスを調べておきます。

手順 3: FastAPIサーバーのコード作成

公開しているドメイン内にディレクトリを作成して必要ファイルを配置していきます。

$ cd ~/yourdomain/public_html/
$ mkdir my_fastapi_app
$ cd ~/yourdomain/public_html/my_fastapi_app/

FastAPI 本体となる main.py です。Gemini Pro が教えてくれたコードそのままです。

# main.py
from fastapi import FastAPI, BackgroundTasks, Request
from pydantic import BaseModel
import time
import logging

# ログ設定(Xserver上で動作確認するためファイルに出力)
logging.basicConfig(filename='app.log', level=logging.INFO)

app = FastAPI()

# 受け取るデータの定義
class WPData(BaseModel):
    post_id: int
    title: str
    content: str = None

# 重い処理をシミュレーションする関数
def process_data_task(data: WPData):
    logging.info(f"Start processing Post ID: {data.post_id}")
    # ここに実際の重い処理(AI解析、画像生成、外部API送信など)を書く
    time.sleep(10) # 10秒かかる処理を想定
    logging.info(f"Finished processing Post ID: {data.post_id}")

@app.post("/webhook")
async def receive_wp_data(data: WPData, background_tasks: BackgroundTasks):
    # バックグラウンドタスクとして登録(レスポンスは即座に返り、処理は裏で続く)
    background_tasks.add_task(process_data_task, data)
    
    return {"status": "accepted", "message": f"Processing Post ID {data.post_id} in background"}

@app.get("/")
async def root():
    return {"message": "FastAPI is running on Xserver"}

WordPress からのデータを受け取り非同期で実行します。WordPress には即座にレスポンスを返します。

手順 4: サーバーの起動(バックグラウンド実行)

Xserver などの共有サーバーではターミナルを閉じるとプロセスが終了してしまいます。そのためターミナルを閉じても終了しないよう nohup を使って起動します。また、Xserver のファイアウォールにより外部からポート 8000番などへ直接アクセスはできませんが、サーバー内部の通信であればポート指定でつながります。

と、Gemini が言っていますのでやってみましょう。

nohup uvicorn main:app --host 127.0.0.1 --port 8000 > server.log 2>&1 &

# --host 127.0.0.1: 外部からの直接アクセスを遮断し、サーバー内部(WordPress)からのみアクセス可能にします(セキュリティ対策)。
# &: バックグラウンドで実行します。

動作確認

curl http://127.0.0.1:8000/
# {"message":"FastAPI is running on Xserver"} と返ってくれば成功

確かに Xserver から一度抜けてもサーバーは稼働しています。nohup で起動したプログラムを停止するにはプロセス ID を調べて kill するしかないようです。

$ ps -e
    PID TTY          TIME CMD
 311344 pts/1    00:00:00 bash
 312142 pts/1    00:00:00 ps
3264002 ?        00:00:34 uvicorn
$ kill 3264002
$ ps -e
    PID TTY          TIME CMD
 311344 pts/1    00:00:00 bash
 319842 pts/1    00:00:00 ps

手順 5: WordPress側のコード (PHP)

Gemini Pro は、WordPress の記事保存時に FastAPI にデータを送るコードも提供してくれています。 wp_remote_post() のパラメータに 'timeout' => 2, 'blocking' => true を指定して、FastAPI からのレスポンスを 2秒待つ設定になっています。とりあえずはこのコードで試してみますが、レスポンスを待つ必要がなければ 'timeout' => 0.01, 'blocking' => false にすれば直ちに次の処理に進みます。

/**
 * 記事公開時にFastAPIへデータを送信する
 */
function send_to_fastapi_on_publish($ID, $post) {
    // 記事のステータスが 'publish' かつ、更新ではなく新規公開または更新時のみ実行したい場合などの条件分岐
    if ($post->post_status !== 'publish') {
        return;
    }

    // 送信するデータ
    $body = [
        'post_id' => $ID,
        'title'   => $post->post_title,
        'content' => $post->post_content, // 必要に応じて送信
    ];

    // FastAPIのエンドポイント (localhost指定)
    $url = 'http://127.0.0.1:8000/webhook';

    // リクエスト送信
    $response = wp_remote_post($url, [
        'headers'     => ['Content-Type' => 'application/json'],
        'body'        => json_encode($body),
        'timeout'     => 2, // 2秒でタイムアウト(PHP側を待たせないための保険)
        'blocking'    => true, // レスポンスを受け取る場合はtrue。完全に投げっぱなしならfalseでも良い
        'data_format' => 'body',
    ]);

    // エラーハンドリング(開発用ログ出力)
    if (is_wp_error($response)) {
        error_log('FastAPI Connection Error: ' . $response->get_error_message());
    } else {
        error_log('FastAPI Response: ' . wp_remote_retrieve_body($response));
    }
}

// アクションフックに登録 (publish_post だと公開時のみ、save_post だと下書き保存時も動くので注意)
add_action('publish_post', 'send_to_fastapi_on_publish', 10, 2);

WordPress でエラーログを確認するには、wp-config.php の設定を変更します。

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );

これで wp-content/debug.log に出力されます。

手順 6: 運用とメンテナンス

何らかの理由で FastAPI のプロセスが停止してしまった場合に自動的に再起動するスクリプトも提示してくれています。シェルスクリプト start_server.sh を cron で実行するものです。

#!/bin/bash
source /home/ユーザーID/miniconda3/bin/activate fastapi-env
cd /home/ユーザーID/my_fastapi_app

# ポート8000が使われていなければ起動
if ! lsof -i :8000 > /dev/null; then
    nohup uvicorn main:app --host 127.0.0.1 --port 8000 > server.log 2>&1 &
    echo "Server started"
fi

実行権限を付与します。

chmod +x start_server.sh

cron の時間指定は 15分毎くらいで大丈夫でしょう。

ということで WordPress から FastAPI サーバーにデータを送って処理するコードが AI Gemini によって完成しました。さらに次の提案までしてくれます。

次のステップとしてはいかがでしょうか? FastAPI側で処理した結果(例えばAIによる要約など)を、WordPressのデータベースに書き戻して記事を更新する処理を追加するコード例を作成しましょうか?

今回の場合は python 側でスクレイピングしてデータベースに保存する予定ですのでとりあえずはここまでです。