WordPressからJamstack構成にブログを移す際に悩むのがTwitter等の埋め込みコードをどう処理するかです。このブログではgatsby-remark-embedderを利用することにしています。採用を決めた基準は次のようなものです。
ほとんどの埋め込みはこれ一つで対応できましたが、卑しい小銭稼ぎ用のAmazonアソシエイトのリンクには標準では対応していません。GatsbyでAmazonLinkをtransformするプラグインもないようで、Webで転がってる埋め込みコード生成系サービスもPA-API5.0へのアップデートで冬の時代を迎えているようでした。以上のような経緯があり、Amazonのリンクに関してはさしあたってiframelyを利用していました。綺麗に表示してくれます。
しかし、早速iframelyから「API Limit越えたので明日止めるで」というアラートが届き、ほどなく非表示となりました。弱小ブログだし滅多なことじゃ越えないだろうと思っていたのですが、初月から頓挫です。課金するにも少し手が出づらいPricingだったので、別の手段でAmazonのリンクをいい感じに変換する方法を探すことにしました。
結論から言うと、今回はTwitterその他の埋め込みコード変換に使っているgatsby-remark-embedderのcustomTransformersを利用することにしました。1からremark pluginを書くというのもとても勉強になるとは思うのですが、今はスピード重視です。だって今この瞬間に表示されてない(なかった)んだもん。
置き換え用のコードの取得にあたって、他のサービスが使っているようなURLから埋め込み用の情報を返してくれるような便利なoEmbed APIはAmazonにはないので、直接WebページからスクレイピングするかProduct Advertising API(PA-API)を利用するという手段が考えられます。今回は後者を利用しました。
なお、gatsby-remark-componentやgatsby-plugin-mdxなどを用いてMarkdown中にコンポーネントを書いたりmdxで記事を管理するという方法も考えましたが、できるだけピュアなMarkdownにしておきたいという青臭い考えが捨てきれずに今回は見送りました。
上のような経緯のために、まずは前段階としてContentfulのすべてのiframelyコードを素のURLに変換します。
挿入箇所がそれなりの量になるので、本当はWordPressからのmigrationのときに触りたかったContent Management APIを利用します。
記事取得は以下を参考にしました。
置き換えるiframelyの埋め込みコードの例です。
<div class="iframely-embed"><div class="iframely-responsive" style="height: 140px; padding-bottom: 0;"><a href="https://www.amazon.co.jp/-/en/dp/B00X9CDPE4" data-iframely-url="//cdn.iframe.ly/KjRzTXD?iframe=card-small"></a></div></div>
全記事に対してぶん回すよりもContent Management APIでエントリーを取得する際に本文中にiframely
を含む記事だけに絞るようにクエリを設定してあげたほうが何となく上品っぽい気がします。以下のようにすれば引っかかるはずです。
client.getEntries({
content_type: "blogPost",
"fields.body.ja[match]": "iframely"
}).then(entries => {
console.log(entries.items);
});
ところがどうもうまく引っかかりません。同様の仕組みで検索をかけていると思われるContentfulの管理画面でもやはり同様です。おそらくHTMLタグ等などの記号が何らかの悪さをしてるっぽいですが、エスケープする方法もわからなかったので、結局全エントリ取得することにしました。
正規表現でASINだけ抜き取って有効なAmazonアソシエイトリンクに置き換えればOKです。
require("dotenv").config({ path: `.env.development` });
const env = process.env;
process.on("unhandledRejection", console.dir);
const contentful = require("contentful-management");
const client = contentful.createClient({
accessToken: env.CONTENTFUL_PERSONAL_ACCESS_TOKEN,
});
(async () => {
const space = await client.getSpace(env.CONTENTFUL_SPACE_ID);
const environment = await space.getEnvironment("master");
const entries = await environment.getEntries({
content_type: "blogPost",
order: "-sys.createdAt",
});
for (let item of entries.items) {
if (item.fields.body.ja.match(/iframely/g)) {
// iframelyを素のリンクに変換
const re = /<div class="iframely-embed">.*?\/dp\/(.*?)".*?<\/div><\/div>/g;
item.fields.body.ja = item.fields.body.ja.replace(
re,
"https://www.amazon.co.jp/exec/obidos/ASIN/$1/mtane0412-22/"
);
const updatedEntry = await item.update();
await updatedEntry.publish();
console.log(`DONE: ${item.fields.title.ja}`);
}
}
})();
実行して無事変換が終了しました。便利です。今後、エントリーに対して様々な一括処理しやすくなるだろうし、WordPress等からのmigrationのやり方も何となく当たりがつけられるようになって勉強になりました。