Python
BeautifulSoup
Webスクレイピング
selenium-webdriver

PythonでアクセスしたWebページのテキストのみの取り出し(Selenium, Beautiful Soup)

HTML内のテキストだけが欲しいと思っていましたが、探しても意外となくて苦労したので書いてみようと思います。

Seleniumを使ってChromeでWebページにアクセスし、Beautiful Soup4でWebページのHTMLからテキストのみを抽出します。

OS : macOS Sierra(10.12.3)
Python : 3.5.1
Selenium : 3.7.0
Beautiful Soup4 : 4.6.0
chromedriver : 2.33

インストール

selenium

pip install selenium

chromedriver

brew install chromedriver

Beautiful Soup4

pip install beautifulsoup4

コード

色々サイトを参考にしつつ以下のコードを実行してみる。

test.py
from selenium import webdriver
from bs4 import BeautifulSoup


# ブラウザを操作するオブジェクトの生成
driver = webdriver.Chrome()
# 指定したURLへの移動
driver.get("https://google.co.jp")
# ページのHTMLを取得
html = driver.page_source
# ブラウザの終了
driver.close()

# 取得したHTMLからBeautifulSoupオブフェクトを生成
# scriptやstyle及びその他タグの除去
soup = BeautifulSoup(html, "lxml")
for s in soup(['script', 'style']):
    s.decompose()

text = ' '.join(soup.stripped_strings)

print(text)

すると以下のようなエラーになってしまった。

UnicodeEncodeError: 'ascii' codec can't encode characters in position 13-14: ordinal not in range(128)

というわけで色々探していたらエンコードがどうのこうのと出てきたので、とりあえず以下のように書き足したらどうにかなった。

test02.py
from selenium import webdriver
from bs4 import BeautifulSoup
import sys, io

# UTF8にエンコード
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# ブラウザを操作するオブジェクトの生成
driver = webdriver.Chrome()
# 指定したURLへの移動
driver.get("https://google.co.jp")
# ページのHTMLを取得
html = driver.page_source
# ブラウザの終了
driver.close()

# 取得したHTMLからBeautifulSoupオブフェクトを生成
# scriptやstyle及びその他タグの除去
soup = BeautifulSoup(html, "lxml")
for s in soup(['script', 'style']):
    s.decompose()

text = ' '.join(soup.stripped_strings)

print(text)
Google Gmail 画像 アカウント 検索 マップ YouTube Play ニュース Gmail ドライブ カレンダー Google+ 翻訳 フォト もっと見る ショッピング ドキュメント ブックス Blogger 連絡先 ハングアウト Keep さらにもっと ログイン 判別できません。エラーが発生しました。 判別を再開始 ヘルプ 起動ワード検出はオフになっています。 「OK Google」の判別を開始 Google インスタント検索はご利用いただけません。検索するには Enter キーを押してください。 詳細 接続が遅いため Google インスタント検索をオフにしました。検索するには Enter キーを押してください。 検索するには Enter キーを押します。 不適切な検索候補の報告 × 日本 プライバシー 規約 設定 検索設定 検索オプション 履歴 ヘルプを検索 フィードバックを送信 広告 ビジネス Googleについて 音声検索を開始するには「OK Google」と発声してください。 指を一切動かさずに検索できます。「OK Google」と話しかけると、その後に話す内容が Chrome で検索されます。 詳細 使用しない 「OK Google」を有効にする

軽い解説

Selenium

基本コメントに書いてある通りです。使用するブラウザによって若干仕様が違うようなのでそこは注意が必要です。

Beautiful Soup4

soup = BeautifulSoup(html, "lxml")

2つ目の引数に"lxml"を渡していますが、渡さなかったら「HTMLパーサーを明示的に指定してください」という警告が出ます。

for s in soup(['script', 'style']):
    s.decompose()
return ' '.join(soup.stripped_strings)

scriptとstyleのタグをHTMLからdecompose()で削除して、stripped_stringsで不要な空白を除去して、空白を区切り文字として文書を連結されたのを最終的に出力しています。

まとめ

PythonでHTMLのテキストのみを抽出することが出来ました。ずっとやってみたかったんですけど、あるタグの内容を取り出したり、タグを除去するだけだったりする記述の方が多かったので結構大変でした。

まだまだ勉強中なので間違えていたり、もっと改善できるという意見がありましたらコメントで指摘して頂ければ幸いです。

追記

上記のGoogleにアクセスした例だと気付きにくかったのですが、出力された文字列中の日本語と日本語の間に不要なスペースが入ってしまうことがあります。
Wikipediaでの例

ウィキペディア へようこそ ウィキペディアは 誰でも編集できる フリー 百科事典 です

このような場合だけでなく、英語と日本語が混在した文だと、英語と英語の間のスペースはそのままにしつつ、日本語と日本語の間のスペースを削除する必要がありますので、下記のようなコードを考えました。

import re

str ="田中 太郎 is a Japanese boy.という 例文があります。"
result = re.sub('([あ-んア-ン一-鿐ー])\s+((?=[あ-んア-ン一-鿐ー]))',r'\1\2', str)

print(str)
print(result)
田中 太郎 is a Japanese boy.という 例文があります。
田中太郎 is a Japanese boy.という例文があります。

正規表現の肯定先読みで「(ひらがな、カタカナ、漢字、長音)空白(ひらがな、カタカナ、漢字、長音)」にマッチさせて、空白を除いたものに置換しています。

参考にしたサイト