Skip to content
RSSで記事を取得して一覧表示させる記事のサムネイル

RSSで記事を取得して一覧表示させる

Webサイトにはよくニュース一覧ページなどが存在します。 Webサイトの制作に携わっていると、クライアントにニュース記事を別ページにも表示したいと依頼があることが多々あります。 今回は実際に記事の一覧を取得して表示させるところまで行なってみたいと思います。 当サイトはRSSに対応していないので今回のサンプルコードでは、私が勝手に師匠と思っているTAKさんのブログから取得してみようと思います。 また今回は環境構築などには触れませんのでご自身で用意してください。

RSSで記事の内容を取得する

URLを指定しフェッチしコンソールに出力してみます。 記事の情報がコンソールに出力されていれば取得できています。 ご自身で決めた場所から記事を取得する際はCORSエラーにご注意ください。

ts
const rssUrl = 'https://www.tak-dcxi.com/rss.xml';

document.addEventListener('DOMContentLoaded', () => {
  fetch(rssUrl)
    .then((response) => response.text())
    .then((xmlString) => console.log(xmlString))
    .catch((error) => console.error('Error:', error));
});

XMLを解析する

取得した記事の情報をDOMParserを使用して解析します。 解析後のものをforEachで繰り返してコンソールに出力してみます。 今回はitemで取得していますがサイトによって違う場合がありますのでXMLファイルを覗いてみて適宜変更してください。 これでコンソールに出力されていればOKです。

ts
const rssUrl = 'https://www.tak-dcxi.com/rss.xml';

const makeRssContents = (xmlString: string) => { 
  // DOMParserを使用してXMLを解析
  const parser = new DOMParser(); 
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml'); 

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item'); 
  items.forEach((item) => console.log(item)); 
}; 

document.addEventListener('DOMContentLoaded', () => {
  fetch(rssUrl)
    .then((response) => response.text())
    .then((xmlString) => makeRssContents(xmlString)) 
    .catch((error) => console.error('Error:', error));
});

記事を表示させてみる

まず取得したデータをdataに格納します。 また画像の取得はサイトによって違う可能性があります。 今回の場合はcontentで取得できました。

ts
interface ContentsData { 
  title: string; 
  link: string; 
  description: string; 
  thumbnail: string; 
  thumbnailWidth: string; 
  thumbnailHeight: string; 
  date: string; 
} 

const rssUrl = 'https://www.tak-dcxi.com/rss.xml';

const makeRssContents = (xmlString: string) => {
  // DOMParserを使用してXMLを解析
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item');
  items.forEach((item) => { 
    const data: ContentsData = { 
      title: item.querySelector('title')?.textContent || '', 
      link: item.querySelector('link')?.textContent || '', 
      description: item.querySelector('description')?.textContent || '', 
      thumbnail: item.querySelector('content')?.getAttribute('url') || '', 
      thumbnailWidth: item.querySelector('content')?.getAttribute('width') || '', 
      thumbnailHeight: item.querySelector('content')?.getAttribute('height') || '', 
      date: item.querySelector('pubDate')?.textContent || '', 
    }; 
  }); 
}

// ...

dataを引数に渡し、記事表示のひな型になるテンプレートを作成します。 ここは自分が良きように適宜変更してください。

ts
// ...

const rssUrl = 'https://www.tak-dcxi.com/rss.xml';

const htmlTemplate = (data: ContentsData): string => { 
  return ` // [!code ++]
    <article> // [!code ++]
      <a href="${data.link}" target="_blank" rel="noopener noreferrer"> // [!code ++]
        <img src="${data.thumbnail}" alt="" width="${data.thumbnailWidth}" height="${data.thumbnailHeight}"> // [!code ++]
        <h3>${data.title}</h3> // [!code ++]
        <time>${data.date}</time> // [!code ++]
        <p>${data.description}</p> // [!code ++]
      </a> // [!code ++]
    </article> // [!code ++]
  `; 
}; 

// ...

今回は記事をid="rss-target"に挿入しますので作成したmakeRssContents関数にidとして引数を渡してあげるように変更しましょう。

ts
// ...

const makeRssContents = (xmlString: string, id: string) => { 
  const targetElement = document.querySelector<HTMLDivElement>(`#${id}`); 

  // DOMParserを使用してXMLを解析
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item');
  items.forEach((item) => {
    const data: ContentsData = {
      title: item.querySelector('title')?.textContent || '',
      link: item.querySelector('link')?.textContent || '',
      description: item.querySelector('description')?.textContent || '',
      thumbnail: item.querySelector('content')?.getAttribute('url') || '',
      thumbnailWidth: item.querySelector('content')?.getAttribute('width') || '',
      thumbnailHeight: item.querySelector('content')?.getAttribute('height') || '',
      date: item.querySelector('pubDate')?.textContent || '',
    };
  });
};

document.addEventListener('DOMContentLoaded', () => {
  fetch(rssUrl)
    .then((response) => response.text())
    .then((xmlString) => makeRssContents(xmlString, 'rss-target')) 
    .catch((error) => console.error('Error:', error));
});

