📰

Python と News API を使ってニュース記事を収集する

2022/03/04に公開

はじめに

ニュース記事を閲覧できるウェブサイトは各種ありますが、データ解析などの目的である程度まとまった量のニュース記事を機械的に収集したい場合には有料サービスを利用する必要があり、誰でも簡単にできるわけではありません。
この記事では、簡単な Python のプログラムと、開発目的ならば無償で利用できる NEWS API を使って、ニュース記事を収集する方法を紹介します。なお、読者は Python, JSON, HTTP に関する基本事項を理解していることを想定しています。

News API とは

NEWS API は、世界中のニュースソースから現在および過去のニュース記事を単純な REST API で取得できるサービスです。多少の制約はありますが、日本の大手新聞社やニュースサイトの日本語記事も取得できます。
https://newsapi.org/

NEWS API は基本的に有料サービスですが、開発目的の場合は制限付きながら無償で利用できます。主な制限は以下の通りです(2022年2月現在)。

  • 新着記事は 1 時間遅れ
  • 検索可能な記事は過去 1 か月以内
  • 1 回のリクエストで取得可能な記事数は 100 件まで
  • 1 日に実行可能なリクエスト数は 100 回まで

認証

NEWS API を使用するためには、毎回のリクエストで API key を提示する必要があります。API key を取得するためには、以下のページからユーザ登録をします(クレジットカードは不要)。
https://newsapi.org/register

入力が必要な情報は以下の通りです。

  • First name: 自分の名前
  • Email address: 有効なメールアドレス
  • Choose a password: 任意のパスワード
  • You are...: 個人かビジネスかを選択
  • 私はロボットではありません: チェック
  • I agree to the terms: 規約を確認

登録が完了するとメールアドレスに API key(32文字のハッシュ値)が送られてきます。

API 仕様

NEWS API では、トピックやキーワードなどを指定して過去のニュース記事を検索する Everything と、国やカテゴリーなどを指定して現在のトップニュースを取得する Top headlines の二つのエンドポイントが提供されています。各エンドポイントの入出力を以下の表に要約します。なお、詳細は公式ドキュメント[1] を参照してください。

Everything エンドポイント(/v2/everything)

入力(リクエストパラメータ)

パラメータ 機能 デフォルト値 備考
apiKey API key 必須 HTTP ヘッダで指定可
q 検索キーワード - 論理条件を指定可
searchIn 検索対象フィールド 全てのフィールド 複数指定可
sources ニュースソースID - 複数指定可
domains 検索対象ドメイン - 複数指定可
excludeDomains 除外するドメイン - 複数指定可
from 検索対象範囲 最も古い日時 ISO 8601 形式
to 検索対象範囲 最も新しい日時 ISO 8601 形式
language 言語 全ての言語 日本語指定不可
sortBy 出力順序 publishedAt 他に relevancy, popularity
pageSize 最大記事数/ページ 20 最大 100
page ページ番号 1

出力(レスポンスオブジェクト)

フィールド 内容 データ型 備考
status リクエスト結果 string ok または ステータスコード
totalResults ヒットした総記事数 int 返却は pageSize 単位
articles 検索した記事 list 以降のフィールドの繰り返し
source ニュースソースIDと名前 id + name
author 著作者 string
title タイトル string
descripriotn 記事要約 string
url 記事URL string
urlToImage 記事画像URL string
publishedAt 日時 string ISO 8601 形式
content 記事内容 string None が多い

Top headlines エンドポイント(/v2/top-headlines)

入力(リクエストパラメータ)

パラメータ 機能 デフォルト値 備考
apiKey API key 必須 HTTP ヘッダで指定可
country 国記号 全ての国 日本は jp
categry カテゴリ 全てのカテゴリ business, entertainment, general, health, science, sports, technology
sources ニュースソースID - 複数指定可
domains 検索対象ドメイン - 複数指定可
q 検索キーワード -
pageSize 最大記事数/ページ 20 最大 100
page ページ番号 1

出力(レスポンスオブジェクト)

Everything エンドポイントと同じ

Python での利用方法

NEWS API は Python を含む各言語用のクライアントライブラリ(SDK)を提供していますが、単純な REST API なので、Python の標準モジュール requests[2] を使った簡単なプログラムでニュース記事を取得できます。以下、各エンドポイントの利用方法を説明します。

Everything エンドポイントの利用

最初に、requests をインポートし、ユーザ登録によって取得した API Key を含むリクエストヘッダを作成します。

import requests

headers = {'X-Api-Key': 'API Key 文字列'}

次に、記事の検索条件を含むリクエストURL とパラメータを作成します。パラメータは URLエンコードする必要がありますが、requests は辞書形式から自動的にエンコードしてくれます。例えば、「コロナウイルス」と「ワクチン」の両方の語を含む記事を新しい順に最大100件検索したい場合は、以下のように指定します。

url = 'https://newsapi.org/v2/everything'
params = {
    'q': 'コロナウイルス AND ワクチン',
    'sortBy': 'publishedAt',
    'pageSize': 100
}

最後に、URL、ヘッダ、パラメータを指定してリクエストを送信し、レスポンスを受信します。

response = requests.get(url, headers=headers, params=params)
print(response)

<Response [200]> が返ってくれば成功です。
レスポンスの中身は、json 形式で見ることができます。

print(response.json())

以下のような内容になっています。

