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

続きの続き csvで出力してexcelと連携しやすくする

もともとTMemoに書き出したテキストは、一括して下記のコードでファイルに出力することができます。

        Memo1->Lines->SaveToFile(fileName);

サブタイトルのように、excelと連携するためには、しかるべき拡張子を持ったファイル名で、中身もcsvつまりComma Separeted Valueで用意すればいいわけです。よって、前記事

のコード他を変更します。フォームは、

のように”Save”ボタンと、出力ファイル名のTEdit(デフォルト名は”output.csv”)というそれなりのコンポーネントを追加。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 <IdIOHandler.hpp>
#include <IdIOHandlerSocket.hpp>
#include <IdIOHandlerStack.hpp>
#include <IdSSL.hpp>
#include <IdSSLOpenSSL.hpp>
#include <IdTCPClient.hpp>
#include <IdTCPConnection.hpp>
#include <IdCTypes.hpp>
#include <IdSSLOpenSSLHeaders.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TIdHTTP *IdHTTP1;
	TIdSSLIOHandlerSocketOpenSSL *IdSSLIOHandlerSocketOpenSSL1;
	TLabel *Label1;
	TMemo *Memo1;
	TButton *Dump;
	TCheckBox *CheckBox1;
	TEdit *Edit1;
	TButton *Save;
	void __fastcall DumpClick(TObject *Sender);
	void __fastcall SaveClick(TObject *Sender);
private:	// ユーザー宣言
public:		// ユーザー宣言
void __fastcall TForm1::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 <vector>
#include <algorithm>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

/*
   "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": {
*/

struct aentry {
	int id;
	String adate;
	String atitle;
};

std::vector<aentry> blogentry;

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) );
				}
			}

			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() : "";

						String date = post->GetValue("date")->Value();
						// TODO: ここで取得したデータをリストやデータベースに格納
						struct aentry newentry;
						newentry.id = acount;
						newentry.adate = date;
						newentry.atitle = title;

						blogentry.push_back(newentry);
						//String ddate = date.SubString(1,10);
						Memo1->Lines->Add(date.SubString(1,10) + "   " + title);
						acount++;
                    }
				}
            }

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

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

	} while (page <= totalPages);
	Label1->Caption = IntToStr(acount);
	/* dumping vector contents */


}


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

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

}

// ---------------------------------------------------------------------------
void __fastcall TForm1::DumpClick(TObject *Sender)
{
	   Memo1->Lines->Clear();
	   int  count = 1;

		if( CheckBox1->Checked )
			std::sort(blogentry.begin(), blogentry.end(), [](const aentry &a, const aentry &b){
			return a.id > b.id;
			});

	   for( const auto& it : blogentry )
		//Memo1->Lines->Add(IntToStr(count++) + ": " + it.adate.SubString(1,10) + "   " + it.atitle);
			Memo1->Lines->Add(IntToStr(count++) + "," + it.adate.SubString(1,10) + "," + it.atitle);

}
//---------------------------------------------------------------------------

void __fastcall TForm1::SaveClick(TObject *Sender)
{
		Memo1->Lines->SaveToFile(Edit1->Text);
}

sslでエラーが出ることがあるので、IdSSLIOHandlerSocketOpenSSL1のプロパティーは、

のようにしておくのが吉です。筆者の環境ではデフォルトではエラーが出ました。実行すると、

で”Dump”して、

“Save”の準備。で”Save”をクリック。

のようにファイル”output.csv”ができているので、そのままダブルクリック。

Excelからはこのように見えているので、B列の幅を少し広げて、C列全体を縦に選択して”挿入”し、列を増やすと、

このようになりますので、セルC1に”=days(B2,B1)”と入力。

C1の内容が日付のシリアル値ぽいので、セルの書式設定から”数値”に修正。

days(B2,B1)はB2とB1の間の日数で、これで良いので、C1の内容をコピーして、C2以降へペースト、

記事の投稿間隔(日単位)が足されました。平均は、

のように、1.88位ですね。筆者のポリシーとしては2日で記事一つを目指しているので一応目標値達成ですかね。忘れずに本来のexcel形式のファイルとしてセーブし直しておきましょう。でないと幅の調整からになります。

コメント