PythonでKindleの新着本データをGoogleスプレッドシートに出力する方法(その2)

Googleスプレッドシートにhelloを表示

この記事では、前回の続きで
KindleのWebページのデータを
Googleスプレッドシート に
出力します。


今回はビデオで作成中の録画を
しています。
事前に若干の練習をしていますが
ほぼライブに近いビデオ
となっています。

つまり、何年も業界を経てきて
いようが失敗を何度もする
という様を御覧ください。😂

中には失敗することが少ない
エンジニアもいるのですが
かなりの少数派です。

そして私は実は
30年以上もパソコンやってるのに
タッチタイピングできません。😂

早く打てればいいのです。
そうすれば
エンジニアになれるのです!

余談はここまでですが
今回はビデオが3本合計で
1時間30分もあります。
1本が長すぎたので3分割
しています。

今回はビデオの中で
記事では書ききれない情報を
つぶやいてます
ので
ぜひとも拾い上げてください!

そのぶん、記事の方は
中身がありません😂

ビデオ編集に慣れていないため
ビデオ編集だけですでに20時間ほど
です。もうカウントしていません。

前提

この記事は Windows 10上で、
Python3 を Anaconda 環境上で使う
ことを前提に書いています。

また、下記の2つの記事
『PythonでAmazon Kindleの書籍をWebスクレイピング』
および
『PythonでKindleの新着本データをGoogleスプレッドシートに出力する方法(その1)』
からの続編のため
上記2つの記事の内容を進めてからでないと
本記事の内容は実行できません。

動作を確認した環境

OS: Windows 10 20H2 (OSビルド 19042.804)
Python 3.8.3

今だけの謝辞

この記事は書きかけです。

ビデオ編集に時間を割き過ぎており
終わりきらなかったため
記事を後から付け足していきます。

また、ビデオも3本を1本ずつ
公開していきます。

ご理解のほどよろしく
お願いいたします。

作成風景と実行結果

ビデオ1 2つのプログラムを合体させて関数化するところまでの作成風景のビデオです。

ビデオ2 Pythonの リスト を作成して関数間でとりあえずデータを受け渡しするところまでの作成風景のビデオです。

ビデオ3 データに合わせてループしてリストを作成するよう修正し、完成させるまでの作成風景のビデオです。


ビデオ中で説明が不足していたことをお伝えします。

ビデオ中ではPythonにのリスト(list)を使用することを
思いついているわけですが
もし基礎を勉強していない状態のあなたならば
どうやってリストという答えを導くでしょうか?

『python 表形式』あたりでWeb検索すると
出てくるかもしれません

それどころか
誰か信頼できる人に聞けば
もっと早くて確実でしょう。

一方で
基礎から学習していた場合
上達しないうちは
自力で早く答えを導き出せる可能性は
かなり低いでしょう。

プログラムの内容

今回のプログラムは
ついにkindleのWebページから
取得した情報を
Googleスプレッドシートに表示させます。

ただし、今回新しくやったことは

2回にわたって作成した
プログラムを合体させて
2つの関数にして呼び出す

ようにしたこと。

関数同士でのデータのやり取りを
pythonの リスト を使って行った

こと。

スプレッドシートのセルの
列方向と行方向に沿って
2重のループをして
データを埋めていった

ことです。

[kindle_to_gss.py]

# 事前に以下の準備が必要
#
# 1. 以下のコマンドを実行
#    conda install bs4
#    conda install selenium
# 2. Google Chromeをインストール
# 3. "Chrome Driver"を下記URLから入手
#    https://sites.google.com/a/chromium.org/chromedriver/home
# 4. "Chrome Driver"をどこか管理しやすい場所に移動
# 5. Amazonのアカウント E-mail, パスワードを用意する

from selenium import webdriver  # seleniumパッケージからWebブラウザ自動操作モジュール
                                # webdriverをインポートする
from bs4 import BeautifulSoup   # bs4パッケージからHTLML解析を行うBeautifulSoup
                                # モジュールをインポートする
import time  # time.sleep()で時間待ちするためだけにtimeモジュールをインポートする
import gspread  # Googleスプレッドシート操作モジュール
                # https://gspread.readthedocs.io/en/latest/
from google.oauth2.service_account import Credentials as cred # Google OAuth認証モジュール


