Python

Pythonスクレイピング入門編4 エクセルでのキーワード指定&データ保存をする

投稿日:

前回の第3回講座では、検索キーワードのキーボード入力を可能にしたり、結果表示を見やすく価格やショップ名も表示するように変更しました。

詳しくは↓から

Pythonスクレイピング入門編3 キーワード入力に対応&価格も表示する

今回の第4回目は、更に便利なプログラムにしてみましょう。

エクセルとの連携機能をつけ、今までの黒背景画面ではなく、エクセルですべての検索結果のデータを見られるようにしたいと思います。

このPython講座はシリーズ構成となっています。
前回までの内容を前提とした講座内容になりますので、ぜひ順番に読んでください。

目次

追加する機能

では、今回追加・改善する機能の詳細を書いておきます。

1 検索するキーワードをエクセルから読み取る

第2回では、複数キーワードに対応しましたし、第3回ではキーボードで検索キーワードを入力できるように、少しずつパワーアップさせてきました。

これを更にバージョンアップして、エクセルでキーワード自体を指定出来るようにします。

もちろん、キーワードは1つだけではなく、複数の指定が出来る仕様にします。

2 検索結果データをエクセルに書き出す

今までは、コマンドプロンプトの中の黒い画面に検索結果を表示してきました。

それだけでもスクレイピングの楽しさは感じていただけたと思いますが、今回はその結果をエクセルに書き出し、どんどん蓄積していけるような仕組みにします。

3 プログラム内にコメントをつける

今回は少し長いプログラムになります。

ですので、後からプログラムを見たときに何をする部分かをパッと見てわかるようにコメントを付けていきます。

完成コード

では完成したプログラムコードを見てみましょう。


import requests
from bs4 import BeautifulSoup
import openpyxl
import datetime

#エクセルを開く&各種変数設定
wbName='楽天検索結果.xlsx'
wb = openpyxl.load_workbook(wbName)
ss1 = wb['調査対象キーワード']
ss2 = wb['検索結果']

#検索結果リストを定義
itemKw=[]
itemName=[]
itemPrice=[]
itemShopName=[]
itemRank=[]

#対象キーワードを読み込む
kw=[]
for r1 in range(100):
    if ss1.cell(row=2+r1,column=1).value!=None:
        kw.append(ss1.cell(row=2+r1,column=1).value)
print('調査対象キーワードは{}個です'.format(len(kw)))

#スクレイピング本体
print('スクレイピングを開始します')
for i in range(len(kw)):
    print('キーワード:「{}」の検索結果を取得します'.format(kw[i]))
    url='https://search.rakuten.co.jp/search/mall/'+kw[i]+'/'
    html=requests.get(url)
    soup=BeautifulSoup(html.text,'html.parser')
    items=soup.select('.searchresultitem')
    rank=1

    for item in items:
        itemKw.append(kw[i])
        itemName.append(item.select_one('.title').text.replace('\n',''))
        itemPrice.append(int(item.select_one('.price .important').text.replace('\n','').replace(',','').replace('円','')))
        itemShopName.append(item.select_one('.merchant').text.replace(' ',''))
        itemRank.append(str(rank))
        rank+=1
print('スクレイピングが完了しました')

#エクセルデータの最終行を取得
for r2 in range(1000000):
    if ss2.cell(row=r2+2,column=1).value==None:
        maxRow=r2+1
        break

#エクセルに検索結果を書き出し
nowTime=datetime.datetime.now()
for r3 in range(len(itemName)):
    ss2.cell(row=maxRow+1+r3,column=1).value=nowTime
    ss2.cell(row=maxRow+1+r3,column=2).value=itemKw[r3]
    ss2.cell(row=maxRow+1+r3,column=3).value=itemRank[r3]
    ss2.cell(row=maxRow+1+r3,column=4).value=itemName[r3]
    ss2.cell(row=maxRow+1+r3,column=5).value=itemPrice[r3]
    ss2.cell(row=maxRow+1+r3,column=6).value=itemShopName[r3]

#エクセルファイルを保存
print('エクセルファイルを上書きします')
wb.save(wbName)

実行結果

それでは、このプログラムを実行したときの結果のエクセルを見てみましょう。

