PHP 8.3のcURLでISBNから書誌情報を取得する:openBD・Google Books API・NDLサーチ
作成日:2026.09.01
PHP 8.3のcURLを使い、ISBNをもとにopenBD、Google Books API、NDLサーチへアクセスする実装例を紹介します。JSON/XMLの解析、書誌情報の共通形式への変換、通信エラーや項目欠損の扱いに加え、openBD v1の終了予定やNDLサーチの利用条件も整理します。
目次
以前、ISBNから書誌情報を取得できるAPIを比較した記事を書きました。今回はその続きとして、PHP 8.3のcURLを使い、ISBNを各APIへ送信して書誌情報を取得する実装例を整理します。
サンプルには、ISBN 978-4150315207(法治の獣)を使います。APIごとにレスポンスの形式や収録項目が異なるため、取得処理を分けた上で、アプリケーション側では同じ形式で扱えるようにしてみます。
この記事は、APIの特徴を比較した前回記事の実装編です。APIの仕様や利用条件は変更される可能性があるため、実際に組み込む際は各公式資料も確認してください。
前提
今回の前提は次のとおりです。
- PHP 8.3
- PHPのcURL、JSON、SimpleXML拡張
- コマンド実行はPowerShell
- サンプルISBNは
978-4150315207 - フレームワークやデータベースは使わず、APIから取得した結果を表示する
ISBNは入力時にはハイフン付きでもよいものとして、APIへ送信する前にハイフンと空白を取り除きます。今回のサンプルでは、APIへは9784150315207を送信します。
ISBNをAPIへ送信する前に正規化する
APIによってはISBN-10とISBN-13の両方を扱えますが、入力値にハイフンや空白が含まれていると、検索条件の組み立てで扱いにくくなります。まずは入力値を正規化する関数を用意します。
function normalizeIsbn(string $isbn): string
{
$normalized = preg_replace('/[-\s]/u', '', $isbn);
if ($normalized === null || preg_match('/^(?:\d{10}|\d{13})$/', $normalized) !== 1) {
throw new InvalidArgumentException('ISBN-10またはISBN-13を指定してください。');
}
return $normalized;
}
$isbn = normalizeIsbn('978-4150315207');
echo $isbn, PHP_EOL;
// 9784150315207
この例では、ISBNの桁数だけを確認しています。チェックディジットまで検証したい場合は、ISBN-10とISBN-13それぞれの検証処理を追加してください。形式が正しいことと、APIに該当する書籍が存在することは別なので、検索結果の有無も別に扱います。
cURLでHTTPレスポンスを取得する
JSONを返すAPIとXMLを返すAPIで共通して使えるように、HTTPリクエストの部分を関数へ分けます。
function getApiResponse(string $url, string $accept): array
{
$handle = curl_init($url);
if ($handle === false) {
throw new RuntimeException('cURLの初期化に失敗しました。');
}
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 15,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER => [
'Accept: ' . $accept,
],
CURLOPT_USERAGENT => 'isbn-metadata-example/1.0',
]);
$body = curl_exec($handle);
$curlErrorNumber = curl_errno($handle);
$curlErrorMessage = curl_error($handle);
$statusCode = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
curl_close($handle);
if ($body === false) {
throw new RuntimeException(
"cURL error {$curlErrorNumber}: {$curlErrorMessage}"
);
}
if ($statusCode < 200 || $statusCode >= 300) {
throw new RuntimeException("HTTP status: {$statusCode}");
}
return [
'status' => $statusCode,
'body' => $body,
];
}
通信エラーとHTTPエラーを同じ「データなし」として扱わないことがポイントです。タイムアウトや名前解決エラーは再試行を検討できますが、HTTP 404やAPI側のレート制限は、原因を確認してから対応を変える必要があります。
openBD APIからJSONを取得する
openBDのv1 APIでは、ISBNを指定して書誌情報を取得できます。今回使うURLは次の形式です。
https://api.openbd.jp/v1/get?isbn=978-4150315207&pretty
PHPでは、先ほど正規化したISBNをURLへ埋め込みます。
function getFromOpenBd(string $isbn): ?array
{
$url = 'https://api.openbd.jp/v1/get?isbn=' . rawurlencode($isbn) . '&pretty';
$response = getApiResponse($url, 'application/json');
$data = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR);
// openBDは該当データがない場合、配列の要素がnullになることがあります。
$record = $data[0] ?? null;
if (!is_array($record)) {
return null;
}
$summary = $record['summary'] ?? [];
return [
'source' => 'openBD',
'isbn' => $isbn,
'title' => $summary['title'] ?? null,
'authors' => isset($summary['author'])
? [(string) $summary['author']]
: [],
'publisher' => $summary['publisher'] ?? null,
'publishedAt' => $summary['pubdate'] ?? null,
'coverUrl' => $summary['cover'] ?? null,
];
}
openBDのレスポンスにはsummaryのほか、詳細なonixやhanmotoも含まれます。ここではアプリケーションで使う項目を少数に絞り、著者名はひとまず1つの文字列として扱っています。複数著者を分けて表示したい場合は、実際のデータ構造を確認して専用の変換処理を追加します。
なお、openBDにはv1 APIを今後終了する予定であることが公式に告知されています。現時点で案内されているURLを使った実装例ですが、将来はエンドポイントだけでなく、収録範囲や収録内容も変更される可能性があります。openBDの公式サイトで最新の案内を確認してから利用してください。
また、openBDのデータは利用目的や利用規約も確認が必要です。本の販促・紹介を目的としたサービスとして案内されているため、読書メモアプリなど別の用途へ組み込む場合は、利用条件に適合するかを確認しておきましょう。
Google Books APIからJSONを取得する
Google Books APIでは、qパラメータにisbn:を付けてISBN検索を行います。APIキーをコードへ直接書かず、環境変数から任意で読み込むようにします。
function getFromGoogleBooks(string $isbn): ?array
{
$query = [
'q' => 'isbn:' . $isbn,
'maxResults' => 1,
];
$apiKey = getenv('GOOGLE_BOOKS_API_KEY');
if ($apiKey !== false && $apiKey !== '') {
$query['key'] = $apiKey;
}
$url = 'https://www.googleapis.com/books/v1/volumes?'
. http_build_query($query);
$response = getApiResponse($url, 'application/json');
$data = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR);
$volume = $data['items'][0] ?? null;
if (!is_array($volume)) {
return null;
}
$volumeInfo = $volume['volumeInfo'] ?? [];
return [
'source' => 'Google Books',
'isbn' => $isbn,
'title' => $volumeInfo['title'] ?? null,
'authors' => $volumeInfo['authors'] ?? [],
'publisher' => $volumeInfo['publisher'] ?? null,
'publishedAt' => $volumeInfo['publishedDate'] ?? null,
'coverUrl' => $volumeInfo['imageLinks']['thumbnail'] ?? null,
];
}
itemsが存在しない場合は、ISBNに一致する結果がなかったと判断できます。ただし、検索結果が返っても指定ISBNと同じ版とは限りません。厳密に版を識別したい場合は、industryIdentifiersのISBNを確認してから採用します。
Google Books APIの検索結果には、説明文やページ数など、openBDと異なる項目が含まれることがあります。まず共通項目だけを取り出し、アプリケーション固有の項目は必要になった段階で追加する方が、APIの違いに対応しやすくなります。
NDLサーチのOpenSearch APIからXMLを取得する
NDLサーチには複数のAPIがあります。今回は前回記事との連続性を考え、OpenSearchでISBN検索を行い、返されたRSS 2.0形式のXMLをPHPで解析します。OpenSearchでは、ISBN-10またはISBN-13を指定すると両方の形式へ変換して完全一致検索が行われます。
OpenSearchのリクエストは、次のような形になります。
https://ndlsearch.ndl.go.jp/api/opensearch?isbn=9784150315207&cnt=1
URLのパラメータはhttp_build_queryで組み立てます。cnt=1は、今回のサンプルで取得するレコード数を1件に制限する指定です。
function getFromNdl(string $isbn): ?array
{
$query = [
'isbn' => $isbn,
'cnt' => 1,
];
$url = 'https://ndlsearch.ndl.go.jp/api/opensearch?'
. http_build_query($query);
$response = getApiResponse($url, 'application/xml, text/xml');
libxml_use_internal_errors(true);
$xml = simplexml_load_string($response['body']);
if ($xml === false) {
throw new RuntimeException('NDLサーチのXMLを解析できませんでした。');
}
$xml->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
$items = $xml->xpath('//item') ?: [];
$record = $items[0] ?? null;
if ($record === null) {
return null;
}
$dc = $record->children('http://purl.org/dc/elements/1.1/');
$title = trim((string) ($record->title ?? ''));
$authors = [];
foreach ($dc->creator as $creator) {
$authors[] = trim((string) $creator);
}
return [
'source' => 'NDLサーチ',
'isbn' => $isbn,
'title' => $title !== '' ? $title : null,
'authors' => array_values(array_filter($authors)),
'publisher' => isset($dc->publisher[0])
? trim((string) $dc->publisher[0])
: null,
'publishedAt' => isset($dc->date[0])
? trim((string) $dc->date[0])
: null,
// NDLの書影APIは終了しているため、ここではURLを生成しません。
'coverUrl' => null,
];
}
OpenSearchのレスポンスはRSS 2.0を拡張したXMLです。標準のitemからタイトルを取得し、Dublin Core名前空間の要素から著者や出版社などを取り出しています。NDLサーチの書誌データには複数のレコードや複数の値が含まれることがあるので、実際のアプリケーションではcntを増やし、各レコードのISBNや版を確認して採用する処理を追加するとよいでしょう。
NDLサーチは、現在のAPI仕様でSRU、OpenSearch、OpenURLなど複数のAPIを案内しています。
また、NDLサーチのAPIは、利用目的やデータ提供機関によって申請の要否が異なります。営利目的かどうか、継続的なアクセスを行うか、どのデータ提供機関のデータを利用するかによって条件が変わるため、APIの利用案内を確認してください。
NDLサーチの書影APIは、提供条件の変更に伴い2026年3月31日で終了しています。このため、今回の共通形式ではNDLのcoverUrlをnullにしています。書影が必要な場合は、別のデータ提供元を利用できるか、画像の利用条件を確認した上で実装します。
3つの結果を共通形式へそろえる
各APIの取得関数が、同じキーを持つ配列を返すようにしました。呼び出し側では、APIごとのsummaryやvolumeInfoを意識せずに表示できます。
$isbn = normalizeIsbn('978-4150315207');
$books = [
getFromOpenBd($isbn),
getFromGoogleBooks($isbn),
getFromNdl($isbn),
];
foreach ($books as $book) {
if ($book === null) {
echo "書籍情報が見つかりませんでした。", PHP_EOL;
continue;
}
echo $book['source'], ': ', $book['title'], PHP_EOL;
echo '著者: ', implode(', ', $book['authors']), PHP_EOL;
echo '出版社: ', ($book['publisher'] ?? '不明'), PHP_EOL;
echo '出版日: ', ($book['publishedAt'] ?? '不明'), PHP_EOL;
}
この形にしておけば、将来別のAPIを追加する場合も、「APIから取得する関数」と「共通形式へ変換する関数」を追加すれば済みます。さらに規模が大きくなったら、配列の代わりにBookMetadataのようなDTOを用意すると、必須項目や型を明確にできます。
一方で、著者名や出版日の表記はAPIによって揺れます。共通形式にしたからといって、3つの結果が同じ内容になるわけではありません。表示前に文字列を統一する処理や、採用するデータソースの優先順位をアプリケーション側で決めておく必要があるでしょう。
エラーと欠損を分けて扱う
実装では、次の状態を分けて扱います。
- ISBNの形式が不正:リクエストを送らず、入力エラーとして扱う
- 通信エラーやタイムアウト:再試行や利用者への再実行案内を検討する
- HTTP 4xx/5xx:APIの仕様やクォータを確認する
- JSONやXMLの解析エラー:レスポンス形式の変更や一時障害を疑う
- 検索結果なし:正常な検索結果として、該当なしを表示する
- 個別項目の欠損:項目単位で
nullや空配列として扱う
たとえば、3つのAPIを一度に呼び出す場合、1つがタイムアウトしただけで全体を失敗にするのか、取得できた結果だけ表示するのかは、アプリケーションの目的で決まります。エラーを握りつぶして「検索結果なし」と表示すると、原因の切り分けが難しくなるので注意します。
実運用では、同じISBNを毎回外部APIへ問い合わせるのではなく、利用条件を確認した上で結果をキャッシュする方法も検討します。キャッシュの有効期限や更新タイミング、保存するデータの範囲は、各APIの規約とデータの更新頻度に合わせて決めます。
APIを選ぶときに確認すること
| API | 形式 | 特徴 | 確認すること |
|---|---|---|---|
| openBD | JSON | 日本の書籍情報や書影を扱いやすい | v1終了予定、データ変更、利用目的と規約 |
| Google Books API | JSON | 広い範囲の書籍を検索できる | 検索結果の版、APIキー、クォータ、画像の利用条件 |
| NDLサーチ OpenSearch | XML(RSS) | 国立国会図書館の書誌情報を検索できる | 利用目的、申請要否、データ提供機関、クレジット表示 |
日本の書籍を中心に扱うのか、海外の書籍も対象にするのか、書影が必須なのか、商用サービスで利用するのかによって、選ぶAPIは変わります。取得できる項目だけでなく、継続アクセスやキャッシュを含む利用条件まで確認して決めることが大切です。
動作を確認する
PHPのバージョンと必要な拡張を確認します。PowerShellでは次のように実行できます。
php -v
php -m | Select-String "curl|json|libxml|SimpleXML"
スクリプトをisbn.phpとして保存したら、次のように実行します。
php isbn.php
確認するポイントは、次のとおりです。
- ハイフン付きのISBNが正規化されている
- 各APIの結果が、タイトルや著者など共通のキーで表示される
- 検索結果がない場合に例外と混同されない
- 書誌項目が欠損していても表示処理が止まらない
- HTTPエラーやcURLエラーの内容をログで確認できる
APIのレスポンスは収録データの更新や仕様変更によって変わります。また、同じISBNでもAPIによって表記や収録項目が一致しない場合があります。実装時と公開前に、サンプルISBNで実際のレスポンスを確認しておくと安心です。
注意点
- openBD v1は終了予定が告知されているため、長期運用する場合は後継仕様の確認が必要です。
- NDLサーチは、利用目的によって申請の要否が変わります。継続的なアクセスや商用利用では、事前に利用案内を確認します。
- NDLサーチの書影APIは終了しているため、NDLのURLを推測して書影を取得しないようにします。
- Google Books APIの検索結果は、指定ISBNの版と一致するかを必要に応じて確認します。
- APIキーや認証情報はソースコードへ直接書かず、環境変数などで管理します。
- タイムアウト、レート制限、API停止を想定し、キャッシュや再試行の方針を決めます。
- 書誌情報や書影画像を保存・表示する場合は、各APIやデータ提供元の利用条件を確認します。
まとめ
PHPのcURLを使うと、ISBNを使った書誌情報APIへのアクセスを共通の通信処理でまとめられます。JSONを返すopenBDとGoogle Books API、XMLを返すNDLサーチでは解析方法が異なりますが、アプリケーションへ渡す形式をそろえておけば、呼び出し側の実装はシンプルにできます。
一方で、APIごとに収録範囲、版の扱い、欠損、書影の提供状況、利用条件が異なります。openBD v1の終了予定や、NDLサーチの利用目的による申請要否も含め、実装前に公式情報を確認した上で採用するAPIを決めるようにします。
参考資料
奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。
これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。
【制作会社・企業様向けサポート】
Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。
ぜひ、プロフィールやWeb制作会社様向け業務案内、一般企業様向け業務案内もご覧くださいね。
書誌データAPI比較(ISBN対応):openBD / Google Books / 国立国会図書館の特徴と選び方
2025.11.05
ISBNをキーに書誌情報を取得できる主要API(openBD、Google Books、国立国会図書館NDLサーチ)を比較し、対応範囲やレスポンス形式、利用制限、実運用での使い分けポイントを整理しました。
PHPでメールOTPによる追加認証を実装する:発行・保存・送信・検証の設計
2026.09.04
PHP 8.3以上を対象に、パスワードログイン後にメールOTPで追加認証を行う処理を、フレームワークに依存しない形で整理します。コードの生成・保存・送信・検証、期限切れや再送、試行回数制限を扱い、メールOTPをTOTPやパスキーと同等に扱わないための注意点も説明します。
Laravel 13で既存のテーブルを使ってログイン機能を実装する
2026.09.02
Laravel 13の既存連載で作成したmembersテーブルを認証用ユーザーとして流用し、Memberモデルと認証プロバイダーを設定します。SQLiteに保存したユーザー情報を使ったメールアドレス・パスワード認証、ログイン済み画面の表示、データベースセッション、ログアウト時のセッション破棄までを実装します。
Laravel 13+Vue.jsで画像・CSVを非同期アップロードする:FormData・ファイル検証・非公開保存
2026.08.31
Laravel 13のBladeに組み込んだVue.jsから、FormDataを使って画像またはCSVを非同期で送信する方法を解説します。JPEG・PNGの画像検証、CSVの文字コード変換と簡易的な内容確認、20MBのサイズ制限、非公開ディスクへの保存、CSRF・バリデーションエラー処理を実装し、fetchを基本にaxiosとの違いも整理します。