WordPress API 経由で記事一覧を得る

C++ Builder CE Rest API プログラミング

まずはcurlで小手調べ、なんらかのAPIを叩く場合には、curlで試してみて、検証確認するのが吉です。というか筆者はほぼそうしています。挙動を見て、C++ Builder CEのindyから”get”します。http[s]なGetならば、パラメーターを調べておきます。さて、御託はそのくらいにして動かして見ましょうかね。Git for WindowsのBashで使えるcurlとWindowsのコマンドプロンプトから使えるcurlとありますが、筆者は前者を使います。jqとかも普通は併用するからです。さて、

記事データは postsエンドポイントから取得できます。
/wp-json/wp/v2/posts
例
https://example.com/wp-json/wp/v2/posts

ということなので、当家では、

https://ghost.mydns.jp/wp-json/wp/v2/posts

外からこのurlへのアクセスを禁止するのは.htaccessでやるのが簡単ですかね。ま当面禁止しませんけどね。アタックするような悪い人は読者にはいないという前提ですね。

$ curl https://ghost.mydns.jp/wp-json/wp/v2/posts
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>

あらま404でちゃいました。wpが抜けていましたね。

$ curl https://ghost.mydns.jp/wp/wp-json/wp/v2/posts
[{"id":11701,"date":"2026-08-25T03:22:00","date_gmt":"2026-08-24T18:22:00","guid":{"rendered":"https:\/\/ghost.mydns.jp\/wp\/?p=11701"},"modified":"2026-08-25T03:22:00","modified_gmt":"2026-08-24T18:22:00","slug":"c-builder-ce-tbitmap-grayscale-png-file","status":"publish","type":"post","link":"https:\/\/ghost.mydns.jp\/wp\/c-builder-ce- 以下カット

取れそうですね。届くのはJSONなので、jq君に見てもらいましょうかね。

curl https://ghost.mydns.jp/wp/wp-json/wp/v2/posts | jq .

で、

[
  {
    "id": 11701,
    "date": "2026-08-25T03:22:00",
    "date_gmt": "2026-08-24T18:22:00",
    "guid": {
      "rendered": "https://ghost.mydns.jp/wp/?p=11701"
    },
    "modified": "2026-08-25T03:22:00",
    "modified_gmt": "2026-08-24T18:22:00",
    "slug": "c-builder-ce-tbitmap-grayscale-png-file",
    "status": "publish",
    "type": "post",
    "link": "https://ghost.mydns.jp/wp/c-builder-ce-tbitmap-grayscale-png-file/",
    "title": {
      "rendered": "C++ Builder CE TBitmap->Grayscale png file"
    },
    "content": {
      "rendered": "\n<p class=\"wp-block-paragraph\">TBitmapについておさらいしておきましょう</p>\n\n\n\n<p class=\"wp-block-paragraph\">前の記事でTBitmapという のが出てきましたが、これを介してGrayscale(白黒ともいふ)イメージを作ってみまし ょう。いつものようにVCLでTImageを使います。実行例は、</p>\n\n\n\n<figure class=\ 以下カット

見やすくなりましたね。記事一覧を得たいのですが、まずはタイトルリストを得るには、まずjson全体をghost-article.jsonとかにセーブしておいて、

$ cat ghost-article.json | jq .[].title
{
  "rendered": "C++ Builder CE TBitmap->Grayscale png file"
}
{
  "rendered": "7 segment digits検出のためのテンプレート準備"
}
{
  "rendered": "C++ Builder CE 13が出たのでインストール"
}
{
  "rendered": "数学 基礎の基礎"
}
以下略

ただし、デフォルトでは記事10個分しかえられないので、

/wp-json/wp/v2/posts?per_page=100&page=1

とかのパラメーター付けると、最大100件得られます。ようするに全記事を得るには、工夫が必要なんですが、Giteaでのレポジトリ一覧と同様なロジックを使います。そろそろコーディングしましょうかね?baseUrlを与えると、全ての記事のタイトルを得る関数は、

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <System.Json.hpp> // JSONパース用
#include <memory>

// ---------------------------------------------------------------------------
void __fastcall TForm1::GetAllPostsIndy(String baseUrl)
{
    // IdHTTPのプロパティ設定(リクエストごとにクリアされないように設定)
    IdHTTP1->Request->UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";
    
    int page = 1;
    int totalPages = 1;

    do {
        // 1回あたり最大100件ずつ取得するURLを生成
        String url = baseUrl + "/wp-json/wp/v2/posts?per_page=100&page=" + IntToStr(page);
        
        try {
            // APIからJSON文字列を取得
            String jsonResponse = IdHTTP1->Get(url);

            // 初回ループ時にレスポンスヘッダーから全ページ数を取得
            if (page == 1) {
                // IndyのRawHeadersからWordPress固有のカスタムヘッダーを検索
                String totalPagesStr = IdHTTP1->Response->RawHeaders->Values["X-WP-TotalPages"];
                if (!totalPagesStr.IsEmpty()) {
                    totalPages = StrToInt(totalPagesStr);
                }
            }

            // JSONのパース処理
            std::unique_ptr<TJSONArray> jsonArray(dynamic_cast<TJSONArray*>(TJSONObject::ParseJSONValue(jsonResponse)));
            if (jsonArray != nullptr) {
                for (int i = 0; i < jsonArray->Count; i++) {
                    TJSONObject* post = dynamic_cast<TJSONObject*>(jsonArray->Items[i]);
                    if (post != nullptr) {
                        // 例: 記事IDの取得
                        int id = post->GetValue("id")->Value().ToInt();
                        
                        // 例: タイトルの取得 (title.rendered)
                        TJSONObject* titleObj = dynamic_cast<TJSONObject*>(post->GetValue("title"));
                        String title = titleObj ? titleObj->GetValue("rendered")->Value() : "";

                        // TODO: ここで取得したデータをリストやデータベースに格納
                    }
                }
            }

            page++; // 次のページへ

        } catch (const Exception &e) {
            // エラーハンドリング(400系、500系エラー、通信タイムアウトなど)
            ShowMessage("エラーが発生しました: " + e.Message);
            break;
        }

    } while (page <= totalPages);
}

実行すると、

このように記事一覧のメインタイトルがTMemoに表示されます。上部の209というのは記事総数です。さて、フォームは、

Unit1.hは、

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdHTTP.hpp>
#include <IdTCPClient.hpp>
#include <IdTCPConnection.hpp>
#include <IdIOHandler.hpp>
#include <IdIOHandlerSocket.hpp>
#include <IdIOHandlerStack.hpp>
#include <IdSSL.hpp>
#include <IdSSLOpenSSL.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TIdHTTP *IdHTTP1;
	TIdSSLIOHandlerSocketOpenSSL *IdSSLIOHandlerSocketOpenSSL1;
	TMemo *Memo1;
	TLabel *Label1;
private:	// ユーザー宣言
public:		// ユーザー宣言
	void __fastcall GetAllPostsIndy(String baseUrl);
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cppは、

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
#include <System.Json.hpp> // JSONパース用
#include <memory>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

void __fastcall TForm1::GetAllPostsIndy(String baseUrl)
{
	// IdHTTPのプロパティ設定(リクエストごとにクリアされないように設定)
	IdHTTP1->Request->UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";

	int page = 1;
	int totalPages = 1;
	int acount = 1;

	do {
		// 1回あたり最大100件ずつ取得するURLを生成
		String url = baseUrl + "/wp-json/wp/v2/posts?per_page=100&page=" + IntToStr(page);

		try {
			// APIからJSON文字列を取得
			String jsonResponse = IdHTTP1->Get(url);

			// 初回ループ時にレスポンスヘッダーから全ページ数を取得
			if (page == 1) {
				// IndyのRawHeadersからWordPress固有のカスタムヘッダーを検索
				String totalPagesStr = IdHTTP1->Response->RawHeaders->Values["X-WP-TotalPages"];
				if (!totalPagesStr.IsEmpty()) {
					totalPages = StrToInt(totalPagesStr);
					//Memo1->Lines->Add("totalPages: " + IntToStr(totalPages) );
				}
			}

			// JSONのパース処理
            std::unique_ptr<TJSONArray> jsonArray(dynamic_cast<TJSONArray*>(TJSONObject::ParseJSONValue(jsonResponse)));
            if (jsonArray != nullptr) {
				for (int i = 0; i < jsonArray->Count; i++) {
					TJSONObject* post = dynamic_cast<TJSONObject*>(jsonArray->Items[i]);
                    if (post != nullptr) {
                        // 例: 記事IDの取得
                        int id = post->GetValue("id")->Value().ToInt();

                        // 例: タイトルの取得 (title.rendered)
                        TJSONObject* titleObj = dynamic_cast<TJSONObject*>(post->GetValue("title"));
                        String title = titleObj ? titleObj->GetValue("rendered")->Value() : "";

						// TODO: ここで取得したデータをリストやデータベースに格納

						Memo1->Lines->Add(title);
						acount++;
                    }
                }
            }

			page++; // 次のページへ

        } catch (const Exception &e) {
			// エラーハンドリング(400系、500系エラー、通信タイムアウトなど)
            ShowMessage("エラーが発生しました: " + e.Message);
			break;
        }

	} while (page <= totalPages);
	Label1->Caption = IntToStr(acount);
}


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	String baseUrl = "https://ghost.mydns.jp/wp";

	Memo1->Lines->Clear();
	GetAllPostsIndy(baseUrl);

}

// ---------------------------------------------------------------------------

肝心のJSONをパースする部分は、

			// JSONのパース処理
			std::unique_ptr<TJSONArray> jsonArray(dynamic_cast<TJSONArray*>(TJSONObject::ParseJSONValue(jsonResponse)));
			if (jsonArray != nullptr) {
				for (int i = 0; i < jsonArray->Count; i++) {
					TJSONObject* post = dynamic_cast<TJSONObject*>(jsonArray->Items[i]);
					if (post != nullptr) {
						// 例: 記事IDの取得
						int id = post->GetValue("id")->Value().ToInt();

						// 例: タイトルの取得 (title.rendered)
						TJSONObject* titleObj = dynamic_cast<TJSONObject*>(post->GetValue("title"));
						String title = titleObj ? titleObj->GetValue("rendered")->Value() : "";

						// TODO: ここで取得したデータをリストやデータベースに格納

						Memo1->Lines->Add(title);
						acount++;
					}
				}
			}

緑部分で最小限の処理しています。日付とかを並記してもいいですね。ちなみにさすがに誰でもGetできる記事一覧ですから、投稿済みの記事だけですね。

公開済み記事の数もあっています。

コメント