
WordPressにInstagramの投稿一覧を「Smash Balloon Social Photo Feed」というプラグインを使って埋め込んでいましたが、Topページしか埋め込まないのにプラグインが必要か?との思いに駆られ、プラグインなしで実装してみました。
一つの特定のインスタ投稿を埋め込むのは簡単ですが、一覧となると結構めんどくさかったです。
こんな感じ(画像です)で埋め込みました↓

結論としては、プラグインを使用しなくてもInstagram Graph APIを使用して実装することができました。これで新規の投稿も自動で更新される?
それなりに面倒くさかったので、備忘録として残します。
InstagramビジネスアカウントIDとアクセストークンの取得
必要なのは「InstagramビジネスアカウントID」と「アクセストークン」です。
この2つだけなのですが、これが結構めんどくさい。
特にアクセストークンは3回も取得することになり、どんだけ複雑にしてんねん!とつぶやくほどです。
まず、取得するためには、InstagramのプロアカウントとFacebookページへの連携が必要になります。
「Facebookページを作成する」 又は「既存のFacebookページと連携させる」かです。
大まかな手順は次のとおりですが、詳しく書くのが面倒なので「Instagram Graph API アクセストークン取得」などで検索してください。
画像付きで詳しく説明されている記事がたくさんあります。
1.Instagramアカウントをプロアカウントに変更する(無料です)
2.InstagramとFacebookを連携させる
3.Facebookアプリを作成する
4.アクセストークン1個目を取得する
5.アクセストークン2個目を取得する
6.アクセストークン3個目の取得(これを使います)
7.InstagramビジネスアカウントIDを取得する
※記事内で紹介しているHTML・CSS・JavaScript・PHPなどのコードは、作成時点における作成者の最適解です。動作環境やバージョンによって結果が異なる場合があります。コードの使用により生じたいかなる損害やトラブルについても、当サイトは責任を負いかねます。
コード【PHPの場合】
JavascriptとPHPで実装できますが、JavaScriptで実装するとアクセストークンが見られてしまいますので、PHPで実装しましょう。
ビジネスアカウントIDは秘密情報ではないですが、アクセストークンは秘密情報なのでサーバー側で管理します。
WordPress環境なので、PHPの冒頭でwp-load.phpを読み込んでやり、wp-config.phpにビジネスアカウントIDとアクセストークンを追記します。そのコードは
define('INSTA_BUSINESS_ID', 'ここに実際のビジネスアカウントID');
define('INSTA_ACCESS_TOKEN', 'ここに実際のアクセストークン');
WordPress の transient でキャッシュしています。wp-load.phpの読み込みは重くなりますが、API の呼び出しが大幅に減るので、全体としては速くなります。
PHP
投稿の表示数をお好みで。
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php';
date_default_timezone_set('Asia/Tokyo');
header('Content-Type: text/html; charset=UTF-8');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
/* ===== 設定 ===== */
$insta_media_limit = 9;
$insta_api_version = 'v22.0'; // Meta側で提供期限があるので、定期的に確認する
define('INSTA_CACHE_KEY', 'insta_items');
define('INSTA_STALE_KEY', 'insta_items_stale');
define('INSTA_CACHE_TTL', HOUR_IN_SECONDS); // 通常のキャッシュ時間
define('INSTA_STALE_TTL', 7 * DAY_IN_SECONDS); // 失敗時に使う予備データの保持期間
define('INSTA_FAIL_TTL', 5 * MINUTE_IN_SECONDS); // 失敗後に再試行するまでの間隔
/* ===== 関数 ===== */
function insta_safe_url($url) {
return (is_string($url) && preg_match('~^https://~i', $url)) ? $url : '';
}
function fetch_insta_items($business_id, $token, $version, $limit) {
$fields = 'name,media.limit(' . (int)$limit . '){caption,media_url,thumbnail_url,permalink}';
$url = 'https://graph.facebook.com/' . $version . '/' . rawurlencode($business_id)
. '?' . http_build_query(['fields' => $fields, 'access_token' => $token]);
$res = wp_remote_get($url, ['timeout' => 8]);
if (is_wp_error($res)) {
error_log('[insta] request failed: ' . $res->get_error_message());
return null;
}
$code = wp_remote_retrieve_response_code($res);
$obj = json_decode(wp_remote_retrieve_body($res), true);
if ($code !== 200 || !is_array($obj)) {
$msg = (is_array($obj) && isset($obj['error']['message'])) ? $obj['error']['message'] : 'unknown';
error_log('[insta] API error. status=' . $code . ' message=' . $msg);
return null;
}
if (empty($obj['media']['data'])) {
error_log('[insta] no media data');
return null;
}
$items = [];
foreach ($obj['media']['data'] as $v) {
$img = insta_safe_url($v['thumbnail_url'] ?? $v['media_url'] ?? '');
$link = insta_safe_url($v['permalink'] ?? '');
if ($img !== '' && $link !== '') {
$items[] = [
'img' => $img,
'caption' => (string)($v['caption'] ?? ''),
'link' => $link,
];
}
}
return $items ?: null;
}
/* ===== 定数の定義漏れチェック ===== */
if (!defined('INSTA_BUSINESS_ID') || !defined('INSTA_ACCESS_TOKEN')) {
error_log('[insta] INSTA_BUSINESS_ID / INSTA_ACCESS_TOKEN is not defined');
echo '<li>Instagramのデータ取得に失敗しました。</li>';
exit;
}
/* ===== キャッシュ ===== */
$insta = get_transient(INSTA_CACHE_KEY);
if ($insta === false) {
$insta = fetch_insta_items(INSTA_BUSINESS_ID, INSTA_ACCESS_TOKEN, $insta_api_version, $insta_media_limit);
if ($insta !== null) {
set_transient(INSTA_CACHE_KEY, $insta, INSTA_CACHE_TTL);
set_transient(INSTA_STALE_KEY, $insta, INSTA_STALE_TTL);
} else {
$stale = get_transient(INSTA_STALE_KEY);
$insta = is_array($stale) ? $stale : [];
set_transient(INSTA_CACHE_KEY, $insta, INSTA_FAIL_TTL);
}
}
if (empty($insta)) {
echo '<li>Instagramのデータを取得できませんでした。</li>';
exit;
}
/* ===== HTML 出力 ===== */
foreach ($insta as $v) {
$img = htmlspecialchars($v['img'], ENT_QUOTES, 'UTF-8');
$link = htmlspecialchars($v['link'], ENT_QUOTES, 'UTF-8');
$caption = htmlspecialchars(mb_strimwidth($v['caption'], 0, 200, '…', 'UTF-8'), ENT_QUOTES, 'UTF-8');
echo '<li>';
echo '<div class="square-content">';
echo '<a href="' . $link . '" target="_blank" rel="noopener noreferrer">';
echo '<img src="' . $img . '" alt="' . $caption . '" loading="lazy">';
echo '</a>';
echo '</div>';
echo '</li>';
}
html
「view more」などのボタンを表示させたいのなら、2行目が必要です。instagramのユーザーネームを入れます。
<ul class="insta_list"></ul>
<div class="insta_btn"><a href="https://www.instagram.com/ユーザーネーム/">view more</a></div>
javascript
サーバー側のPHPのURLを入れます。
(function () {
const ENDPOINT = '/ここにPHPのファイル名.php'; // 相対パス推奨
function loadInstagram() {
const list = document.querySelector('.insta_list');
if (!list) return;
fetch(ENDPOINT)
.then(function (response) {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response.text();
})
.then(function (html) {
list.innerHTML = html;
})
.catch(function (error) {
console.error('Instagramのデータ取得に失敗しました:', error);
list.innerHTML = '<li>Instagramのデータを取得できませんでした。</li>';
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadInstagram);
} else {
loadInstagram();
}
})();
css
style設定はお好みに合わせて調整してください。下のコードは3×3で表示するようにしています。一応レスポンシブ対応です。
.insta_list {
width: 100%;
max-width: 600px;
margin: 0 auto;
padding: 0;
list-style: none;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
}
.insta_list li {
position: relative;
padding-top: 100%;
}
.insta_list .square-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.insta_list img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}
.insta_btn {
background: black;
padding: .5em 1em;
width: fit-content;
margin: 10px auto;
cursor: pointer;
transition: .3s;
border-radius: 5px;
-webkit-user-select: none;
user-select: none;
}
.insta_btn a {
color: white;
font-size: 15px;
text-decoration: none;
}
.insta_btn:hover {
background: #999;
}
