【PHP/JavaScript】(初心者向け)一定時間ごとにAPIからデータを取ってくる仕組みをはじめから

ここでは PHP と JavaScript を用いて API からデータを取得するサンプルコードを動かしてみます。扱うデータは暗号通貨のマーケット情報です。

CoinGecko で提供されている API がAPIキーの登録無しに無料で利用でき,cryptwatchに比べて反応速度と使い勝手が良かったので,今回はこちらでチャレンジしてみます。

コードを実行するには XAMPP をインストールしてローカルサーバを立ち上げておく必要があります。

マーケット情報をリアルタイム更新する

今回作ろうとしているものは,上のようにいくつかの仮想通貨の価格を並べて表示し,リアルタイム更新しようというものです。

fetchでjsonを取得する

API から情報を取得するには,php を用いなければなりません(JavaScriptからは直接リクエストできない)。

情報は curl という仕組みを用いて取得します。curl は URL の形式でデータを要求する仕組みです。今回取得したいデータは

https://api.coingecko.com/api/v3/coins/markets?vs_currency=jpy

です。このアドレスをブラウザのアドレス欄に直接入力すると,画面上に

[{"id":"bitcoin","symbol":"btc","name":"Bitcoin",
"image":"https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579",
"current_price":4801761,"market_cap":90387906324536,
"market_cap_rank":1,
"fully_diluted_valuation":100836722781080,

....

のような文字列が並びます。

実はデータそのものはブラウザにアドレスを入力するだけで簡単に手に入ります。あとは,これをプログラムの側からどうやって手に入れるかです。

これと同じ文字列をfetch()を用いて取得してみましょう。

let res = await loadBoard();    //Market Summaryの取得

async function loadBoard() {
    return fetch("loadBoard.php")
        .then(res => res.json())
}

loadBoard()を実行すると配列を返してくるので,resに格納します。関数の前にある await の詳しい説明は省略しますが,関数がデータを返す前に処理が次に進まないように待ってもらう,という意味だと解釈しておけば良いでしょう。また,関数側には async を書いておきます。

関数の方を見てみましょう。fetch()loadBoard.php にアクセスし,結果が返ってきたら.then()に進みます。response => response.json()はアロー関数という関数の省略した書き方なのですが,結果が res に格納され,.json()をいう命令を与えることで,配列(オブジェクト)に変換されます。この結果を,return で戻します。

<?php
//curlでMarket Summaryを取得
$url = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=jpy";
$ch = curl_init(); //curlの初期化
curl_setopt($ch, CURLOPT_URL, $url); //URLを指定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //データを文字列で受け取る
$res = curl_exec($ch); //curlの実行
curl_close($ch); //curlの終了
echo $res; //結果を出力
?>

PHPのほうを見てみましょう。まず,curlのURLを指定します。coins/marketsはAPIが提供しているすべての通貨について,価格などの情報を要求します。vs_currency=jpyは結果を日本円で取得するためのパラメータです。 vs_currencyは必須パラメータなので,設定しないとエラーとなります。

curlの初期化から結果を得るまではただの手続きなのでコピペしましょう。

最後に echo で結果を出力します。通常,echo を実行すると画面上に出力が行われますが,fetch()で呼び出した場合は画面上ではなく fetch() に返されます(今回のコードでは res に格納される)。

実際にデータを取得するまでの手続きは少しややこしく感じるかもしれませんが,JavaScript → PHP → API → PHP → JavaScript という流れをイメージするとよいでしょう。JavaScript と PHP の橋渡しをしているのが fetch() で,PHP と API の橋渡しをしているのが curl です。

必要なデータを抽出する

res に格納されたデータは以下のようになります。

res = [
{

 ......

current_price: 4821945,
id: "bitcoin",
image: "https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579",
name: "Bitcoin",
price_change_percentage_24h: 3.50388,

......

},
{

......

current_price: 339880,
id: "ethereum",
image: "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
name: "Ethereum",
price_change_percentage_24h: 5.25495,

......

}

配列の中に,連想配列(オブジェクト)がいくつも入っています。例えば,ビットコインの価格を取得したいなら,res[0].current_price と書けば,4821945 が得られ,イーサリアムの24時間変動率を得たいなら,res[1].price_change_percentage_24h と書けば 5.25495 が得られます。

CoinGeckoでは取得できる暗号通貨の種類が数多くありますが,ここでは10個に絞って表示したいと思います。

const pairs = [ //取得する通貨のid
    'bitcoin', 'ethereum', 'cardano', 'tether', 'binancecoin',
    'ripple', 'solana', 'polkadot', 'usd-coin', 'dogecoin'
];

表示したい通貨の id を配列に格納します。取得できる id は res の中に格納されているので,コードに console.log(res); を書き込んで確認すると良いでしょう。

    pairs.forEach( function(pair) {
        //resの各idからpairsに一致するものを検索
        for (let i = 0; i<res.length; i++) {
            if(res[i].id == pair) {
                //24時間変化率の数値に+の符号を付ける
                if (res[i].price_change_percentage_24h > 0) {
                    change = '+' + res[i].price_change_percentage_24h.toFixed(2);
                } else {
                    change = res[i].price_change_percentage_24h.toFixed(2);
                }
                //HTMLコードを生成
                chars += '<tr class="board">'
                + `<td class="coinimage"><img src="${res[i].image}" width=20></td>`
                + `<td>${res[i].name}</td>` //通貨名
                + `<td>${formatter.format(res[i].current_price)}</td>` //現在価格
                + `<td>${change}%</td></tr>` //24時間変化率
                break;
            }
        }
    });

必要な通貨の情報を抽出します。forEachpairsに格納された文字列について一つ一つ,resの中に存在するかどうかを探していきます。

forEachを実行するとpairの中にbitcoinethereumなどの文字列が順番に放り込まれていきます。

たとえば,pair='bitcoin'であるとしましょう。そして,次のfor文でresを一つずつ検証していきます。

resにはres[0].id = 'bitcoin'res[1].id = 'ethereum',・・・のようにデータが格納されています。これらを順番にif文で判定し,id'bitcoin'と一致したと判定されると価格や変化率などのデータを出力します。

CoinGeckoのAPIは通貨の画像も提供しているので,手軽にアイコンとして表示できる点もおもしろいところです。

一致するデータが見つかれば,残りのデータを検証する必要はないので,breakでループを抜け,次の通貨について検索していきます。

    //HTMLコードを#contaierに書き込む
    let element = document.querySelector("#container");
    element.innerHTML = `<table id="boards">${chars}</table>`;

生成したHTMLコードをブラウザの画面に出力します。

一定時間ごとに更新する

setInterval(cryptBoard, 10000, pairs);

情報を一定時間ごとに更新するにはsetInterval(呼び出す関数, 間隔[ミリ秒],関数に渡す変数やオブジェクト)を使います。ここでは,10000ミリ秒(10秒)ごとに関数 cryptBoard を呼び出して,情報を更新しています。

一般的に API は一定時間ごとにリクエストできる回数が限られているので,あまり頻繁にリクエストしないほうが良いでしょう。

(CoinGeckoについてはAPIの回数制限の情報が見つからなかったのですが,制限はあると思います。)

まとめ

ここでは,fetch()curlという仕組みを利用して,外部のサーバーとの間でデータを送受信する方法を学びました。こうした外部のサーバーとの間でデータなどの受け渡しを行う仕組みを広く API と呼びます。

今回紹介した事例のように,API を利用することでさまざまな情報を手に入れることができます。プログラミングを学習する過程では,まずは何らかのデータを入手してみて,そのデータをいかにして利用するかというテーマに取り組んでみると良いでしょう。

最後にコード全体を示します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<style type="text/css">
.board td{padding:0.25em;}
.coinimage{margin:8px;}
</style>
</head>
<body>
<div id="container"></div>
<script>
//日本円の表示形式を指定
const formatter = new Intl.NumberFormat('ja-JP', {
                                currency: 'JPY',
                                minimumFractionDigits: 2 //小数点以下2桁まで表示
                            });
//Market Summaryの取得
async function loadBoard() {
    return fetch("loadBoard.php")
        .then(res => res.json())
}
//Market Summaryの出力
async function cryptBoard(pairs) {
    let chars = '';     //HTMLコードを格納する文字列
    let change;         //24時間の変化率
    let res = await loadBoard();    //Market Summaryの取得
    pairs.forEach( function(pair) {
        //resの各idからpairsに一致するものを検索
        for (let i = 0; i<res.length; i++) {
            if(res[i].id == pair) {
                //24時間変化率の数値に+の符号を付ける
                if (res[i].price_change_percentage_24h > 0) {
                    change = '+' + res[i].price_change_percentage_24h.toFixed(2);
                } else {
                    change = res[i].price_change_percentage_24h.toFixed(2);
                }
                //HTMLコードを生成
                chars += '<tr class="board">'
                + `<td class="coinimage"><img src="${res[i].image}" width=20></td>`
                + `<td>${res[i].name}</td>` //通貨名
                + `<td>${formatter.format(res[i].current_price)}</td>` //現在価格
                + `<td>${change}%</td></tr>` //24時間変化率
                break;
            }
        }
    });
    //HTMLコードを#contaierに書き込む
    let element = document.querySelector("#container");
    element.innerHTML = `<table id="boards">${chars}</table>`;
}
const pairs = [ //取得する通貨のid
    'bitcoin', 'ethereum', 'cardano', 'tether', 'binancecoin',
    'ripple', 'solana', 'polkadot', 'usd-coin', 'dogecoin'
];
setInterval(cryptBoard, 1000, pairs);
</script>
</body>
</html>
<?php
//curlでMarket Summaryを取得
$url = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=jpy";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); //URLを指定
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //データを文字列で受け取る
$res = curl_exec($ch);
curl_close($ch);
echo $res;
?>