{'status': 'ok',
 'totalResults': 255,
 'articles': [{'source': {'id': None, 'name': 'Sputnik International'},
   'author': 'Sputnik 日本',
   'title': 'NZ首都でコロナ規制の抗議デモ\u3000警察が強制排除',
   'description': 'ニュージーランドの首都ウェリントンにある国会議事堂近くで2日、新型コロナウイルス対策の規制に抗議するデモが行われた。このデモは先月8日から続いており、参加者はワクチン接種の義務化の反対などを訴えている。',
   'url': 'https://jp.sputniknews.com/20220303/nz-10289597.html',
   'urlToImage': 'https://cdnn1.img.jp.sputniknews.com/images/sharing/article/jpn/10289597.jpg?102895981646299239',
   'publishedAt': '2022-03-03T09:05:00Z',
   'content': "https://jp.sputniknews.com/20220303/nz-10289597.html\r\n/html/head/meta[@name='og:title']/@content\r\n/html/head/meta[@name='og:description']/@content\r\nhttps://cdnn1.img.jp.sputniknews.com/img/07e6/03/03… [+666 chars]"},
  {'source': {'id': None, 'name': 'Nhk.or.jp'},
…

取得した記事は、pandas のデータフレームに変換するとその後の取り扱いが楽になります。以下は、レスポンス中の記事をデータフレームに変換した後、日時、タイトル、URL の三つの列を表示するコードです。なお、見やすくするために各列の表示幅を25文字に制限しています。

import pandas as pd
pd.options.display.max_colwidth = 25

if response.ok:
    data = response.json()
    df = pd.DataFrame(data['articles'])
    print('totalResults:', data['totalResults'])

print(df[[ 'publishedAt', 'title', 'url']])

実行結果は以下のようになります。

totalResults: 255
             publishedAt                     title                       url
0   2022-03-03T09:05:00Z   NZ首都でコロナ規制の抗議デモ 警察が強制排除  https://jp.sputniknew...
1   2022-03-03T02:21:50Z  米 新たなコロナ対策発表 ワクチン接種率向...  https://www3.nhk.or.j...
2   2022-03-03T02:00:00Z  【5月実施の新型コロナウイルス職域接種(大...  https://prtimes.jp/ma...
3   2022-03-02T17:05:04Z  北海道情報大学北海道情報大学が3月18日か...  https://japan.cnet.co...
4   2022-03-02T12:20:40Z  「新規感染者数 しばらくは高いレベルで推移...  https://www3.nhk.or.j...
..                   ...                       ...                       ...
95  2022-02-16T21:32:08Z  コロナ 抗体持つ人は各地で約95% 免疫が...  https://www3.nhk.or.j...
96  2022-02-16T17:00:00Z  【東京・小池知事】「コロナから高齢者と子ど...  http://fxya.blog129.f...
97  2022-02-16T15:00:10Z    コロナ後遺症、ワクチン接種で半減 英政府調査  http://fxya.blog129.f...
98  2022-02-16T14:34:17Z  新型コロナ 愛知で新規感染者6591人 1...  https://news.livedoor...
99  2022-02-16T14:13:19Z  【コロナ】さいたま市でワクチン2回接種の1...  https://www.2nn.jp/ne...

[100 rows x 3 columns]

検索条件にヒットした総記事数(totalResults)がページサイズ(pageSize)よりも大きい場合は、ページ番号(page)を指定することにより残りの記事を取得できます。

Top headlines エンドポイントの利用

Everything エンドポイントの時とは URL とパラメータの内容が異なるだけで、利用方法は同じです。
例えば、ビジネス分野の日本語記事のヘッドラインを最大20件(デフォルトの件数)検索したい場合は、以下のように指定します。

# Top headlines Endpoint
url = 'https://newsapi.org/v2/top-headlines'
params = {
    'category': 'business',
    'country': 'jp'
}

# Get response
response = requests.get(url, headers=headers, params=params)

# Make dataframe
if response.ok:
    data = response.json()
    df = pd.DataFrame(data['articles'])
    print('totalResults:', data['totalResults'])

print(df[[ 'publishedAt', 'title', 'url']])

結果は以下のようになります。

totalResults: 68
             publishedAt                     title                       url
0   2022-03-03T11:00:00Z  走行中の東北新幹線で車掌の顔を殴る…暴行事...  https://www.fnn.jp/ar...
1   2022-03-03T10:14:26Z  『うめきた』新駅の現状を公開!工事の8割が...  https://www.youtube.c...
2   2022-03-03T10:05:56Z  無人コンテナ船 東京湾へ 荒れる海の次は過...  https://www.youtube.c...
3   2022-03-03T10:02:34Z  カセットコンロ、ガス共に値上げへ 原油高騰...  https://www.youtube.c...
4   2022-03-03T09:11:54Z  アネスト岩田 ターンパイク箱根(箱根小田原...  https://www.hamakei.c...
5   2022-03-03T09:00:00Z  イオンなど50社、業種超え共同配送 人手不...  https://www.nikkei.co...
6   2022-03-03T08:41:42Z  JAL、東京/羽田〜ロンドン/ヒースロー線...  https://www.traicy.co...
…
17  2022-03-03T05:12:00Z  日本初 LNG燃料フェリー「さんふらわあ ...  https://trafficnews.j...
18  2022-03-03T05:04:03Z  ルネサス、インド・タタ系とEV技術を共同開...  https://www.nikkei.co...
19  2022-03-03T04:44:40Z  Windows 11プレビュー版、新セキュ...  https://pc.watch.impr...

おわりに

以上説明したように、News API を使用することにより、各種のニュース記事を Python のプログラムによって自動的に収集できます。なお、実際に利用する際には無償利用時の制限に加えて、以下の点を考慮しておく必要があります。

  • 短い記事を除き、本文全体を取得できない
  • 日本語のニュースソースID が使用できない
  • Everything エンドポイントでは、日本語を直接指定できない
  • Top headlines エンドポイントでは、パラメータの組み合わせに制限がある
  • 期待通りの検索結果が得られない場合がある(特に、from, to の日時指定時)

参考

[1] Documentation - News API
[2] Requests: 人間のためのHTTP

Discussion