0
1

More than 1 year has passed since last update.

Pythonプログラミング:BeautifulSoup4を使ってlivedoor NEWSから話題のニュースを取得(スクレイピング)してみた

Last updated at Posted at 2021-10-07

はじめに

前回記事に続き、データ収集に関する投稿です。

  1. ニュースアクセスランキング編
  2. 今日のニュース編
  3. 話題のニュース編 ★本稿
  4. キーワード検索結果のニュース編

今回は、話題のニュースが対象です。
というわけで、Pythonプログラムで取得(スクレイピング)する処理を実現してみようと思います。

※本稿執筆時点(2021/10/07)の情報に基づき、Code紹介と実行例を示します。

本稿で紹介すること

  • 話題のニュース(livedoor NEWS)の取得

尚、筆者は以下のVersionで動作確認をしています。
- Python: 3.6.10
- BeautifulSoup4: 4.9.3

本稿で紹介しないこと

  • Pythonライブラリのインストール方法および使い方
    • urllib.request
    • BeautifulSoup4
    • re
    • pandas
    • csv
    • traceback
    • os

サンプルコード

Code量も多くないので、全体のCodeを紹介。
ポイントは4つ。

1. 日付の指定

話題のニュースなのですが、Webブラウザから見える範囲だと当日含め直近1週間分です。
が、URLに日付(YYYY-MM-DD)を付与することにより、過去日に遡及して取得可能です。
ただし、それでも限界はあって、ざっくり1年半前までが取得可能という仕組みです。

2. User-Agentの設定

HTTPリクエストヘッダーにUserAgentを設定するのがベター。
自宅PC(個人環境)なので不要ですが、会社PC(企業環境)であればProxyの設定も同じようにヘッダーに設定するのがベター。

3. 名前付きグループの正規表現

後々、ニュースIDをKeyに記事全文を取得することも視野に、ニュースIDも抽出しておくのがベター。

4. タグ要素の指定

各ページSourceを眺めて、タグ構成を鑑みて要素を指定し、BeautifulSoup4で情報を取得する必要あり。
多くの場合、タグに付与されたclass属性を指定し、目的のタグ(およびその内部のText)を取得する処理を実装することになります。
WebブラウザでページのSourceを眺めて、HTMLタグの構造を把握するのは簡単です。

Codeを紹介

以下、全体のCodeです。

from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
import re
import pandas as pd
import csv
import traceback
import datetime
import os

# 保管先フォルダの名称
OUTPUT_DIR = 'livedoor_news'
# 話題のニュース(ライブドアニュース)のURL
URL = 'https://news.livedoor.com/social_reaction/%s/'
# ニュースID抽出用の正規表現(名前付きグループ)
REG_URL = r'(?P<L1>(https?://[^/]+/))(?P<L2>([^/]+))/(?P<L3>([^/]+))/(?P<L4>([^/]+))/'
# ニュースアーカイブの遡及可能な日数
DAYS_AGO_MAX = 570
# UserAgent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36'

def create_folder():
    if not os.path.exists(OUTPUT_DIR):
        # フォルダが存在しなければ、フォルダを作成
        os.makedirs(OUTPUT_DIR)

def get_links(delta=None):
    # 基準日を実行時点で初期化
    target = datetime.date.today()
    #print(target.strftime('%Y-%m-%d'))
    if delta in list(range(-DAYS_AGO_MAX, 1)):
        # delta日前を求めるtimedeltaを作成
        td = datetime.timedelta(days=delta)
        # 基準日のdelta日前を計算して取得
        target = target + td
    #print(target.strftime('%Y-%m-%d'))
    try:
        print(URL %target.strftime('%Y-%m-%d'))
        # HTTPリクエストヘッダーにUser Agentを設定
        req = Request(URL %target.strftime('%Y-%m-%d'), data=None, headers={'User-Agent': USER_AGENT})
        with urlopen(req) as res:
            # HTMLドキュメントからBeautifulSoupを初期化
            soup = BeautifulSoup(res.read().decode('euc_jp', 'ignore'), 'html.parser')
            # 話題のニュース部分を検索し、全てを取得
            soupNewsTopic = soup.find('ol', class_='socialReactionList').find_all('li')

            articles = []
            for idx, soupNews in enumerate(soupNewsTopic):
                # ICON(FacebookとTwitter)を除外
                if len(soupNews.contents) == 0 or soupNews.contents[0].name == 'i':
                    continue
                # 詳細ページURLをHTMLタグの属性から抽出
                #url = soupNews.a.get('href')
                url = soupNews.find('a')['href']
                # NewsBodyを検索し取得
                soupNewsBody = soupNews.find('div', class_='socialReactionBody')
                # NewsBodyから各種データを抽出
                article = {
                    'url': url,
                    # ニュースIDを詳細ページURLから抽出
                    'id': re.search(REG_URL, url).groupdict()['L4'],
                    # タイトル/サマリをHTMLタグの本文から抽出
                    'title': soupNewsBody.find('h3', class_='socialReactionTtl').text,
                    'summary': soupNewsBody.find('p', class_='socialReactionDesc').text
                }
                articles.append(article)
                #print('%2d: %s' %(idx + 1, soupNewsBody.find('h3', class_='socialReactionTtl').text))

            df = pd.DataFrame(articles)
            # DataFrameからCSVファイルを生成
            # encoding='sjis'だとJupyterLab(CSVTable)上で表示不可なことに注意
            df.to_csv('%s/livedoor_news_topic_%s.csv' %(OUTPUT_DIR, target.strftime('%Y-%m-%d')), encoding='utf-8', index=False, quotechar='"', quoting=csv.QUOTE_ALL)
    except Exception as e:
        # エラー概要
        print('Exception: ', e)
        print('=====')
        # エラー詳細(スタックトレース)
        print(traceback.format_exc().rstrip())
        print('=====')

正常に実行できると、以下のようになります。
実行例.png

ちなみに、2020年4月の緊急事態宣言が発令された翌日だと、以下のような記事があり、社会情勢を反映していますね。
実行例2.png

まとめ

BeautifulSoup4を使って話題のニュースを取得(スクレイピング)する方法を紹介。

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