def get_data_from_kindle():

	wait_time = 30 # 各Web操作での最大の待ち時間
	wait_time_login = 3 # の固定の待ち時間

	# Google ChromeのWebドライバを起動する
	driver = webdriver.Chrome('C:\\Program Files (x86)\\chromedriver_win32\\chromedriver')
	driver.implicitly_wait(wait_time) # 各Web操作での最大の待ち時間を設定

	# Amazonのログインページに飛ぶ
	driver.get("https://www.amazon.co.jp/ap/signin?_encoding=UTF8&accountStatusPolicy=P1&openid.assoc_handle=jpflex&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.pape.max_auth_age=0&openid.return_to=https%3A%2F%2Fwww.amazon.co.jp%2Fgp%2Fyour-account%2Forder-history%3Fie%3DUTF8%26digitalOrders%3D1%26opt%3Dab%26orderFilter%3Dyear-2018%26returnTo%3D%26unifiedOrders%3D1&pageId=webcs-yourorder&showRmrMe=1")

	'''
	# E-mailアドレスを入力する
	email_elem = driver.find_element_by_id("ap_email") # E-mail入力欄を見つける
	email_elem.send_keys("hogehoge") # E-mailアドレスを入力する
	email_elem.submit() # ボタンを押す(送信)

	# パスワードを入力する
	password_elem = driver.find_element_by_id("ap_password") # パスワード入力欄を見つける
	password_elem.send_keys("hogehoge") # パスワードを入力する
	password_elem.submit() # ボタンを押す(送信)
	'''

	time.sleep(wait_time_login) # もっといい方法がないか今後見つけたい


	'''
	Amazon新着ページの本一覧部分のHTML構造

	<span class="aok-inline-block zg-item">
		<a class="a-link-normal" target="_blank" href="hoge">
			<span class="zg-text-center-align">
				<div class="a-section a-spacing-small">
					<img alt="この世界の片隅に : 上 (アクションコミックス)" src="hoge.jpg"
						height="200" width="200">
				</div>
			</span>
			<div class="p13n-sc-truncate-desktop-type2 p13n-sc-truncated"
				aria-hidden="true" data-rows="1" data-truncate-by-character="1">
				この世界の片隅に : 上 (アクションコミックス)
			</div>
		</a>
		<div class="a-row a-size-small">
			<a class="a-size-small a-link-child" href="hoge">
				こうの史代
			</a>
		</div>
		<div class="a-icon-row a-spacing-none">
			<a class="a-link-normal" title="5つ星のうち 4.6" href="hoge">
				<i class="a-icon a-icon-star a-star-4-5 aok-align-top">
					<span class="a-icon-alt">
						5つ星のうち 4.6
					</span>
				</i>
			</a>
			<a class="a-size-small a-link-normal" href="hoge">
				369
			</a>
		</div>
		<div class="a-row a-size-small">
			<span class="a-size-small a-color-secondary">
				Kindle版
			</span>
		</div>
		<div class="a-row">
			<a class="a-link-normal a-text-normal" target="_blank" href="hoge">
				<span class="a-size-base a-color-price">
					<span class="p13n-sc-price">
						¥99
					</span>
				</span>
			</a>
		</div>
	</span>
	'''

	data=[['タイトル','リンク','画像','評価','著者','著者リンク','レビュー数','レビューリンク','版','価格']]

	page = 1 # Amazon新着ページの表示ページを1ページ目から開始する

	while True:
		# Amazon新着ページのURL(ページ番号付き)
		url = "https://www.amazon.co.jp/gp/bestsellers/digital-text/" \
				f"4897508051/ref=zg_bs_pg_1?ie=UTF8&pg={page}"

		ret = driver.get(url) # Amazon新着ページに飛ぶ

		# ページの内容をHTMLとして解析しておいしいスープを作る
		soup = BeautifulSoup(driver.page_source,"html.parser")

		# スープの中から新着本の一覧を一挙に取り出してelems(要素群)とする
		elems = soup.find_all(class_="zg-item")

		if len(elems) == 0 :
			# 『ページが見つからなかった』ページが表示されたと判斷する
			break

		# 要素群から1つ1つ要素(elem)を取り出してループする
		for elem in elems:
			# 予め初期化する それは、商品によってデータが無い部分あるので・・・
			img_link = ''
			book_title = ''
			book_link = ''
			star = ''
			author = ''
			author_link = ''
			review_num = ''
			review_link = ''
			version = ''
			price = ''

			# 商品のデータを取り出す
			# 商品によってデータが無い部分があるので ひたすらtryして
			# なかったら(AttributeErrorなら)、パス(pass)する
			# 実はもっとスマートな方法があるのですが、説明が難しくなるのでやめます
			try:
				# 次に見つかった<img>タグのsrc属性を取り出す
				img_link = elem.find_next("img").get("src")
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったp13s-... クラスの<div>タグで囲んだテキストを取り出す
				book_title = elem.find_next("div", class_="p13n-sc-truncate-desktop-type2 p13n-sc-truncated").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-link-normalクラスの<a>タグのhref(参照URL)を取り出す
				book_link = elem.find_next("a", class_="a-link-normal").get("href")
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-icon-altクラスの<span>タグで囲んだテキストを取り出す
				star = elem.find_next("span",class_="a-icon-alt").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-size-small ...クラスの<a>タグで囲んだテキストを取り出す
				author = elem.find_next("a", class_="a-size-small a-link-child").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-size-small ...クラスの<a>タグのhref(参照)を取り出す
				author_link = elem.find_next("a", class_="a-size-small a-link-child").get("href")
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-size-small ...クラスの<a>タグで囲んだテキストを取り出す
				review_num = elem.find_next("a", class_="a-size-small a-link-normal").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-size-small ...クラスの<a>タグのhref(参照)を取り出す
				review_link = elem.find_next("a", class_="a-size-small a-link-normal").get("href")
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったa-size-small ...クラスの<span>タグで囲んだテキストを取り出す
				version = elem.find_next("span", class_="a-size-small a-color-secondary").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass
			try:
				# 次に見つかったp13n-sc-priceクラスの<span>タグで囲んだテキストを取り出す
				price = elem.find_next("span", class_="p13n-sc-price").text
			except AttributeError:
				# データ(属性=Attribute)のないものだった
				pass

			data.append([book_title,book_link,img_link,star,author,author_link,review_num,review_link,version,price])

		page += 1 # Amazon新着ページの表示ページを1つ進める

	driver.close()

	return data


