Pythonプログラミング：BeautifulSoup4を使ってlivedoor NEWSから今日のニュースを取得（スクレイピング）してみた

# はじめに

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

1. [ニュースアクセスランキング編](https://qiita.com/Blaster36/items/163333b81398b5bd4af0)
2. 今日のニュース編　★本稿
3. [話題のニュース編](https://qiita.com/Blaster36/items/f631727cd270abc57993)
4. [キーワード検索結果のニュース編](https://qiita.com/Blaster36/items/680000a7479e3a90857f)

今回は、[今日のニュース](https://news.livedoor.com/straight_news/)が対象です。
というわけで、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を紹介。
ポイントは3つ。

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

## 2. 名前付きグループの正規表現
後々、ニュースIDをKeyに記事全文を取得することも視野に、ニュースIDも抽出しておくのがベター。

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

## Codeを紹介
以下、全体のCodeです。

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

# 保管先フォルダの名称
OUTPUT_DIR = 'livedoor_news'
# 今日のニュース（ライブドアニュース）のURL
URL = 'https://news.livedoor.com/straight_news/'
# ニュースID抽出用の正規表現（名前付きグループ）
REG_URL = r'(?P<L1>(https?://[^/]+/))(?P<L2>([^/]+))/(?P<L3>([^/]+))/(?P<L4>([^/]+))/'
# 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():
    try:
        # HTTPリクエストヘッダーにUser Agentを設定
        req = Request(URL, data=None, headers={'User-Agent': USER_AGENT})
        with urlopen(req) as res:
            # HTMLドキュメントからBeautifulSoupを初期化
            soup = BeautifulSoup(res.read().decode('euc_jp', 'ignore'), 'html.parser')
            # 今日のニュース部分を検索し、全てを取得
            soupNewsStraight = soup.find('ul', class_='straightList').find_all('li')

            articles = []
            for idx, soupNews in enumerate(soupNewsStraight):
                # 詳細ページURLをHTMLタグの属性から抽出
                url = soupNews.a.get('href')
                # NewsBodyを検索し取得
                soupNewsBody = soupNews.find('div', class_='straightBody')
                # NewsBodyから各種データを抽出
                article = {
                    'url': url,
                    # ニュースIDを詳細ページURLから抽出
                    'id': re.search(REG_URL, url).groupdict()['L4'],
                    # タイトル／サマリ／提供元／公開日時をHTMLタグの本文から抽出
                    'title': soupNewsBody.find('h3', class_='straightTtl').text,
                    'summary': soupNewsBody.find('p', class_='straightSummary').text,
                    'vender': soupNewsBody.find('p', class_='straightVender').text,
                    'datetime': soupNews.a.time.get('datetime')
                }
                articles.append(article)
                print('%2d: %s' %(idx + 1, soupNewsBody.find('h3', class_='straightTtl').text))

            df = pd.DataFrame(articles)
            # DataFrameからCSVファイルを生成
            # encoding='sjis'だとJupyterLab（CSVTable）上で表示不可なことに注意
            df.to_csv('%s/livedoor_news_straight.csv' %OUTPUT_DIR, 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/685193/5a7b5330-0a12-cef9-6747-25328fa2d505.png)

# まとめ
BeautifulSoup4を使って[今日のニュース](https://news.livedoor.com/straight_news/)を取得（スクレイピング）する方法を紹介。