アラカン"BOKU"のITな日常

文系システムエンジニアの”BOKU”が勉強したこと、経験したこと、日々思うことを書いてます。

pythonでWebスクレイピング(Requests)して、EXCELファイルに書き込み(openpyxl)資料化する

WEBで一覧になっているデータを、そのままEXCELの資料にできたら便利だな。

たまにそう思います。

自分がやりたいのは、主に。

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

こんな感じで資料に使えそうな表を提供してくれるサイトとかありますからね。

f:id:arakan_no_boku:20180110222920j:plain

ここから、一覧表の部分だけを抜き取って資料化できるかな・・ということで、やってみます。

 

まず、必要なモジュールをインストール

 

WebからHTMLファイルをダウンロードするモジュールがまず必要です。 

pip install requests

次に、ダウンロードしたHTMLを解析して、必要なデータを抜き出す処理に使うモジュールです。

pip install beautifulsoup4 

 最後に、EXCELの読み書きをpythonからやるツールです。

pip install openpyxl

とりあえず、これでOKなはずです。

 

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

 

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

ecodb.net

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

ここでHTMLを確認する必要があります。

ソースの表示でベタのHTMLを見るのもいいですが、Google Chromeなら、もう少しスマートな方法があります。 

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

f:id:arakan_no_boku:20180110224838j:plain

そうすると、デベロッパーツールが立ち上がってきて、クリックした場所のHTMLをスマートに確認できます。

f:id:arakan_no_boku:20180110225008j:plain

HTMLを眺めてみると、とりあえず、シンプルな<table>タグで構成されています。 

このページに関しては。

ポイントは、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')

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

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

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

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

note.nkmk.me

qiita.com

qiita.com

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

f:id:arakan_no_boku:20180111224915j:plain

なかなか良い感じです。 

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

処理も速いですしね。

 

追加情報です。

 

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

今回は、一番シンプルなセレクタ(select)しか使ってませんが、他にもいろいろ機能はあります。 

今のところ、日本語で一番情報が豊富だと自分が思っているサイトのリンクをのせておきます。

kondou.com - Beautiful Soup 4.2.0 Doc. 日本語訳 (2013-11-19最終更新)

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

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

openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files — openpyxl 2.3.3 documentation

まあ、今回はこんな感じで。