0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Copilotを使った、JavaScriptによるWebスクレイピング

Posted at

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);
});
  • dateDate オブジェクトに変換し、_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);
});
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?