サイトの中の言い方をそろえたくて、ある言葉が使われている記事を全部探したことがありました。管理画面の投稿の検索や、REST API の search で探せばすぐ終わると思っていたのですが、一部の記事がどうしても見つかりません。
原因は、テーマのページビルダーで作った記事でした。ページビルダーの記事は、本文が WordPress のふつうの本文の欄(post_content)ではなく、テーマ専用の場所に入っています。WordPress の検索は本文の欄しか見ないので、そこに入っていない言葉は見つからないわけです。
表のページを読んで探す
どの作りの記事でも、表に出ているページには必ず本文が出ています。そこで、サイトマップからページの URL を集め、1 ページずつ読んで言葉を探すことにしました。読むだけなので、記事には何も手を入れません。
// find-word.mjs(Node.js 18 以上)
// 使い方: node find-word.mjs https://example.com 探す言葉
const [base, word] = process.argv.slice(2);
const locs = xml => [...xml.matchAll(/<loc>(?:<!\[CDATA\[)?([^<\]]+)/g)].map(m => m[1]);
// 1. サイトマップから、ページの URL を全部集める
const index = await (await fetch(base + '/sitemap.xml')).text();
let urls = [];
for (const sm of locs(index)) {
if (/(category|date|author|tag)-sitemap/.test(sm)) continue; // 一覧のページは外す
urls.push(...locs(await (await fetch(sm)).text()));
}
urls = [...new Set(urls)];
// 2. 1 ページずつ読んで、言葉の前後を出す
for (const url of urls) {
const html = await (await fetch(url)).text();
const text = html
.replace(/<script[\s\S]*?<\/script>/g, ' ')
.replace(/<style[\s\S]*?<\/style>/g, ' ')
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ');
let i = -1;
while ((i = text.indexOf(word, i + 1)) >= 0) {
console.log(url + '\t' + text.slice(Math.max(0, i - 15), i + word.length + 10));
}
await new Promise(r => setTimeout(r, 1000)); // サーバーに負担をかけないよう 1 秒あける
}
動かすと、見つかったページの URL と、言葉の前後の文章が 1 行ずつ出ます。サイトマップは All in One SEO などの SEO のプラグインが作っているものを使っています。WordPress だけのときは /wp-sitemap.xml です。
気をつけること
- 自分が管理しているサイトだけで使います。1 ページごとに 1 秒あけているので、200 ページなら 4〜5 分かかります。
- ヘッダー・サイドバー・関連記事の一覧に出る言葉も見つかります。たくさんのページに同じ文章が出ていたら、本文ではなく共通の部分です。
- 言葉を直したあとに探し直すときは、先にページのキャッシュを消します。関連記事の一覧に、直す前のタイトルが残っていることがあります。
- 「問題」「話題」のように、ほかの言葉の一部になっているものも見つかります。前後の文章を見て分けるか、前の字を除く書き方(
/(?<![問話])題/のような正規表現)にします。
見つかったあとに直すとき
ふつうの本文の記事は、編集画面や REST API で直せます。ページビルダーの記事は、プログラムから保存すると部品の内容が消えることがあるので、編集画面で直して「更新」を押します。直す前に、編集画面の入力欄の内容をメモしておくと安心です。
検索結果に出るディスクリプション(SEO のプラグインの欄)やアイキャッチの画像の中の文字も、表のページの読み取りで一緒に見つかります。画像の中の文字だけは読み取れないので、図を使っている記事は目で見て確認します。
