Googleの人気記事を拾ってくるサイト

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>Googleニュース 人気記事アグリゲーター</title>
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
  <style>
    body { background: #f8f9fa; padding-top: 20px; font-family: 'Arial', sans-serif; transition: background .3s, color .3s; }
    .dark-mode { background: #2c2c2c; color: #f1f1f1; }
    #controls { margin-bottom: 20px; }
    .card { border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); margin-bottom: 15px; transition: transform .2s, background .3s, color .3s; }
    .dark-mode .card { background: #3a3a3a; color: #f1f1f1; }
    .card:hover { transform: translateY(-3px); }
    .card-title { font-size: 1.25rem; margin-bottom: .5rem; }
    .meta { font-size: 0.85rem; color: #6c757d; margin-bottom: .5rem; }
    .dark-mode .meta { color: #ccc; }
    .thumbnail { width: 100%; height: auto; border-top-left-radius: 10px; border-top-right-radius: 10px; }
    #loading { display: none; font-size: 2rem; text-align: center; margin-top: 40px; }
    #error { display: none; color: red; text-align: center; margin-top: 20px; }
    @media (min-width: 768px) {
      #articles .col-md-6 { flex: 0 0 50%; max-width: 50%; }
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="d-flex justify-content-between align-items-center mb-3">
      <h1>Googleニュース 人気記事</h1>
      <button id="darkModeToggle" class="btn btn-outline-secondary">ダークモード</button>
    </div>
    <div id="controls" class="d-flex flex-wrap justify-content-between align-items-end">
      <div class="form-group mb-2 mr-2">
        <label for="categorySelect">カテゴリ:</label>
        <select id="categorySelect" class="form-control">
          <option value="WORLD">世界</option>
          <option value="BUSINESS">ビジネス</option>
          <option value="TECHNOLOGY">テクノロジー</option>
          <option value="ENTERTAINMENT">エンタメ</option>
          <option value="SPORTS">スポーツ</option>
          <option value="SCIENCE">科学</option>
          <option value="HEALTH">健康</option>
        </select>
      </div>
      <div class="form-group mb-2 mr-2 flex-grow-1">
        <label for="searchInput">キーワード:</label>
        <input id="searchInput" type="text" class="form-control" placeholder="タイトルで絞り込み">
      </div>
      <div class="form-group mb-2 mr-2">
        <label for="maxItems">表示数:</label>
        <input id="maxItems" type="number" class="form-control" value="20" min="1" max="100">
      </div>
      <button id="refreshBtn" class="btn btn-primary mb-2">更新</button>
    </div>
    <div id="loading"><i class="fas fa-spinner fa-spin"></i> 読み込み中...</div>
    <div id="error"></div>
    <div id="articles" class="row"></div>
  </div>

  <!-- FontAwesome & jQuery & Bootstrap JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
  <script>
    const controls = {
      category: document.getElementById('categorySelect'),
      search: document.getElementById('searchInput'),
      maxItems: document.getElementById('maxItems'),
      refresh: document.getElementById('refreshBtn'),
      darkToggle: document.getElementById('darkModeToggle')
    };
    const articlesContainer = document.getElementById('articles');
    const loading = document.getElementById('loading');
    const errorMsg = document.getElementById('error');
    const body = document.body;

    function getFeedUrl(topic) {
      return `https://news.google.com/rss/headlines/section/topic/${topic}?hl=ja&gl=JP&ceid=JP:ja`;
    }
    async function fetchArticles() {
      articlesContainer.innerHTML = '';
      errorMsg.style.display = 'none';
      loading.style.display = 'block';
      const topic = controls.category.value;
      const apiUrl = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(getFeedUrl(topic))}`;
      try {
        const res = await fetch(apiUrl);
        const data = await res.json();
        if (data.status !== 'ok') throw new Error('取得失敗');
        let items = data.items.map(item => ({
          title: item.title,
          link: item.link,
          date: new Date(item.pubDate),
          thumbnail: item.thumbnail || ''
        }));
        const kw = controls.search.value.trim();
        if (kw) items = items.filter(i => i.title.includes(kw));
        items = items.slice(0, parseInt(controls.maxItems.value) || items.length);
        render(items);
      } catch (e) {
        console.error(e);
        errorMsg.textContent = '記事の取得に失敗しました。';
        errorMsg.style.display = 'block';
      } finally {
        loading.style.display = 'none';
      }
    }
    function render(items) {
      if (!items.length) {
        articlesContainer.innerHTML = '<p class="text-center w-100">該当する記事がありません。</p>';
        return;
      }
      items.forEach(i => {
        const col = document.createElement('div'); col.className = 'col-12 col-md-6';
        const thumb = i.thumbnail ? `<img src="${i.thumbnail}" class="thumbnail" alt="サムネイル">` : '';
        col.innerHTML = `
          <div class="card">
            ${thumb}
            <div class="card-body">
              <h2 class="card-title"><a href="${i.link}" target="_blank">${i.title}</a></h2>
              <p class="meta">公開: ${i.date.toLocaleString()}</p>
            </div>
          </div>`;
        articlesContainer.appendChild(col);
      });
    }
    controls.refresh.addEventListener('click', fetchArticles);
    controls.darkToggle.addEventListener('click', () => {
      body.classList.toggle('dark-mode');
    });
    document.addEventListener('DOMContentLoaded', fetchArticles);
  </script>
</body>
</html>

投稿者: chosuke

趣味はゲームやアニメや漫画などです

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です