"BOKU"のITな日常

62歳・文系システムエンジニアの”BOKU”は日々勉強を楽しんでます

WEBページの表データをEXCELファイルに取り込む/PythonでWebスクレイピング

WEBスクレイピングで必要なデータだけ目的のページから抜き取って、そのままEXCELで一覧表にするPythonプログラムを作ってみます。

f:id:arakan_no_boku:20190223210405j:plain

 

やりたいこと
 

例えば下記のサイトみたいな使えそうな表を提供してくれるサイトから、一覧表の部分(ランキングの表とか)だけを抜き取ってEXCELの表に自動的に変換したい。

そう思っているわけです。

ecodb.net

 資料にするのが楽ですからね。

 

必要なモジュール

手順としては。

  • WebサイトからHTMLファイルをダウンロードする。
  • HTMLファイルを解析して必要な部分のデータだけ抜き出す。
  • 抜き出したデータを編集してEXCEL表形式で保存する。

みたいになります。

幸い、便利なpythonのモジュールがあります。

まず、それをインストールします。

 

WebからHTMLファイルをダウンロードするモジュール

pip install requests

 

HTMLを解析して、必要なデータを抜き出すモジュール

pip install beautifulsoup4 

 

EXCELの読み書きをpythonからやる。

pip install openpyxl

 

とりあえず、これで道具としてはそろいます。

 

対象のWEBページを決めてHTMLを確認する

 

今回は「報道の自由度ランキング」を対象にします。

ecodb.net

このページのランキングの表をEXCELに取り込む対象にします。 

そのために、HTMLを確認します。

今回は、Google Chromeの「検証」機能を使います。 

 

ソースの検証

 

確認したい表にマウスをあてて、右クリックメニューから「検証」を選びます。

f:id:arakan_no_boku:20180110224838j:plain

そうすると、デベロッパーツールが立ち上がってきます。

クリックした場所のHTMLをスマートに確認できます。

f:id:arakan_no_boku:20180110225008j:plain

 

このページのHTMLの解析

 

HTMLを見て、一覧表部分のHTMLタグを確認します。

シンプルな<table>タグで構成されています。 

ポイントは、ranking_tableというクラス名のDIVタグの内側に、取得したいテーブルコンテンツがすべて収まっている。

 だから、ranking_tableというクラス名のDIVタグの内側を、すっぽり抜き取ってしまえば良いということがわかります。

それが確認できたので次にいきます。

 

pythonExcelファイルを作る

 

これは、あれこれ説明するよりもソースを見た方がはやいような気がします。 

コメントもつけてますし。

 

import requests as web
import bs4
import openpyxl as excel
from openpyxl.styles import Font
from openpyxl.styles import PatternFill
from openpyxl.styles import Border
from openpyxl.styles import Side
from openpyxl.styles import Alignment

# B1などのrange指定文字列に変換する
def cell(col,row):
    col_ar = ['B','C','D','E','F']
    return col_ar[col] + str(row)

# 列番号を列を示す文字に変換する
def col(col):
    col_ar = ['B','C','D','E','F']
    return col_ar[col]    
   
# 新規ワークブックオブジェクトを生成する
wb = excel.Workbook()
# アクティブシートを得る
sheet = wb.active
# シート名を変更する
sheet.title = "報道の自由度ランキング"
# B2セルにタイトルを書く。フォントサイズを24にして、センタリングする
sheet['B2'] = "報道の自由度ランキング"
sheet['B2'].font = Font(size=24)
sheet['B2'].alignment = Alignment(wrap_text=False,  # 折り返し改行
                                  horizontal='center',  # 水平位置
                                  vertical='center'  # 上下位置
        )
# セルを結合する
sheet.merge_cells('B2:F2')
# WEBページにアクセスして、HTMLを取得する
res = web.get('http://ecodb.net/ranking/pfi.html')
if(res.status_code == web.codes.ok):
    # パースしてDOMを取得する
    soup = bs4.BeautifulSoup(res.text,"html.parser")
    # ranking_tableクラスのDIVの内側にあるthタグをすべて取得する   
    th = soup.select('div[class=ranking_table] th')
    # thタグをヘダーとして書き込み、背景色とボーダーをつける
    for h in range(len(th)):
        sheet[cell(h,4)] = th[h].getText()
        sheet.row_dimensions[4].height = 20
        sheet.column_dimensions[col(h)].width = 17
        sheet[cell(h,4)].fill = PatternFill(patternType='solid', fgColor='E0FFFF00')
        sheet[cell(h,4)].border = Border(outline=True,
                                         left=Side(style='thin', color='FF000000'),
                                         right=Side(style='thin', color='FF000000'),
                                         top=Side(style='thin', color='FF000000'),
                                         bottom=Side(style='thin', color='FF000000')
        )
        sheet[cell(h,4)].alignment = Alignment(wrap_text=False,  # 折り返し改行
                                       horizontal='general',  # 水平位置
                                       vertical='center'  # 上下位置
        )
    # ranking_tableクラスのDIVの内側にあるtdタグをすべて取得する   
    td = soup.select('div[class=ranking_table] td')
    for i in range(len(td)):
        # 1行5項目なので5で割ったり、5の余りをとったりしている
        c = i % 5
        r = (i//5)+5
        # セルに値をセットする
        sheet[cell(c,r)] = td[i].getText()
        # セルの高さを20にする
        if(c == 0):
            sheet.row_dimensions[r].height = 20
        # 罫線を引く
        sheet[cell(c,r)].border = Border(outline=True,
                                                  left=Side(style='thin', color='FF000000'),
                                                  right=Side(style='thin', color='FF000000'),
                                                  top=Side(style='thin', color='FF000000'),
                                                  bottom=Side(style='thin', color='FF000000')
        )
        # 横方向は標準、縦方向は中央にする
        sheet[cell(c,r)].alignment = Alignment(wrap_text=False,  # 折り返し改行
                                       horizontal='general',  # 水平位置
                                       vertical='center'  # 上下位置
        )
     
else:
    res.raise_for_status()

# EXCELブックを保存する
wb.save('test02.xlsx')

 

ポイントです。

 

ソースのこの部分です。

soup.select('div[class=ranking_table] th')

HTMLの解析には「Beautifulsoup」を使ってます。

www.crummy.com

さきほど確認したように「ranking_tableというクラス名のDIVタグの内側に、取得したいテーブルコンテンツがすべて収まっている」という構造です。

上記の処理では、BeautifulsoupのSelectを使い、クラス名をキーにして、ずぼっとデータを抜いてます。

後続のコードは、そこから行のデータを順次取り出して、EXCELファイルに加工しているだけです。

EXCELの操作は「openpyxl」というpythonのライブラリを使ってやってます。

ソースを読むにあたっては、以下の記事とかの基本的な使い方を参照してもらえると、そんなに難しいことはやってないのでわかると思います。

note.nkmk.me

qiita.com

qiita.com

 

実行結果

 

これを実行して、生成されたEXCELシートはこんな感じです。

f:id:arakan_no_boku:20180111224915j:plain

なかなか良い感じです。 

いちいち、EXCELが立ち上がったりせずに、バックグラウンドで静かにEXCELファイルを作ってくれます。 

処理も速いですしね。

 

追加情報です。

 

Beautifulsoupのドキュメントの日本語訳サイトです。

ちょっとバージョンは古いですが。

kondou.com

EXCELブックの操作の「openpyxl」について、さらに詳しく知りたい場合にそなえて本家のリファレンスのリンクをのせておきます。

(残念ながら英語ですけど)

openpyxl.readthedocs.io

今回はこんな感じで。

ではでは。