このようなデータを自動で収集し、保存して蓄積していってくれます。

エクセルのテンプレートを準備

今回は、エクセルのファイルを使います。一から作ると大変ですのでテンプレートを準備しました。↓からダウンロードしてください。

楽天検索結果.xlsx

注意事項

少し注意点があります。
ダウンロードした後、以下の作業を行い、少し注意して取り扱ってください。

  • ファイル名を「楽天検索結果.xlsx」に変更してください
  • 今回のPythonプログラムと同じフォルダに置いてください
  • エクセル内のシート名は変更しないでください

以上の事を守ってください。

プログラム解説

今回もプログラムを詳細に解説していきます。

是非ソースコードを見ながら確認していってください。

1-4行目 使用モジュールの宣言

まずはいつもどおり、必要なモジュールの読み込みを行います。

requestsとBeautifulSoupモジュールは定番でいつもと同じですが、今回は3行目「openpyxl」と4行目「datetime」モジュールを追加で読み込んでいます。

openpyxlは名前からなんとなく想像できるかもしれませんが、Pythonでエクセルファイルを取り扱うときに使うモジュールです。

datetimeも名前どおり、日付や時間をPythonで扱うために便利な機能をまとめたモジュールです。

openpyxlはPythonの標準ライブラリには含まれていないことが多いですので、以下を参考に、

pip install openpyxl

を実行し、モジュールをインストールしてください。

これだけ読めばOK。Pythonのパッケージ管理システム「pip」入門と使い方

6行目 コメント

#エクセルを開く&各種変数設定

こちらの#タグは、コメントタグです。
つまり、Pythonの処理には全く影響を与えない部分で、あくまでもプログラムのメモとか、第三者へのコメントとかを好きに記述することができます。

#を付けた部分から行の終わりまでがコメントとみなされ、Pythonは無視をすることになります。

もし多くのコメントを書く必要がある場合は、#を付けた行を沢山書いてもいいですし、もう一つ便利な書き方があります。

”’ここから
すべて
コメントです。
”’

このように、「”’」と「”’」で囲まれた部分をPythonはコメントとみなし、複数行になっても無視をしてくれます。

チームでのプログラミングの時などにはとても便利です。

ただし、#のコメントと異なり、”””はインデントレベルを直前の文と合わせないとエラーとなりますので、#で良いところは#で済ませるなど、少しでもプログラムを減らすようにしましょう。

7-10行目 エクセルを開く&エクセル関連の設定

wbName=’楽天検索結果.xlsx’
wb = openpyxl.load_workbook(wbName)
ss1 = wb[‘調査対象キーワード’]
ss2 = wb[‘検索結果’]

次に今回のメイン処理であるエクセルの操作の部分です。

wbName=’楽天検索結果.xlsx’

こちらは、これから使用するエクセルファイルのファイル名をwbNameという変数に格納します。
↑で、エクセルファイル名を「楽天検索結果.xlsx」にしてもらったのは、ここでその名前を指定するからです。

wb = openpyxl.load_workbook(wbName)

ここでは、インポートしたopenpyxlモジュールを使います。

wbというのが、ワークブック(つまりエクセルファイル)を操作するためのオブジェクトです。

load_workbookメソッドを使用し、その引数には先程のエクセルのファイル名を指定します。
wbNameにファイル名が入ってるので、変数で指定していますが、直接
wb = openpyxl.load_workbook(‘楽天検索結果.xlsx’)
のように指定しても問題はありません。

ss1 = wb[‘調査対象キーワード’]
ss2 = wb[‘検索結果’]

こちらは、ss1とss2というオブジェクトを作成しています。

エクセルファイルの「調査対象キーワード」というワークシートをss1に、「検索結果」というワークシートをss2として記憶させています。

以上で、エクセルファイルの指定と、使うワークシート名の指定が完了しました。
以後は、ss1とss2というオブジェクトでワークシートの内容を操作できます。

12-17行目 リストを宣言&初期化

itemKw=[]
itemName=[]
itemPrice=[]
itemShopName=[]
itemRank=[]

ここではスクレイピングしてきたデータをそれぞれのリストに追加していくための、リストの定義です。

