1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

セキュリティニュース記事のRSSを取得してみた

1
Posted at

セキュリティニュース記事のRSSを取得してみた

はじめに

RSSでニュース記事をとってこれるのか気になったのでやってみました.

本記事では,ローカル環境で動作するシンプルな構成で,

  • IPA
  • JPCERT/CC
  • JVN

の RSS を取得し,画面を横 3 分割して一覧表示するところを紹介する.


取得対象の RSS

今回利用する RSS は以下の 3 つ.


システム構成

全体構成

  • フロントエンド

    • HTML
    • CSS
    • JavaScript
  • バックエンド(ローカル用)

    • Python(簡易 HTTP サーバ)

ブラウザの CORS 制限を回避するため,Python で RSS を中継するミニサーバを立てる構成にしている.


ディレクトリ構成

security-curation/
├─ index.html
├─ style.css
├─ app.js
└─ server.py

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Security News Curation</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header>
    <h1>RSSで情報取得してみた</h1>
  </header>

  <main class="columns">
    <section id="ipa" class="column">
      <h2>IPA</h2>
      <ul class="rss-list" id="ipa-list"></ul>
    </section>

    <section id="jpcert" class="column">
      <h2>JPCERT/CC</h2>
      <ul class="rss-list" id="jpcert-list"></ul>
    </section>

    <section id="jvn" class="column">
      <h2>JVN</h2>
      <ul class="rss-list" id="jvn-list"></ul>
    </section>
  </main>

  <script src="app.js"></script>
</body>
</html>

style.css

body {
  margin: 0;
  font-family: "Segoe UI", sans-serif;
  background: #f4f6f8;
}

header {
  background: #111;
  color: #fff;
  padding: 12px 20px;
}

.columns {
  display: flex;
  height: calc(100vh - 60px);
}

.column {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  box-sizing: border-box;
}

.column h2 {
  margin-top: 0;
  padding-bottom: 6px;
  border-bottom: 2px solid;
}

/* IPA */
#ipa {
  background: #ffffff;
}
#ipa h2 {
  color: #005bac;
  border-color: #005bac;
}

/* JPCERT */
#jpcert {
  background: #0f172a;
  color: #e5e7eb;
}
#jpcert a {
  color: #93c5fd;
}
#jpcert h2 {
  border-color: #38bdf8;
}

/* JVN */
#jvn {
  background: #fff7ed;
}
#jvn h2 {
  color: #c2410c;
  border-color: #fb923c;
}

.rss-list {
  list-style: none;
  padding: 0;
}

.rss-list li {
  margin: 10px 0;
  padding: 8px;
  border-radius: 6px;
  background: rgba(0,0,0,0.03);
}

.rss-list a {
  text-decoration: none;
  font-weight: bold;
  display: block;
}

.rss-list span {
  font-size: 0.85em;
  opacity: 0.7;
}

app.js

async function loadRSS(url, listId) {
  try {
    const res = await fetch(url);
    const text = await res.text();

    const parser = new DOMParser();
    const xml = parser.parseFromString(text, "text/xml");

    const items = xml.querySelectorAll("item");
    const list = document.getElementById(listId);

    items.forEach(item => {
      const title = item.querySelector("title")?.textContent ?? "";
      const link = item.querySelector("link")?.textContent ?? "";
      const date = item.querySelector("pubDate")?.textContent ?? "";

      const li = document.createElement("li");
      li.innerHTML = `
        <a href="${link}" target="_blank">${title}</a>
        <span>${date}</span>
      `;
      list.appendChild(li);
    });

  } catch (e) {
    console.error("RSS load failed:", url, e);
  }
}

loadRSS("/rss/ipa.rdf", "ipa-list");
loadRSS("/rss/jpcert.rdf", "jpcert-list");
loadRSS("/rss/jvn.rdf", "jvn-list");

server.py

import http.server
import socketserver
import requests

PORT = 8000

class Handler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith("/rss/"):
            rss_map = {
                "/rss/ipa.rdf": "https://www.ipa.go.jp/security/alert-rss.rdf",
                "/rss/jpcert.rdf": "https://www.jpcert.or.jp/rss/jpcert.rdf",
                "/rss/jvn.rdf": "https://jvn.jp/rss/jvn.rdf",
            }

            url = rss_map.get(self.path)
            if not url:
                self.send_error(404)
                return

            r = requests.get(url)
            self.send_response(200)
            self.send_header("Content-Type", "application/xml")
            self.end_headers()
            self.wfile.write(r.content)
        else:
            super().do_GET()

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print(f"Serving at http://localhost:{PORT}")
    httpd.serve_forever()

起動方法

$ pip install requests
$ python3 server.py
http://localhost:8000

ブラウザで確認

ブラウザで以下にアクセスする.

http://localhost:8000

IPA,JPCERT/CC,JVN のセキュリティニュースが
画面を横に 3 分割したレイアウトで表示されれば成功である.

image.png

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?