大量のHTMLにtitleやdescriptionを反映する機会があり、試しに一括で反映可能なスクリプトを書いてみました。
#実現できること
・エクセルに記載されているtitleやdescriptionを抽出し、該当するHTMLファイルに一括反映
・og関連やcanonicalタグなど、ページ固有の設定の一括反映
#サンプルのディレクトリ構成
root /
├ public /
├ config.xlsx
├ package.json
└ head.js
- public 〜 html/css/jsなどの公開用のディレクトリ
- config.xlsx 〜 title、meta情報を記載したエクセル
- head.js 〜 エクセルファイルを解析し、htmlファイルに反映するスクリプト
#実装について
node.jsを使用し実装します。
使用するモジュール
- xlsx
- cheerio
以下コマンドでインストールしてください。
npm install xlsx cheerio --save-dev
エクセルファイル
A列にファイルパス、B列にtitle、Cにdescriptionを記載しています。ファイルパスは"/"から始まるように記載します。
ソースコード
head.js
(function () {
"use strict";
/*
* 設定
*/
const userSetting = {
// 対象のエクセルファイル
xlsxFile: 'config.xlsx',
// 対象のシート名
xlsxSheet: 'Sheet1',
// 開始の行 ※エクセル画像のNo.1
startRowNum: 2,
// 終わりの行 ※エクセル画像のNo.2
endRowNum: 16,
// ルートディレクトリ
root: '/public',
// canonicalをセットするかどうか
isSetCanonical: true,
// canonicalが存在している場合、上書きするかどうか
isOverwrite: true,
// サイトURL
siteUrl: 'https://xxxxxxxxxx.com/'
}
const fs = require("fs");
const xlsx = require("xlsx");
const utils = xlsx.utils;
const cheerio = require('cheerio');
const getData = (filePath) => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
const replaceString = (str) => {
str = str.replace(/"/g, """);
str = str.replace(/'/g, "'");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
return str;
}
const file = xlsx.readFile(userSetting.xlsxFile);
const sheet = file.Sheets[userSetting.xlsxSheet];
for (let i = userSetting.startRowNum; i <= userSetting.endRowNum; i++) {
let baseFilePath = (sheet["A" + i] !== undefined) ? sheet["A" + i].v : undefined;
let title = (sheet["B" + i] !== undefined) ? sheet["B" + i].v : undefined;
let description = (sheet["C" + i] !== undefined) ? sheet["C" + i].v : undefined;
let relativePath = "." + userSetting.root + baseFilePath;
if (relativePath !== undefined) {
if (relativePath.slice(-1) === "/") {
relativePath = relativePath + "index.html";
}
getData(relativePath).then((data) => {
/*
* 対象ファイルが存在しており、
* htmlデータを取得できた時
*/
let $ = cheerio.load(data, {
decodeEntities: false
});
const setMetaContent = (elm, val) => {
if ($(elm)[0] === undefined) {
} else {
if ($(elm)[0].attribs !== undefined) {
$(elm)[0].attribs.content = val;
}
}
}
// title
if (title !== undefined) {
title = replaceString(title);
$('title').text(title);
setMetaContent('meta[property="og:title"]', title);
}
// description
if (description !== undefined) {
description = replaceString(description);
setMetaContent('meta[name="description"]', description);
setMetaContent('meta[property="og:description"]', description);
}
let ogUrl = userSetting.siteUrl + baseFilePath.substr(1);
setMetaContent('meta[property="og:url"]', ogUrl);
// canonicalの設定
if (userSetting.isSetCanonical && relativePath.slice(-10) === "index.html") {
// canonicalタグが存在しているかチェック
let n;
let isExist;
let linkLength = $('link').length;
for (let j = 0; j < linkLength; j++) {
if ($('link')[j].attribs.rel === "canonical") {
isExist = true;
n = j;
}
}
// 現状canonicalタグが存在する場合、上書き
// 存在しない場合、新たにcanonicalタグを追加
if (isExist && userSetting.isOverwrite) {
$('link')[n].attribs.href = userSetting.siteUrl + baseFilePath.substr(1);
} else {
let canonical = '<link rel="canonical" href="' + userSetting.siteUrl + baseFilePath.substr(1) + '" >';
$(canonical).appendTo("head");
}
}
let html = $.html();
fs.writeFile(relativePath, html, function (err) {
if (err !== null) {
console.log("error (" + relativePath + ")");
}
});
}, (err) => {
/*
* 対象ファイルが存在していない場合
*/
return true;
});
}
}
})();
実行
コマンドプロンプトおよび、ターミナルで以下のコマンドを実行
node head.js
#補足事項
- head.jsのある場所がrootになる場合は、userSetting.rootを空にしてください
root /
├ index.html
├ config.xlsx
├ package.json
└ head.js
.js
const userSetting = {
// ~ 中略 ~
root: '',
// ~ 中略 ~
}
#留意点
- 即席で作成したので細かいテストは行っておりません。不具合や誤作動でのクレームはご遠慮ください。
- Numbersで書き出したエクセルではうまく動作するか不明です。
#サンプル
githubにサンプルのソースコードをアップしてます。カスタマイズ含め、自由にご利用ください。
需要がありそうなら、何かしらでGUI化しようと思いますのでコメントいただけますと幸いです。