Amazon URLから本のタイトルとISBNを取得するHTML/JavaScript


過去にjavaのjscoupライブラリを使ってamazonURLから書誌情報を抜き出すプログラムを書きました。

https://qiita.com/hextomino/items/277b080e318d966f6b27

本稿は実行しやすいHTML/JavaScript環境にしました。
外部のallorigins.winサービスを利用しています。

一桁件しか試してませんので、大量だとエラーになるかもしれません。

```html:getTitleISBNfromAmazonURL.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Amazon図書情報 タイトルISBN抽出</title>
  <style>
    body { font-family: sans-serif; padding: 1em; }
    textarea { width: 100%; height: 150px; }
    table { border-collapse: collapse; width: 100%; margin-top: 1em; }
    th, td { border: 1px solid #ccc; padding: 0.5em; }
    button.retry { font-size: 0.9em; padding: 2px 5px; }
  </style>
</head>
<body>
  <h1>Amazon図書情報 一括抽出ツール</h1>
  <p>Amazonの書籍ページURLを改行で複数入力してください：</p>
  <textarea id="urlList" placeholder="https://www.amazon.co.jp/dp/XXXXXXX\nhttps://www.amazon.co.jp/dp/YYYYYYY"></textarea>
  <button onclick="processAll()">情報を取得</button>

  <h2>結果</h2>
  <table id="resultTable">
    <thead>
      <tr>
        <th>タイトル</th>
        <th>ISBN</th>
        <th>再取得</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>

  <script>
    // 書籍情報を取得して指定行に表示
    async function fetchBookInfo(url, rowElement) {
      const proxy = 'https://api.allorigins.win/raw?url=' + encodeURIComponent(url);
      try {
        const res = await fetch(proxy);
        const html = await res.text();
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');

        const title = doc.querySelector('#productTitle')?.textContent.trim() || '取得失敗';
        let isbn = '取得失敗';

        const rows = doc.querySelectorAll('#detailBullets_feature_div li, table tr');
        rows.forEach(row => {
          const text = row.textContent;
          if (text.includes('ISBN-13')) {
            isbn = text
              .replace(/.*ISBN-13/i, '')                        // ラベル削除
              .replace(/[\u200E\u200F\u202A-\u202E\uFEFF]/g, '')// 不可視文字除去
              .replace(/[:：]/g, '')                            // 半角・全角コロン除去
              .trim();
          }
        });

        updateRow(rowElement, title, isbn, url);
      } catch (e) {
        updateRow(rowElement, '取得失敗', '取得失敗', url);
      }
    }

    // テーブルの1行を更新
    function updateRow(row, title, isbn, url) {
      row.innerHTML = `
        <td>${title}</td>
        <td>${isbn}</td>
        <td>
          ${title === '取得失敗' || isbn === '取得失敗' ? `<button class="retry" onclick="retryFetch(this, '${url}')">再取得</button>` : ''}
        </td>
      `;
    }

    // 再取得ボタン押下時の処理
    function retryFetch(button, url) {
      const row = button.closest('tr');
      row.innerHTML = '<td colspan="3">再取得中...</td>';
      fetchBookInfo(url, row);
    }

    // 全URLを処理
    async function processAll() {
      const urls = document.getElementById('urlList').value
        .split('\n')
        .map(u => u.trim())
        .filter(Boolean);
      const tbody = document.querySelector('#resultTable tbody');
      tbody.innerHTML = '';

      for (const url of urls) {
        const row = document.createElement('tr');
        row.innerHTML = '<td colspan="3">取得中...</td>';
        tbody.appendChild(row);
        fetchBookInfo(url, row);
      }
    }
  </script>
</body>
</html>
```