こちらは以前までで学習済みですので割愛しますが、今回は、

  • 検索キーワード(itemKw)
  • 商品名(itemName)
  • 商品価格(itemPrice)
  • ショップ名(itemShopName)
  • 検索順位(itemRank)

という情報をエクセルに書き込むため、それらのデータを一時的に保存するリストが上記の5行になります。

19-24行目 検索キーワードをエクセルから読み込む

#対象キーワードを読み込む
kw=[]
for r1 in range(100):
    if ss1.cell(row=2+r1,column=1).value!=None:
        kw.append(ss1.cell(row=2+r1,column=1).value)
print(‘調査対象キーワードは{}個です’.format(len(kw)))

ここでは、エクセルから検索対象のキーワードの読み込みを行っています。

kw=[]

こちらのkwリストが、検索対象のキーワードを格納するリストです。

for r1 in range(100):

for文。

要は、エクセルのキーワードリストを何行目まで読み取るかというのをrange(100)で指定しています。

今回は最大100キーワードまで読み込めるようにします。

if ss1.cell(row=2+r1,column=1).value!=None:

if文です。

ss1オブジェクトであるスプレッドシート(調査対象キーワード)の2+r1行目かつ1列目の値が空白でなければ。という条件式です。

スプレッドシートオブジェクト.cell(row=○,column=○).value

という指定で、エクセルの特定のセルの値を取得できます。
頻繁に使いますので覚えてしまってください。

kw.append(ss1.cell(row=2+r1,column=1).value)

↑で、1列目を上からどんどん読み込み、キーワードがセットされている場合にこちらの処理に移ります。

ここでは、kwリストに、読み込んだキーワードをどんどん追加していきます。
appendメソッドを再び使います。

print(‘調査対象キーワードは{}個です’.format(len(kw)))

ここでは、キーワードの読み込みが完了した後に何個キーワードが設定されていたかを確認の意味で画面表示させます。

以前学習したformatメソッドに、len(kw)というkwリストの要素数を当て込み、表示させます。

こういう作業の途中経過を画面表示していけば、バグに気づきやすくなるのでおすすめです。

26-43行目 スクレイピングを行う

#スクレイピング本体
print(‘スクレイピングを開始します’)
for i in range(len(kw)):
    print(‘キーワード:「{}」の検索結果を取得します’.format(kw[i]))
    url=’https://search.rakuten.co.jp/search/mall/’+kw[i]+’/’
    html=requests.get(url)
    soup=BeautifulSoup(html.text,’html.parser’)
    items=soup.select(‘.searchresultitem’)
    rank=1

    for item in items:
        itemKw.append(kw[i])
        itemName.append(item.select_one(‘.title’).text.replace(‘\n’,”))
        itemPrice.append(int(item.select_one(‘.price .important’).text.replace(‘\n’,”).replace(‘,’,”).replace(‘円’,”)))
        itemShopName.append(item.select_one(‘.merchant’).text.replace(‘ ‘,”))
        itemRank.append(str(rank))
        rank+=1
print(‘スクレイピングが完了しました’)

メインのスクレイピング部分は、今までと大きく変わっていません。

すべての「.searchresultitem」のhtml文をitemsリストに格納した上で、それを次々とループしてitemに代入し、検索結果のデータ(リスト)に追加していく というだけです。

45-49行目 エクセルデータの最終行を取得する

#エクセルデータの最終行を取得
for r2 in range(1000000):
    if ss2.cell(row=r2+2,column=1).value==None:
        maxRow=r2+1
        break

ここでは、書き出すエクセルファイルの「検索結果」シートが現在どこまでデータが入っているかを確認する処理です。
なぜ必要かというと、最終行を取得することで、その次の行からデータを追加していくことが可能になるからです。

for r2 in range(1000000):

このfor文で、r2に0から999,999まで順次数値を代入して繰り返します。
つまり、r2はエクセルの行数を表しており、各行にデータが入っているかをチェックしていくループ部分です。

if ss2.cell(row=r2+2,column=1).value==None:

こちらは、ss2つまり検索結果シートオブジェクトのセルの1列目を2行目(r2+2)からずーっとチェックしていく文です。
もしセルの内容がNone つまり何も入力がされてなければ、その行の前の行が最終行であるということが言えますので、それをif文で判断させています。

