BeautifulSoupを使ってWikipediaのテキストを抽出する

 

BeautifulSoupを使ってWikipedia上の記事からテキストを抽出してみました。

本記事の前半ではurllib、後半ではRequestsを使ってWikipediaの記事を取ってきています。

 

 

環境

Wikipediaの記事から<p>で囲まれたテキストを抽出する

ライブラリ

必要なライブラリのインポート

import urllib.parse as parser
import urllib.request as request
from bs4 import BeautifulSoup

Wikipediaの記事をダウンロード

urllibを使ってWikipediaの記事をダウンロードします。

詳細はこちら

Wikipedia織田信長の記事をダウンロードしています。

link = "https://ja.wikipedia.org/wiki/"
with request.urlopen(link + parser.quote_plus("織田信長")) as response:
    html = response.read().decode("utf-8")

htmlには織田信長の記事のHTML形式で書かれた文字列が入っています。

BeautifulSoup

BeautifulSoup関数を使ってHTMLで書かれた文字列を解析します。

BeautifulSoupには2つの引数を渡します。

  • 解析したいHTMLの文字列
  • "lxml"
soup = BeautifulSoup(html, "lxml")

soupはBeautifulSoupのオブジェクトとなっていて、いくつかの変数やメソッドが用意されています。

textを使うと、HTMLの形式で書かれていたものから全てのタグを取り除いて読みやすいテキストの形にしてくれます。

#タグを取り除く
print(soup.text)

<p>で囲まれたテキストを取り出す

find_allメソッドを使えば指定したタグで囲まれた部分をリストの形で取り出すことができます。

p_tags = soup.find_all("p")

p_tagsには<p>と</p>で囲まれている部分が入っています。

例えばこんな感じです。

sample = p_tags[0]
print(sample)
"""
<p><b>織田 信長</b>(おだ のぶなが)は、<a href="/wiki/%E6%88%A6%E5%9B%BD%E6%99%82%E4%BB%A3_(%E6%97%A5%E6%9C%AC)" title="戦国時代 (日本)">戦国時代</a>から<a href="/wiki/%E5%AE%89%E5%9C%9F%E6%A1%83%E5%B1%B1%E6%99%82%E4%BB%A3" title="安土桃山時代">安土桃山時代</a>にかけての<a href="/wiki/%E6%AD%A6%E5%B0%86" title="武将">武将</a>・<a href="/wiki/%E6%88%A6%E5%9B%BD%E5%A4%A7%E5%90%8D" title="戦国大名">戦国大名</a>。<b><a href="/wiki/%E4%B8%89%E8%8B%B1%E5%82%91" title="三英傑">三英傑</a></b>の一人。
</p>
"""

textを使って本文だけを取り出します。

これでWikipediaからテキストを抽出することができました。

print(sample.text)
"""
織田 信長(おだ のぶなが)は、戦国時代から安土桃山時代にかけての武将・戦国大名。三英傑の一人。
"""

ちなみに、find_allメソッドを使うと、型は以下のようになっています。

p_tags = soup.find_all("p")
print(type(p_tags))
#<class 'bs4.element.ResultSet'>

sample = p_tags[0]
print(type(sample))
#<class 'bs4.element.Tag'>

print(type(sample.text))
#<class 'str'>

Requestsを使ってみる

ここではrequestsを使ってWikipediaの記事をダウンロードし、Beautifulsoupを使って必要な情報を取り出してみます。

流れはこんな感じです。全体のソースコードは下に置いてあります。

  1. URLの用意。
  2. requests.get(URL)を使って指定したURLのページを取ってくる。
  3. htmlに変換。
  4. Beautifulsoupを使って必要な情報を取り出す。

以下のURLにダウンロードしたいWikipediaのタイトルを足します。

url = "https://ja.wikipedia.org/wiki/"

 

例えば、織田信長の記事を取ってきたい場合は以下のようにします。

url = "https://ja.wikipedia.org/wiki/織田信長"

 

requests.get(URL)で指定したURLのページを取ってきます。

import requests
url = "https://ja.wikipedia.org/wiki/織田信長"
response = requests.get(url)

 

htmlに変換します。

# htmlに変換
html = response.text

 

最後に前半部分でやったようにBeautifulsoupを使ってpタグで囲まれた部分を抽出します。

for p_tag in soup.find_all('p'):
    text = "".join(p_tag.text.strip().split(" "))

 

ソースコード

import codecs
import re
import requests
from bs4 import BeautifulSoup

url = "https://ja.wikipedia.org/wiki/"
keyword_list = ["織田信長", "徳川家康", "豊臣秀吉",
           "伊達政宗", "武田信玄", "上杉謙信",
           "明智光秀", "島津義弘", "北条氏康",
           "長宗我部元親", "毛利元就", "真田幸村",
           "立花宗茂", "石田三成", "浅井長政"]

corpus = []
for keyword in keyword_list:
    # 戦国大名の記事をダウンロード
    response = requests.get(url + keyword)
    # htmlに変換
    html = response.text
    
    soup = BeautifulSoup(html, 'lxml')
    for p_tag in soup.find_all('p'):
        text = "".join(p_tag.text.strip().split(" "))
        if len(text) == 0:
            continue
        # e.g. [注釈9] -> ''
        text = re.sub(r"\[注釈[0-9]+\]", "", text)
        # e.g. [20] -> ''
        text = re.sub(r"\[[0-9]+\]", "", text)
        corpus.append(text)
        
print(*corpus, sep="\n", file=codecs.open("wiki.txt", "w", "utf-8"))

このコードでは複数の戦国大名の記事をダウンロードし、pタグで囲まれた部分を抽出しています。抽出したものを正規表現を使って前処理をしています。

最後に前処理したデータをprint()を使ってファイルに書き込んでいます。

Q&A

BeautifulSoupにlxmlを渡すとエラーが出る場合

下のコードのところで

soup = BeautifulSoup(html, "lxml")

このようなエラーが出る場合あります。

Couldn't find a tree builder with the features you requested: lxml.

そんなときは以下のようにすると解決できました。

soup = BeautifulSoup(html, "html.parser")