def output_data_to_gss(data):
	# Google APIで発行したキーファイルの保存場所を変数に代入
	keyfile = 'keyfile.json'

	# 操作対象のoogleスプレッドシートのブック名を変数に代入
	bookname = 'Kindle新着本'

	# 操作対象のGoogleスプレッドシートのシート名を変数に代入
	sheetname = 'シート1'

	# Google APIを使用する範囲(scope)を設定する。
	# Googleスプレッドシートと、それを保存するGoogle Driveを読み書きする範囲に設定する。
	# scopeの値は ⇒ https://developers.google.com/sheets/api/guides/authorizing
	# spreadsheet scope でWeb検索して見つかった
	scope = ['https://www.googleapis.com/auth/spreadsheets',
			'https://www.googleapis.com/auth/drive']

	# キーファイルと使用範囲から資格情報(credential)を取得する
	credentials = cred.from_service_account_file(keyfile, scopes=scope)

	# 資格情報をもとに、Googleスプレッドシートへのアクセスの承認を得る(authoprize)
	gc = gspread.authorize(credentials)

	# ブック名を指定してGoogleスプレッドシートを開く
	book = gc.open(bookname)

	# ブック内のワークシートを取得する
	sheet = book.worksheet(sheetname)

	for row in range(len(data)):
		for col in range(len(data[0])):
			# ワークシート内のセルを更新(update)する
			try:
				sheet.update_cell(row+1,col+1,data[row][col])
				time.sleep(1)
			except:
				time.sleep(60)
				sheet.update_cell(row+1,col+1,data[row][col])

	# その他のワークシート操作は下記サイトにある
	# 参考:https://qiita.com/plumfield56/items/dab6230512f3381fdcad




# kindleから情報を取得
data = get_data_from_kindle()

#data = [['タイトル', 'リンク'], ['ほげ日記', 'http://hoge.com'], ['へろ日記', 'http://hero.com']]

# Googleスプレッドシートにデータを出力する
output_data_to_gss(data)

まとめ

いかがでしたでしょうか。
ついにKindleから取ってきた
データをGoogleスプレッドシートに
書き出せました。

ただ、Googleスプレッドシートには
1ユーザが1分以内に書き込める回数
が決まっていて遅い処理しかできません。

ただし、Googleに申請をすると
この上限値を上げることができるそうです。

日頃よく使う人で本格的に自動化するときは
上限を上げる申請を出したほうがよさそうです。

なお、英語についてはGoogle翻訳に頼っても
いいでしょう。
でも、ある程度覚えるようにしておけば
すぐに対応できるので
はかどりますよね!

では、また次回の記事をお楽しみください。😊

タイトルとURLをコピーしました