JavaScriptによるWebスクレイピング
以前は、HTMLを見比べて、どうやって抜き出すか考えていたけど、
Copilotでページの内容を参照できるから、コード書くのがすごい楽。
作ったコードのメモを兼ねて、やり取りをまとめさせたので、備忘録として残す。
まだまた、一発で作るのは難しいけど、何度か軌道修正してできた。
生成AIがブラウザ操作できれば、コードはいらないかもしれないけど、流石にまだ怖いよね。
🎯 目的
保育園の予約管理ページ(https://www.mjbr.jp/...
)において、複数日の予約情報を JavaScript を使って自動的に取得・整形し、以下のような処理を行う:
- 各予約ページのフォームからパラメータを抽出
- 日付順に並び替え
- 曜日を追加
- 表形式で表示(
console.table
)
🦜 ステップ1:リンク抽出
以下は a.mb2
クラスを持つリンクを抽出する JavaScript のコードです:
const mb2Links = Array.from(document.querySelectorAll('a.mb2'))
.map(a => a.href)
.filter(href => href);
-
a.mb2
クラスを持つリンクを抽出 - 各リンクは予約ページへのURL
🦜 ステップ2:フォームからパラメータ抽出
以下は HTML からフォームのパラメータを抽出する JavaScript のコードです:
const extractParamsFromHTML = (html) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const form = doc.querySelector("form");
const params = {};
if (form) {
const elements = form.querySelectorAll("input, select, textarea");
elements.forEach(el => {
const name = el.name;
if (!name) return;
if (el.type === "checkbox" || el.type === "radio") {
if (el.checked) {
params[name] = params[name]
? params[name] + ", " + el.value
: el.value;
}
} else {
params[name] = el.value;
}
});
const td = form.querySelector("td");
if (td) {
params["date"] = td.textContent.trim();
}
}
return params;
};
-
name
属性をキーにして、フォームの値をparams
に格納 - チェックボックス・ラジオボタンは 文字列として連結
-
<td>
要素から登園日(date
)を取得
🦜 ステップ3:fetch → 抽出 → 整形 → 表示
以下は、リンク先のページを fetch し、パラメータを抽出・整形して表示する JavaScript のコードです:
const allParams = [];
Promise.all(
mb2Links.map(url =>
fetch(url)
.then(res => res.text())
.then(html => {
const params = extractParamsFromHTML(html);
const dateMatch = params.date?.match(/(\d{4})年(\d{1,2})月(\d{1,2})日/);
if (dateMatch) {
const [_, year, month, day] = dateMatch;
const dateObj = new Date(`${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`);
params._dateObj = dateObj;
const weekdayMap = ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'];
params.weekday = weekdayMap[dateObj.getDay()];
}
allParams.push(params);
})
.catch(err => {
console.error("Fetch error for", url, err);
})
)
).then(() => {
allParams.sort((a, b) => a._dateObj - b._dateObj);
const displayParams = allParams.map(({ _dateObj, ...rest }) => rest);
console.table(displayParams);
});
-
date
をDate
オブジェクトに変換し、_dateObj
に格納 -
weekday
を日本語で追加 -
date
順に並び替え -
console.table()
で表形式表示
✅ 出力例(テーブル)
(割愛)
全コード
const mb2Links = Array.from(document.querySelectorAll('a.mb2'))
.map(a => a.href)
.filter(href => href);
const extractParamsFromHTML = (html) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const form = doc.querySelector("form");
const params = {};
if (form) {
const elements = form.querySelectorAll("input, select, textarea");
elements.forEach(el => {
const name = el.name;
if (!name) return;
if (el.type === "checkbox" || el.type === "radio") {
if (el.checked) {
if (params[name]) {
// params[name] = [].concat(params[name], el.value);
params[name] = params[name] ? params[name] + ", " + el.value : el.value;
} else {
params[name] = el.value;
}
}
} else {
params[name] = el.value;
}
});
// 登園日(td[0])
const td = form.querySelector("td");
if (td) {
params["date"] = td.textContent.trim();
}
}
return params;
};
const allParams = [];
Promise.all(
mb2Links.map(url =>
fetch(url)
.then(res => res.text())
.then(html => {
const params = extractParamsFromHTML(html);
// 日付を Date オブジェクトに変換
const dateMatch = params.date?.match(/(\d{4})年(\d{1,2})月(\d{1,2})日/);
console.log("date", params.date, dateMatch);
if (dateMatch) {
const [_, year, month, day] = dateMatch;
const dateObj = new Date(`${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`);
params._dateObj = dateObj;
// 曜日を追加(日本語)
const weekdayMap = ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'];
params.weekday = weekdayMap[dateObj.getDay()];
}
allParams.push(params);
})
.catch(err => {
console.error("Fetch error for", url, err);
})
)
).then(() => {
// date順に並び替え
allParams.sort((a, b) => a._dateObj - b._dateObj);
console.table(allParams)
// _dateObj は表示しない
const displayParams = allParams.map(({ _dateObj, ...rest }) => rest);
// テーブル表示
console.table(displayParams);
});