maxRow=r2+1
        break

上記の空白行を見つけた場合の実際行われる処理です。

maxRowという変数に、最終的にデータが入っている行数を保存しておきます。
r2は0からはじまりますが、スプレッドシートオブジェクトのrow変数は1行目からの指定になるので、r2に1を足したのが実際の最終行という意味で+1をしています。

また、最終行をセットしたら、もう100万行目まで見る必要がないので、この時点でbreak文を入れてあり、forを抜けるようにしました。
Pythonがエクセルを検査するのはあっという間の作業ですが、少しでも無駄な処理を減らす方がPCにも優しいです。
breakが無いと、どんどん100万行目までチェックして行き、maxRowが増えていってしまうので、ここでのbreakは必須でもあります。

51-59行目 エクセルに書き出す

#エクセルに検索結果を書き出し
nowTime=datetime.datetime.now()
for r3 in range(len(itemName)):
    ss2.cell(row=maxRow+1+r3,column=1).value=nowTime
    ss2.cell(row=maxRow+1+r3,column=2).value=itemKw[r3]
    ss2.cell(row=maxRow+1+r3,column=3).value=itemRank[r3]
    ss2.cell(row=maxRow+1+r3,column=4).value=itemName[r3]
    ss2.cell(row=maxRow+1+r3,column=5).value=itemPrice[r3]
    ss2.cell(row=maxRow+1+r3,column=6).value=itemShopName[r3]

ここでは、各リストに追加していったデータを実際にスプレッドシートに書き出す処理です。

nowTime=datetime.datetime.now()

今回は、書き出すときに「検索した日時」を同時に記録していくため、このプログラムを実行したときの日時を取得します。

そのために、nowTimeという変数に、先頭でimportしたdatetimeモジュールのdatetime.now()メソッドによりその時点の日時を取得し格納します。

このnowTimeを全部の行に書き出すことになります。

  for r3 in range(len(itemName)):
 ss2.cell(row=maxRow+1+r3,column=1).value=nowTime
    ss2.cell(row=maxRow+1+r3,column=2).value=itemKw[r3]
    ss2.cell(row=maxRow+1+r3,column=3).value=itemRank[r3]
    ss2.cell(row=maxRow+1+r3,column=4).value=itemName[r3]
    ss2.cell(row=maxRow+1+r3,column=5).value=itemPrice[r3]
    ss2.cell(row=maxRow+1+r3,column=6).value=itemShopName[r3]

この処理はとってもシンプルです。

取得した商品名のリストのデータ数分までforのループでどんどんデータを呼び出し、「検索結果」シートのmaxRow(最終行)+1+r3行目に書き出していく処理です。

61-63行目 エクセルを保存する

#エクセルファイルを保存
print(‘エクセルファイルを上書きします’)
wb.save(wbName)

最後の処理です。

データを書き込み終わったエクセルファイルですが、そのまま閉じるとせっかくのデータは消えてしまいますので、ここで上書き保存をして終了をします。

wb.save(wbName)

という、ワークブックオブジェクト名.save(ファイル名) という指定をすればそのファイル名で保存出来ます。

今回は、最初に指定したワークブック名と同じものを指定することで上書き保存をすることになります。

以上で終了です。

まとめ

いかがでしたでしょうか。

エクセルファイルですべてのキーワード指定も出来、さらに結果も書き出してくれるという仕組みに成長しました。

これでプログラムとデータを完全に分離できました
この考え方は非常に重要です。ユーザは一切プログラムコードを触る必要がなくなり、エラーや誤編集をしてしまうリスクをゼロにできるからです。

エクセルファイルも手軽に扱うことのできるPythonの凄さや、便利さを感じていただけたのではないでしょうか。

 







- Python


  1. kazu より:

    キーワードを、削除して新たなキーワードで、スクレイピングするとエラーになるのですが、どういったことが考えられますか?不躾な質問ですみません。テンプレートで、そのままスクレイピングすると成功はします。

comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

お金関係に詳しいアラフォー子持ちブロガー。

副業でせっせとお小遣い増を目論むも、副業が順調すぎて脱サラ&法人化を実現しました。

貯金額は?

Loading ... Loading ...