【Python】Beautiful Soup を使ってブログ記事のテキストを抜き出してみる

2020.02.24

形態素解析の入門中で、ブログの内容の分析を試しています。

テキストデータ取得のためにWebスクレイピングツールとして Beautiful Soup(bs4) を触っています。 Beautiful Soup は HTMLや XMLドキュメントを解析するための Pythonパッケージです。

これ単体でボリュームがあったので内容をブログ化しようと思います。

目次

  1. 環境
  2. セットアップ
  3. やってみる#1: requestsでリクエスト取得
  4. やってみる#2: bs4で解析・テキスト取得
    1. 解析
    2. フィルター無しでテキスト取得
    3. フィルター有りでテキスト取得
  5. やってみる#3: (追加) bs4で解析・テキスト取得
    1. パラグラフのみ抽出
    2. リストの各アイテムのみ抽出
  6. おわりに
  7. 参考

環境

  • OS: macOS Catalina 10.15.3
  • Python: 3.7.3
  • beautifulsoup4: 4.8.2

セットアップ

適当なディレクトリ上で仮想環境を作成して activate します。

python -m venv venv
source ./venv/bin/activate

必要なパッケージをインストールします。 以下 requirements.txt を作成して pip install -r requirements.txt を実行。

./requirements.txt

requests
beautifulsoup4

以降、 Visual Studio Code 上の Jupyter Notebookでコードを書いていきました。

やってみる#1: requestsでリクエスト取得

以降で必要になるライブラリをインポートします。

from bs4 import BeautifulSoup
import requests

今回は以下のブログから情報を取得してみます。

url = "https://dev.classmethod.jp/cloud/aws/aws-nw-architectures-net320/"
response = requests.get(url)

ちゃんと取得できてそうですね。

やってみる#2: bs4で解析・テキスト取得

解析

response を解析します。

soup = BeautifulSoup(response.text, 'html.parser')
type(soup)
# <class 'bs4.BeautifulSoup'>

解析結果は BeautifulSoupオブジェクト として格納されます。

フィルター無しでテキスト取得

get_text() でテキスト取得ができます。

単純に soup.get_text() を行うとスクリプトやスタイルの内容も混じります。

フィルター有りでテキスト取得

HTML要素とそのクラスでフィルターを行った上で get_text() を行います。 ページのソースを見てみると、ブログ本文は div要素の single_article_contentsクラス であることが分かりました。

find() を使ってフィルターを行った上で get_text() を行います。

contents = soup.find('div', class_="single_article_contents")
print(contents.get_text())

良い感じに本文を取得できました。

やってみる#3: (追加) bs4で解析・テキスト取得

上の contents.get_text() でテキスト取得はできましたが、 後の分析に向けて少し整形したい気持ちです。

色々と考慮するとキリがありませんが、 今回は以下の対応試してみます。

パラグラフのみ抽出

ブログのテキストの 多く はパラグラフ(

...

)要素内に記述されます。 これらを抽出してみましょう。

find_all() を使用して取得します。

texts_p = [c.get_text() for c in contents.find_all('p')]

空白行、改行コードを削除する場合は、以下になります。

import re

# p 要素の抽出
texts_p = [c.get_text() for c in contents.find_all('p')]
# 空白行削除 + 改行コード削除
texts_p = [t.replace('\n','') for t in texts_p if re.match('\S', t)]

リストの各アイテムのみ抽出

ブログのテキストの ほとんど はパラグラフと リストアイテム (

  • ...
  • ) 要素内に記述されます。 次はリストアイテムを抽出してみます。

    ※処理を実装する前に TagNavigableString の2つの型について補足します。

    find()find_all() で得られた各オブジェクトは Tag と呼ばれる型です。 Tag は複数の TagもしくはNavigableString を子供に持つ 木構造 のオブジェクトです。

    以下実行します。

    from bs4.element import Tag, NavigableString
    
    def parse_li(li):
        """
        リストアイテム(li)のテキストを返す
        ※ li内の入れ子リストは除外する (重複するため)
        """
        buffer = []
        for child in li:
            if type(child) == NavigableString:
                buffer.append(child.string)
            elif type(child) == Tag:
                # リスト構造ではない child のみ返り値に含める
                if child.find_all('li') == []:
                    buffer.append(child.get_text())
        return ''.join(buffer)
    
    # li 要素の抽出
    texts_li = [parse_li(li) for li in contents.find_all('li')]
    # 空白行削除 + 改行コード削除
    texts_li = [t.replace('\n','') for t in texts_li if re.match('\S', t)]
    

    各アイテムを一覧化できました。

    おわりに

    Beautiful Soup を触ってみました。 解析結果のオブジェクトの仕様さえ分かると、とても扱いやすいパッケージだなと感じます。

    以下に今回の Notebookを上げています。

    ブログの内容をテキストで抜き出せたので、メインの形態素解析に入門していきたい。

    この記事が少しでもどなたかのお役に立てば幸いです。

    参考