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?

Day7. Web ニュースタイトル取得(スクレイピング) - 勝手にChatGPTチャレンジ (Python)

Last updated at Posted at 2025-12-07

前提

本日のお題


7. Web ニュースタイトル取得(スクレイピング)

何を作る?
ニュースサイト(HTML)から記事タイトルを取得して一覧表示。

学べること

  • requests での HTTP GET
  • BeautifulSoup などを使った HTML パース
  • エラー処理・ユーザーエージェントなどの基礎

面白いところ

  • 「自分好みのニュースフィード」を作る第一歩
  • 正規表現や CSS セレクタを変えて「抽出チャレンジ」できる

回答

コード

スクレイピングは普段触れていなさ過ぎて浦島太郎なのでChatGPTパイセンにコードを生成してもらい、それを読んで勉強したいと思います。

97_news_scraper.py
"""
ニュースサイトから記事タイトルを取得して表示するシンプルなスクレイピングツール。

デフォルト:
  - サイト: Hacker News
  - URL: https://news.ycombinator.com/
  - タイトルの CSS セレクタ: .titleline > a

使い方例:
  python 07_news_scraper.py
  python 07_news_scraper.py --url https://news.ycombinator.com/ --selector ".titleline > a" --limit 10
"""

import argparse
from typing import List

import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException


def fetch_html(url: str, timeout: float = 10.0) -> str:
    """指定した URL から HTML を取得する。失敗した場合は例外を投げる。"""
    headers = {
        # 実運用では自分の連絡先などに変えてください
        "User-Agent": "PythonNewsScraper/0.1 (+https://example.com/)",
        "Accept-Language": "ja,en;q=0.9",
    }

    try:
        resp = requests.get(url, headers=headers, timeout=timeout)
        resp.raise_for_status()
    except RequestException as e:
        raise RuntimeError(f"HTTP リクエストに失敗しました: {e}") from e

    return resp.text


def extract_titles(html: str, css_selector: str, limit: int | None = None) -> List[str]:
    """
    HTML から CSS セレクタにマッチする要素を見つけて、そのテキストをタイトルとして取り出す。
    """
    soup = BeautifulSoup(html, "html.parser")

    elements = soup.select(css_selector)
    if limit is not None:
        elements = elements[:limit]

    titles: List[str] = []
    for el in elements:
        text = el.get_text(strip=True)
        if text:
            titles.append(text)

    return titles


def main() -> None:
    parser = argparse.ArgumentParser(
        description="ニュースサイトから記事タイトルを取得して一覧表示する簡単スクレイパー"
    )
    parser.add_argument(
        "--url",
        default="https://news.ycombinator.com/",
        help="スクレイピング対象の URL (デフォルト: Hacker News トップページ)",
    )
    parser.add_argument(
        "--selector",
        default=".titleline > a",
        help=(
            "記事タイトルを指す CSS セレクタ。"
            "デフォルトは Hacker News 用の '.titleline > a'"
        ),
    )
    parser.add_argument(
        "--limit",
        type=int,
        default=30,
        help="表示する最大件数 (0 以下なら制限なし, デフォルト: 30)",
    )

    args = parser.parse_args()

    if args.limit is not None and args.limit <= 0:
        limit: int | None = None
    else:
        limit = args.limit

    print(f"[INFO] URL: {args.url}")
    print(f"[INFO] CSS セレクタ: {args.selector}")
    print()

    try:
        html = fetch_html(args.url)
    except RuntimeError as e:
        print(e)
        return

    titles = extract_titles(html, args.selector, limit=limit)

    if not titles:
        print("タイトルが 1 件も取得できませんでした。")
        print("・CSS セレクタが正しいか?")
        print("・サイト側の HTML 構造が変わっていないか?")
        print("・JavaScript で後からタイトルが追加されるサイトではないか?")
        return

    print(f"=== 取得したタイトル (最大 {limit if limit is not None else '制限なし'}件) ===")
    for i, title in enumerate(titles, start=1):
        print(f"{i:2d}. {title}")


if __name__ == "__main__":
    main()

長いですが、BeautifulSoup部分は要するにHTMLをパースしてほしい情報をCSSセレクターで取ってくるという割と普通そうに見えるフローのようです。

このくらいであればちょっと情報が欲しいときに使ってみようかなと思える気もします。

※サイトによってはスクレイピング禁止だと思うのでそれを調べるのが面倒な気もしますが…。

実行例

$python 07_news_scraper.py
[INFO] URL: https://news.ycombinator.com/
[INFO] CSS セレクタ: .titleline > a

=== 取得したタイトル (最大 30件) ===
 1. Self-hosting my photos with Immich
 2. Have I been Flocked? – Check if your license plate is being watched
 3. Guy Built a Compact Camera Using an Optical Mouse
 4. Cloudflare outage on December 5, 2025
 5. Wolfram Compute Services
 6. Making tiny 0.1cc two stroke engine from scratch
 7. Leaving Intel
 8. Gemini 3 Pro: the frontier of vision AI
 9. PalmOS on FisherPrice Pixter Toy
10. Infracost (YC W21) is hiring Sr Node Eng to make $600B/yr cloud spend proactive
11. Netflix to Acquire Warner Bros
12. Adenosine on the common path of rapid antidepressant action: The coffee paradox
13. Frinkiac – 3M "The Simpsons" Screencaps
14. Ivan Sutherland Sketchpad Demo 1963 [video]
15. Schizophrenia sufferer mistakes smart fridge ad for psychotic episode
16. Extra Instructions Of The 65XX Series CPU (1996)
17. Albert Michelson's Harmonic Analyzer (2014) [pdf]
18. Idempotency keys for exactly-once processing
19. Netflix’s AV1 Journey: From Android to TVs and Beyond
20. Patterns for Defensive Programming in Rust
21. PC-Man (IBM PC 1983) and the spark of childhood wonder
22. I'm Peter Roberts, immigration attorney who does work for YC and startups. AMA
23. Perpetual futures, explained
24. YouTube caught making AI-edits to videos and adding misleading AI summaries
25. Roko's Dancing Basilisk
26. Show HN: HCB Mobile – financial app built by 17 y/o, processing $6M/month
27. Guide to making a CHIP-8 emulator (2020)
28. Tides are weirder than you think
29. Fizz Buzz in CSS
30. Nook Browser

感想

  • BeautifulSoupも上手に使えると便利
  • サイトのスクレイピング可/不可の確認が面倒(個人の感想)
  • CSSほとんど覚えていないので使う際はどちらにせよ調べないといけない…。

なれないことは難しい。

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?