BeautifulSoup4でタグをCSVに変換する

229, 2021-04-15

目次

BeautifulSoup4で得たタグをCSVに変換する

PythonにはHTML/XMLパーサーとしてBeautifulSoup4という外部ライブラリがあります。
これを使うと簡単にHTML/XMLなどのドキュメントをパースすることが可能です。

今回は、このBeautifulSoup4を使って得たタグのリストをCSVファイルに書き込んでみたいと思います。
具体的には↓を見ていきます。

  • CSVとは?

  • 単一のulをCSVに変換する

  • 複数のulをCSVに変換する

  • テーブルをCSVに変換する

CSVとは?

CSV(シーエスブイ)とは「Comma Separated Values」の略です。
これは直訳すると、「カンマで区切られた複数の値」という意味になります。
たとえば↓のようなものがCSVフォーマットの文字列です。

one,two,three
cat,dog,bird

値がカンマ(,)で区切られて並べられています。
この時一行を「行」または「レコード」といい、これが1つの塊のデータとして扱われます。
行(レコード)が複数並んでいる状態はつまり複数のデータがある状態です。

たとえば学校の名簿を考えてみたいと思います。
名簿には学年や名前、出席番号などが書かれています。
この時生徒1人分のデータがレコードになります。

つまり↓ということです。

1,田中太郎,101
1,山田花子,102
1,原人北京,103
...

HTMLなどののデータをこのようなCSVに変換したいというのは割とよくあることです。
スクレイピングなどでデータを取得して、そのHTMLのデータから目的のデータを抽出して、それをファイルにCSVで保存するという具合にです。

リスト内包表記について

この記事でよく使われるリスト内包表記の解説です。
知ってる方は飛ばしてください。

リスト内包表記とは↓のようなフォーマットの表記です。

結果 = [ 要素 for 要素 in リスト ]

↑のようにするとリスト内の要素が新しく出来たリストに保存され、結果として返ってきます。
具体的にコードにすると↓のようなコードになります。

lis = [1, 2, 3]
res = [el for el in lis]
print(res)

↑のコードを実行すると↓のような結果になります。

[1, 2, 3]

リスト内包表記ではforの左側にある要素に対して加工などを行うことができます。
たとえば要素を10で割るという加工を行うと↓のようなコードになります。

lis = [1, 2, 3]
res = [el / 10 for el in lis]
print(res)

↑のコードを実行すると↓のような結果になります。

[0.1, 0.2, 0.3]

リスト内包表記は普通のfor文と比べてコードが短くなるという特徴があります。

単一のulをCSVに変換する

それでは単一のulタグをレコードに変換してCSVファイルに保存してみます。
例としては↓のようになります。

from bs4 import BeautifulSoup
import csv

# 元になるHTML
code = '''<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
'''
soup = BeautifulSoup(code, 'html.parser')  # パースする
row = [li.text for li in soup.find_all('li')]  # liタグのテキストをリストにする

with open('out.csv', 'w') as fout:  # 書き込み用ファイルを開き
    writer = csv.writer(fout)  # ライターを作成し
    writer.writerow(row)  # レコードを一行書き込む

codeをBeautifulSoupでパースしてsoupという結果を得ます。
このsoupのメソッドfind_all()を呼び出してliタグのリストを得ます。
それをリスト内包表記で回してli.textをリストの要素に格納していきます。
これでrowというliタグのテキストが格納されたレコードができました。

あとはout.csvというファイルを開いて、そのファイルオブジェクトをcsv.writer()に渡します。
(今回はCSVファイルの保存にcsvモジュールを使います)
writerを作ったらwriterow()メソッドに先ほどのrowを渡して書き込みます。
これであとはwriterが良い感じのフォーマットにしてくれます。

書き込んだout.csvファイルの中身は↓のようになっています。

one,two,three

ul内のデータがCSVとして保存できてますね。

複数のulをCSVに変換する

複数のulタグをCSVに変換するには↓のようにします。

from bs4 import BeautifulSoup
import csv

# 元になるHTML
code = '''<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
<ul>
<li>cat</li>
<li>dog</li>
<li>bird</li>
</ul>
'''
soup = BeautifulSoup(code, 'html.parser')  # パースする
mat = []  # 行列

for ul in soup.find_all('ul'):  # ulタグを取り出す
    row = [li.text for li in ul.find_all('li')]  # liを取り出してtextを保存する
    mat.append(row)  # 行を追加

with open('out.csv', 'w') as fout:  # 書き込み用ファイルを開き
    writer = csv.writer(fout)  # ライターを作成し

    for row in mat:
        writer.writerow(row)  # レコードを一行書き込む

soupを作ってfind_all()ulタグを取り出してfor文で回します。
そしてそのulタグからfind_all()を呼び出してliタグを取り出します。
そのliタグはリスト内包表記で回してtextをリストの要素に保存します。
その結果をrowとして、出来あがったらmatに追加します。
ちなみにmatmatrix(行列)の略です。

matが出来上がったらout.csvファイルを書き込みモードで開きます。
そのファイルオブジェクトをcsv.writer()に渡してwriterを作ります。
matfor文で回して行(row)を取り出し、writerwriterow()メソッドに渡して行(レコード)をファイルに書き込みます。

書き込まれたout.csvを見ると↓のようになっています。

one,two,three
cat,dog,bird

テーブルをCSVに変換する

テーブルをCSVに変換したい場合は↓のようにします。

from bs4 import BeautifulSoup
import csv

# 元になるHTML
code = '''<table>
<tr>
    <td>one</td>
    <td>two</td>
    <td>three</td>
</tr>
<tr>
    <td>cat</td>
    <td>dog</td>
    <td>bird</td>
</tr>
</table>
'''
soup = BeautifulSoup(code, 'html.parser')  # パースする
mat = []  # 行列

# テーブルをパースする
table = soup.find('table')
for tr in table.find_all('tr'):  # trタグを取る
    row = [td.text for td in tr.find_all('td')]  # tdのtextを保存する
    mat.append(row)  # 行を追加

with open('out.csv', 'w') as fout:  # 書き込み用ファイルを開き
    writer = csv.writer(fout)  # ライターを作成し

    for row in mat:
        writer.writerow(row)  # レコードを一行書き込む

基本的には複数のulタグのパースと同じです。
まずsoup.find()tableタグを取ります。
そしてtableタグのfind_all()trタグを取ってきてfor文で回します。
trタグのfind_all()tdタグを取ってきて、リスト内包表記でtdtextをリストの要素に保存します。
その結果をrowとして、matに保存します。

matが出来上がったらout.csvを書き込みモードで開き、writer.writerow()で行を書き込みます。

おわりに

今回はBeautifulSoup4で取得したデータをCSVに変換してみました。
CSVはスクレイピングなどでもよく使われるデータフォーマットです。
Pythonではモジュールを使えば簡単に変換することができます。

(^ _ ^)

カンマで区切ってあげる

(・ v ・)

けっこうです



この記事のアンケートを送信する