変数articleを定義してhtmlTemplateを呼び出します。 targetElementにtemplateを追加していきます。

ts
// ...

const makeRssContents = (xmlString: string, id: string) => {
  const targetElement = document.querySelector<HTMLDivElement>(`#${id}`);

  // DOMParserを使用してXMLを解析
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item');
  items.forEach((item) => {
    const data: ContentsData = {
      title: item.querySelector('title')?.textContent || '',
      link: item.querySelector('link')?.textContent || '',
      description: item.querySelector('description')?.textContent || '',
      thumbnail: item.querySelector('content')?.getAttribute('url') || '',
      thumbnailWidth: item.querySelector('content')?.getAttribute('width') || '',
      thumbnailHeight: item.querySelector('content')?.getAttribute('height') || '',
      date: item.querySelector('pubDate')?.textContent || '',
    };

    const article = htmlTemplate(data); 

    // 指定されたIDの要素にテンプレートを追加
    if (targetElement) targetElement.innerHTML += article; 
  });
};

// ...

表示するHTML側には、id="rss-target"の要素を置いておくのを忘れないようにしましょう

html
<!-- body以外は省略 -->
<body>
  <div id="rss-target"></div>
</body>

以上で記事が表示されたかと思います。

日付をフォーマットする

ここまでで記事を表示することはできたかと思います。 私の好みになるのですが日付を〇〇年〇〇月〇〇日と表示させたいので最後に日付のフォーマットをして終わりにしましょう。

ts
// ...

const makeRssContents = (xmlString: string, id: string) => {
  const targetElement = document.querySelector<HTMLDivElement>(`#${id}`);

  // DOMParserを使用してXMLを解析
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item');
  items.forEach((item) => {
    const pubDate = new Date(item.querySelector('pubDate')?.textContent || ''); 

    const formatDate = (date: Date): string => { 
      const year = date.getFullYear(); 
      const month = date.getMonth() + 1; 
      const day = date.getDate(); 
      const result = `${year}年${month}月${day}日`; 
      return result; 
    }; 

    const data: ContentsData = {
      title: item.querySelector('title')?.textContent || '',
      link: item.querySelector('link')?.textContent || '',
      description: item.querySelector('description')?.textContent || '',
      thumbnail: item.querySelector('content')?.getAttribute('url') || '',
      thumbnailWidth: item.querySelector('content')?.getAttribute('width') || '',
      thumbnailHeight: item.querySelector('content')?.getAttribute('height') || '',
      date: formatDate(pubDate), 
    };

    const article = htmlTemplate(data);

    // 指定されたIDの要素にテンプレートを追加
    if (targetElement) targetElement.innerHTML += article;
  });
};

// ...

最終的なコード

以下が最終的なコードになります。

ts
interface ContentsData {
  title: string;
  link: string;
  description: string;
  thumbnail: string;
  thumbnailWidth: string;
  thumbnailHeight: string;
  date: string;
}

const rssUrl = 'https://www.tak-dcxi.com/rss.xml';

const htmlTemplate = (data: ContentsData): string => {
  return `
    <article>
      <a href="${data.link}" target="_blank" rel="noopener noreferrer">
        <img src="${data.thumbnail}" alt="" width="${data.thumbnailWidth}" height="${data.thumbnailHeight}">
        <h3>${data.title}</h3>
        <time>${data.date}</time>
        <p>${data.description}</p>
      </a>
    </article>
  `;
};

const makeRssContents = (xmlString: string, id: string) => {
  const targetElement = document.querySelector<HTMLDivElement>(`#${id}`);

  // DOMParserを使用してXMLを解析
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(xmlString, 'application/xml');

  // 各item要素を取得
  const items = xmlDoc.querySelectorAll('item');
  items.forEach((item) => {
    const pubDate = new Date(item.querySelector('pubDate')?.textContent || '');

    const formatDate = (date: Date): string => {
      const year = date.getFullYear();
      const month = date.getMonth() + 1;
      const day = date.getDate();
      const result = `${year}年${month}月${day}日`;
      return result;
    };

    const data: ContentsData = {
      title: item.querySelector('title')?.textContent || '',
      link: item.querySelector('link')?.textContent || '',
      description: item.querySelector('description')?.textContent || '',
      thumbnail: item.querySelector('content')?.getAttribute('url') || '',
      thumbnailWidth: item.querySelector('content')?.getAttribute('width') || '',
      thumbnailHeight: item.querySelector('content')?.getAttribute('height') || '',
      date: formatDate(pubDate),
    };

    const article = htmlTemplate(data);

    // 指定されたIDの要素にテンプレートを追加
    if (targetElement) targetElement.innerHTML += article;
  });
};

document.addEventListener('DOMContentLoaded', () => {
  fetch(rssUrl)
    .then((response) => response.text())
    .then((xmlString) => makeRssContents(xmlString, 'rss-target'))
    .catch((error) => console.error('Error:', error));
});

いかがだったでしょうか。 実務において記事を別ページにも表示するとなった際は参考にしてみてください。