<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<atom:link href="https://officevba.info/feed/" rel="self" type="application/rss+xml" />
	<link>https://officevba.info</link>
	<description>仕事の役に立つVBA・GAS・Pythonのコードを紹介していきます。</description>
	<lastBuildDate>Sat, 31 Jan 2026 12:16:46 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://officevba.info/wp-content/uploads/2017/04/cropped-Excel_1-32x32.jpg</url>
	<title>VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<link>https://officevba.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>VBAの実行前に作成したマクロを別ファイルで保存</title>
		<link>https://officevba.info/savecopyas/</link>
					<comments>https://officevba.info/savecopyas/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 26 Nov 2024 11:44:42 +0000</pubDate>
				<category><![CDATA[ブックの操作]]></category>
		<category><![CDATA[FSO(FileSystemObject)]]></category>
		<category><![CDATA[SaveCopyAs]]></category>
		<category><![CDATA[バックアップ]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2541</guid>

					<description><![CDATA[保存する前に動作させたVBAがフリーズ 保存するのを忘れてVBAコードを実行した結果、Excelがフリーズしてもう一度コードを書きなおすのは多分あるあるだと思うのですが、この間も懲りずにやらかしました。 そこでコード実行 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>保存する前に動作させたVBAがフリーズ</h2>
<p>保存するのを忘れてVBAコードを実行した結果、Excelがフリーズしてもう一度コードを書きなおすのは多分あるあるだと思うのですが、この間も懲りずにやらかしました。</p>
<p>そこでコード実行時にバックアップを作成するコードを考えてみましたのでご紹介します。</p>
<h2>通常のファイルの保存での問題点を解決するSaveCopyAsメソッド</h2>
<p>ファイルをバックアップ用に保存することを考えた場合、通常の上書き保存（ThisWorkbook.Save）だと「保存前の状況に戻したい」場合に困ることがあります。</p>
<p>また、「名前を付けて保存（ThisWorkbook.SaveAs ○○）」だとコピーの動作後に名前をつけて保存したファイルが現在開いている状態になり、元のファイルは保存前の状態で閉じられた扱いになってしまいます。</p>
<p>理想的には①現在のファイルは保存前の状態で維持しつつ、②作成したツールをバックアップとして保存することで、この挙動をかなえてくれるのがSaveCopyAsメソッドです。<br />
（FileSystemObjectのCopyFileだと以前の保存状態のものをバックアップに保存、今のファイルを新しく保存する運用でSaveCopyAsメソッドとはバックアップファイルと現在のファイルが逆になります。今回はSaveCopyAsメソッドの挙動が想定していたものでしたのでSaveCopyAsメソッドを利用します。）</p>
<h2>実行前にマクロファイルをバックアップとして保存するコードの紹介</h2>
<p>以下のコードをツールに組み込んでおいて、作成したコードの先頭に「Call 実行前にVBAを含むツールファイルを自動保存」の1行を入力しておくことでツールファイルが格納されているフォルダに「backUp」フォルダが作成され、ツールファイルのバックアップが保存されます。</p>
<p>元になるツールファイル自体は保存前の状態、バックアップファイルはツールの動作時の状態になりますので、フリーズしてしまったり、無限ループに陥って強制終了した場合などは「backUp」フォルダ内の直近で作成されたファイルを利用すればすぐに復旧可能です。</p>
<div class="VBACode">
<pre>
Sub 実行前にVBAを含むツールファイルを自動保存()

    Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
    '参照設定「Microsoft Scripting Runtime」を設定している場合
    'Dim fso As FileSystemObject: Set fso = New FileSystemObject
    
    If Not fso.folderExists(ThisWorkbook.Path &#038; "\backUp") Then
        fso.createFolder (ThisWorkbook.Path &#038; "\backUp")
    End If

    ThisWorkbook.SaveCopyAs (ThisWorkbook.Path &#038; "\backUp\" &#038; Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 4) &#038; Format(Now, "yyyymmdd_hhmmss") &#038; ".xlsm")

End Sub
</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/savecopyas/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>コピペを多用して動作が重くなってしまったブックをVBAで軽量化した事例</title>
		<link>https://officevba.info/misbehavingtextbox/</link>
					<comments>https://officevba.info/misbehavingtextbox/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 08 Sep 2024 01:26:43 +0000</pubDate>
				<category><![CDATA[シートの操作]]></category>
		<category><![CDATA[オブジェクト]]></category>
		<category><![CDATA[テキストボックス]]></category>
		<category><![CDATA[削除]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2511</guid>

					<description><![CDATA[同僚からの動作の重いブックについて相談 私の勤務先の同僚から「原因がよくわからないが、処理が重くなってしまったファイルを改良して使いやすくしたい」と相談を受けました。 その際ちょっと変わった状況になっていたので、備忘録と [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>同僚からの動作の重いブックについて相談</h2>
<p>私の勤務先の同僚から「原因がよくわからないが、処理が重くなってしまったファイルを改良して使いやすくしたい」と相談を受けました。<br />
その際ちょっと変わった状況になっていたので、備忘録と情報共有の目的で記事にしておきます。</p>
<h2>セルの結合があるので動作が重い？</h2>
<p>同僚が言うには、セルの結合が入っているので処理が重くなっていて、また修正に手間がかかるのが困るとのことでブックを確認しました。<br />
ざっくりとした内容ですが、1つのブックの中にシートは一つで、その中には3種類の研修について1日のスケジュールが書かれた表になっていました。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/09/VBA174-1.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2024/09/VBA174-1-88x300.jpg" alt="" width="88" height="300" class="alignnone size-medium wp-image-2513" srcset="https://officevba.info/wp-content/uploads/2024/09/VBA174-1-88x300.jpg 88w, https://officevba.info/wp-content/uploads/2024/09/VBA174-1.jpg 347w" sizes="(max-width: 88px) 100vw, 88px" /></a></p>
<p>5分刻みでスケジュールを調整したいらしく、コマ数に応じて行数を変更してセルを結合する書式で、確かにセルの結合は複数使われていました。<br />
ただ感覚的な判断ですが、100行15列くらいの使用でセル結合を多用しただけで動作が重くなるのは考えにくい気がしました。</p>
<p>以前に新しいブックにシートの全セルをコピーしてそのままペーストすると動作が軽くなったことがあるとも同僚が言っていたこともセル結合が影響していないのではないかと思うきっかけでした。</p>
<h2>原因の調査と特定</h2>
<p>とりあえず、変なところに情報が残っていてそれが悪さをしている可能性を考慮して使用していない行列をいったん削除してみても動作は変わりませんでした。<br />
また、他のブックへのリンクも多少含まれていたのでリンクを切断してみたものの動作の重さは変わらなかったです。</p>
<p>次に同僚が以前に多少解消できたと話していた通りコピペで新しいシートに貼り付けようとしたら、PCがフリーズして動かなくなりました。<br />
使用しているセルを選択してコピーしたので、フリーズする原因は使用している範囲（Range）上にあるということがわかりました。</p>
<p>そこで、試しにセルに含まれているオブジェクトを確認してみたところ無茶苦茶な数のテキストボックスが含まれていました。<br />
後でVBAで数を確認したところ28,000個くらい配置されていましたw</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/09/VBA174-2.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2024/09/VBA174-2-99x300.jpg" alt="" width="99" height="300" class="alignnone size-medium wp-image-2514" srcset="https://officevba.info/wp-content/uploads/2024/09/VBA174-2-99x300.jpg 99w, https://officevba.info/wp-content/uploads/2024/09/VBA174-2.jpg 315w" sizes="(max-width: 99px) 100vw, 99px" /></a></p>
<p>誰かが何かのタイミングで思いがけず背景色・枠線なしのテキストボックスを作成し、行か列のコピーの際にそのテキストボックスも含めたコピーを繰り返した結果<br />
ゾンビのように増えたテキストボックスが動作を重くしていたようです。</p>
<h2>解決方法について</h2>
<p>背景色・枠線なしの見えないテキストボックスが悪さをしているのは確認できましたが、選択するだけでフリーズしてしまう状況で、手作業での良い解決方法が思いつかず、VBAを使用してテキストボックスをすべて削除して解決を図りました。</p>
<p>使用したコードは以下の通りです。</p>
<div class="VBACode">
<pre>
Sub シート上のオブジェクトすべて削除()
    
    Dim x As Long: x = ActiveSheet.Shapes.Count
    Dim i As Long: 
    For i = x To 1 Step -1
        ActiveSheet.Shapes(i).Delete
    Next i

End Sub
</pre>
</div>
<p>こちらのコードを実行した結果、無事にテキストボックスはすべて削除され、サクサク動作するようになりました。<br />
テキストボックスなどのオブジェクトを見えない状態で作成した際、気づかずに増殖して動作に悪影響を及ぼすことがあるのは新しい発見でした。<br />
共有するファイルの扱いには気を付けようと思いました。</p>
<h2>その他の改善点</h2>
<p>今回動作が重い原因は隠れたテキストボックスだったため不要なテキストボックスを削除することで動作は軽くなりました。<br />
ただ、コマ数に応じてセルの結合を変更するのはかなり手間そうでした。</p>
<p>そこで以前<a href="https://officevba.info/mergecells/" target="_blank">こちら</a>の記事で紹介したセルの結合を切り替えるVBAコードを組み込んでショートカットを割り当て、スムーズにコマの入力と時間の変更ができるようにして提案しました。</p>
<div class="VBACode">
<pre>
Sub セル結合とセル解除切替()
    
    If Selection.MergeCells = True Then
        Selection.MergeCells = False
    Else
        Selection.MergeCells = True
    End If

End Sub
</pre>
</div>
<h2>そもそものフォーマットの問題点と改善方法について</h2>
<p>今回のワークシートの使い方については「データの格納場所」と「配布・閲覧資料」を同時に1つのシートで完結させようとしている点が問題だと思います。<br />
またセルの結合自体がそもそもその後の加工に向かないので使わないほうが良いという話もあります。<br />
あるべき使い方としてはデータの格納場所としてテーブルの形式で構造化されたデータを用意し、それとは別に閲覧用シートに出力する形をとるのが望ましいと思います。</p>
<p>ただし、あるべき形でExcelファイルを運用しようとすると、フォーマットの作り方においてほかのセルの値を呼び出したりする知識が必要だったり、構造化データを格納する意識が必要になったり、難易度が上がります。</p>
<p>Excelは手軽に利用できる反面、使用する多くの方が関数などのExcelを便利に使う知識がないケースも多いです。<br />
個人的にはどこまでデータ管理の知識を落とし込めるかは使用者（達）次第になるので今回の運用も致し方ないと思っています。</p>
<p>要するにデータと配布資料が1か所に集まっていて構造化データになっていないし、セルの結合を使用していて編集に時間がかかるフォーマットだったとしてもVBA<br />
を用いてセルの結合などを効率化することで、編集を簡略化してしまえば実際の運用上で問題になることはないと考えています。<br />
特に今回使用したコードはすごく簡単なのでメンテナンスも不要ですし、扱いが楽なのも問題ないと思う理由です。</p>
<p>逆に構造化データの概念とかをいきなり説明してフォーマットを強引に変更したり、運用を強制したりした方が使用者（達）の反感を買ってしまったり、なじめなかったりして実業務に悪影響になることも多い気がします。<br />
（効率的な業務を推進したい身としては悩ましいですが。）</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/misbehavingtextbox/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GASを用いて差し込み印刷のような複数メール作成2</title>
		<link>https://officevba.info/gas-createdrafthtmlmail2/</link>
					<comments>https://officevba.info/gas-createdrafthtmlmail2/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Fri, 02 Aug 2024 09:41:59 +0000</pubDate>
				<category><![CDATA[Gmail作成]]></category>
		<category><![CDATA[GAS]]></category>
		<category><![CDATA[Gmail]]></category>
		<category><![CDATA[GoogleAppsScripts]]></category>
		<category><![CDATA[差し込み印刷]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2506</guid>

					<description><![CDATA[GAS版のメール大量送信ツールの改良版の検討 以前にこちらの記事で、GASを使用して差し込み印刷のように少しずつ内容を変えたメールをGmailで大量に送るツールをご紹介しました。 これはこちらの記事でExcelVBAでO [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>GAS版のメール大量送信ツールの改良版の検討</h2>
<p>以前に<a href="https://officevba.info/gas-createdrafthtmlmail/" target="_blank">こちら</a>の記事で、GASを使用して差し込み印刷のように少しずつ内容を変えたメールをGmailで大量に送るツールをご紹介しました。</p>
<p>これは<a href="https://officevba.info/excelvbaoutlookmailpreparation4/" target="_blank">こちら</a>の記事でExcelVBAでOutloookを使用してメールを送るツールのGAS版として作成したものでしたが、以下のような不便さがありました。</p>
<p>・スクリプトの実行時間の上限が6分（GoogleWorkSpaceだと30分）で、件数が多いと途中で止まる<br />
　多分重い添付ファイルを複数つけたメールを作成しているとGoogleWorkSpaceのアカウントでも200件いかないくらいで止まったりしました。</p>
<p>・途中で止まったりした場合にどこまでメールを作成していたかをスプレッドシートから判断できない</p>
<p>・あとは添付ファイルの入力欄をT列固定にしていたのですが、無駄なスペースが多くて見にくい問題もありました。</p>
<p>・そのほか添付ファイルの参照時にシートの行のループと添付フォルダに格納されているファイルを参照するループの二重ループになっていたものを一回のループで済むようにして高速化・効率化しました。</p>
<p>今回は上記の課題を解決したコードを作成しましたのでご紹介します。<br />
また、これまでいくつかGASでツールを作ってきて、よく使う機能を共通化する目的で関数を複数作成して使用しています。<br />
この辺りも使いながらご紹介していきます。</p>
<h2>ドライブの中に格納する添付ファイルについて</h2>
<p>こちらは以前のものと全く変わらない構成になっています。<br />
ツールのスプレッドシートファイルと同じフォルダに「添付」フォルダを用意し、メールに添付したいファイルをここに格納します。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/08/VBA173_1.jpg"><img fetchpriority="high" decoding="async" src="https://officevba.info/wp-content/uploads/2024/08/VBA173_1-700x302.jpg" alt="" width="700" height="302" class="alignnone size-large wp-image-2504" srcset="https://officevba.info/wp-content/uploads/2024/08/VBA173_1-700x302.jpg 700w, https://officevba.info/wp-content/uploads/2024/08/VBA173_1-300x130.jpg 300w, https://officevba.info/wp-content/uploads/2024/08/VBA173_1-768x332.jpg 768w, https://officevba.info/wp-content/uploads/2024/08/VBA173_1-1536x663.jpg 1536w, https://officevba.info/wp-content/uploads/2024/08/VBA173_1-2048x885.jpg 2048w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h2>スプレッドシートの構成</h2>
<h3>基本仕様</h3>
<p>用意したスプレッドシートは以下の画像のような構成になっています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/08/VBA173_2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2024/08/VBA173_2-700x211.jpg" alt="" width="700" height="211" class="alignnone size-large wp-image-2505" srcset="https://officevba.info/wp-content/uploads/2024/08/VBA173_2-700x211.jpg 700w, https://officevba.info/wp-content/uploads/2024/08/VBA173_2-300x91.jpg 300w, https://officevba.info/wp-content/uploads/2024/08/VBA173_2-768x232.jpg 768w, https://officevba.info/wp-content/uploads/2024/08/VBA173_2-1536x464.jpg 1536w, https://officevba.info/wp-content/uploads/2024/08/VBA173_2.jpg 1910w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>基本的には<a href="https://officevba.info/gas-createdrafthtmlmail/" target="_blank">以前の記事</a>とほぼ変わらない作りになっていますが若干変更を加えました。</p>
<p>B2セルに件名、B3セルに本文のテンプレートを記述し、送付先によって変更したい内容は<会社名>や<宛名>のように「<○○>」の形式で記載しておきます。</p>
<p>6行目以降にメールを送るための情報と件名・本文内に埋め込みたい内容の一覧表（送付リスト）を作成します。</p>
<p>送付リストの中で6行目のカラム名に対して件名・本文のテンプレートの中で「<カラム名>」という記載になっている箇所を置き換えてメールを作成します。<br />
画像ではメール本文の一行目の「<会社名>」の欄が作成したメールでは１通目が「株式会社〇×_1」、2通目が「株式会社▲□_2」となります。</p>
<p>添付ファイルの情報については部分一致で参照するかか完全一致で参照するかをコードの中で切り替えられるようにしていて、取得した添付ファイルの情報はI列にID、J列に完全なファイル名を記載する仕様になっています。</p>
<p>またメールの作成対象はA列が「〇」になっているものとなります。</p>
<h3>変更内容</h3>
<p>添付ファイルの情報を格納する列を可変にしました。<br />
以前のものは添付内容についてはT列に入力する必要がありましたが、T列でなくても大丈夫ないようになりました。</p>
<p>ただ、ファイルを格納するフォルダ名・スプレッドシートのカラム名（6行目）・スクリプトの中のフォルダ名を全て揃えておく必要があり、<br />
今回は「添付」という名前にしています。</p>
<h2>一括で少しずつ宛名や内容の異なる大量のメールを作成するGASのコード</h2>
<h3>コードと仕様の概要</h3>
<p>①メールを送付前に添付ファイルのID・ファイル名をリセットし、送信済みフラグを初期化する運用となります。<br />
（スクリプト：resetCompFlgAndAttachInfo、ボタンを設置）</p>
<p>②スクリプトの上限が近づくと（5分経過時）以下の挙動で繰り返しすべてのメールを作成するまで処理が続きます。<br />
・ループを抜けてスプレッドシートにメール作成済みのフラグを入力する<br />
・まだメール未作成の行が残っている場合はトリガーで自身のスクリプトを1分後に再実行する</p>
<p>③メールの添付情報は最初の実行時に参照してスプレッドシートに記載しています。<br />
別々にも実行できるようにボタンも容易しました。（inputArrachInfo・setMailFromSs）<br />
（スクリプト：inputArrachInfo、ボタンを設置、メール作成用のスクリプト：inputArrachInfoAndSetMailFromSsの最初にも実行）</p>
<h3>コードの紹介</h3>
<div class="GASCode">
<pre>// メール情報SS
let tarSs = SpreadsheetApp.getActiveSpreadsheet();
let mailListSheet = tarSs.getSheetByName('mailList');
let startRowIndex = 6;

// 送信するフラグの初期化と添付ファイル情報の削除
function resetCompFlgAndAttachInfo() {

  let mailListArr = func_getMailListArr(mailListSheet, startRowIndex); // メールの送付先・内容などの配列
  let flagColIndex = f_arr_colNameToIndex(mailListArr, '送信する') // カラム名が「送信する」になっているカラムを抜き出す;
  let flagArr = f_arr_colNameToColArr(mailListArr, 0, 1);

  // 添付ファイルのカラムインデックスを取得
  let attachColName = '添付';
  let attachColIndex = f_arr_colNameToIndex(mailListArr, attachColName) // カラム名がattachColNameになっているカラムを抜き出す
  // SSから添付ファイルの記載位置を取得
  let attachInfoArr = f_arr_colNameToColArr(mailListArr, attachColIndex, 3); // 添付のカラムを二次元配列に格納

  for (let i = 1; i < attachInfoArr.length; i++) {
    // 入力先のセルを初期化
    flagArr[i][0] = '〇';       // 送信するフラグの設定
    attachInfoArr[i][1] = '';   // 添付ファイルIDの初期化
    attachInfoArr[i][2] = '';   // 添付ファイル名の初期化
  }

  flagArr = f_arr_arrToSh(flagArr, mailListSheet, startRowIndex, flagColIndex + 1);         // 送信するフラグをSSに入力
  attachInfoArr = f_arr_arrToSh(attachInfoArr, mailListSheet, startRowIndex, attachColIndex + 1); // SSの添付情報を初期化

}

// 添付情報の取得とメール送信
function inputAttachInfoAndSetMailFromSs() {

  inputAttachInfo();
  setMailFromSs_TriggerRepeat();

}

// 添付ファイルをスプレッドシートに記入
function inputAttachInfo() {
  inputAttachInfoToSs('添付', 'partial');
}

// 添付ファイルを設定するコード
// 引数：対象シート・対象カラム名・perfect or partial
// perfect or partial：ファイル名から対象ファイルを参照する際に、完全一致で抽出するか、部分一致で抽出するか
function inputAttachInfoToSs(tarColName, matchType) {

  let mailListArr = func_getMailListArr(mailListSheet, startRowIndex); // メールの送付先・内容などの配列
  let tarColIndex = f_arr_colNameToIndex(mailListArr, tarColName) // カラム名がtarColNameになっている
  let attachObj = f_drive_getFileObjInChildFolder(tarColName)

  // 添付ファイルの情報をSSに入力
  let attachInfoArr = f_arr_colNameToColArr(mailListArr, tarColIndex, 3); // 添付のカラムを二次元配列に格納

  // 行のループ
  for (let i = 1; i < attachInfoArr.length; i++) {

    // 入力先のセルを初期化
    attachInfoArr[i][1] = '';
    attachInfoArr[i][2] = '';

    // セルに入力しておいた添付ファイルの情報を記載した値を取得 → 改行で分割して配列に格納
    let partialNameArr = attachInfoArr[i][0].split('\n');
    for (let x = 0; x < partialNameArr.length; x++) {

      // 対象ファイル名：partialNameArr[x]
      if (matchType == 'perfect') {
        // ファイル名完全一致で呼び出す場合      
        if (attachObj[partialNameArr[x]] != undefined) {
          attachInfoArr[i][1] = attachInfoArr[i][1] + '\n' + attachObj[partialNameArr[x]];
          attachInfoArr[i][2] = attachInfoArr[i][2] + '\n' + partialNameArr[x];
        }
      } else if (matchType == 'partial') {
        // ファイル名不完全でも呼び出す場合
        for (attachInfo in attachObj) {
          if (attachInfo.indexOf(partialNameArr[x]) != -1) {

            attachInfoArr[i][1] = attachInfoArr[i][1] + '\n' + attachObj[attachInfo];
            attachInfoArr[i][2] = attachInfoArr[i][2] + '\n' + attachInfo;

          }
        }
      }

    }
    attachInfoArr[i][1] = attachInfoArr[i][1].substring(1);     // 最初の改行を削除
    attachInfoArr[i][2] = attachInfoArr[i][2].substring(1); // 最初の改行を削除
  }

  f_arr_arrToSh(attachInfoArr, mailListSheet, startRowIndex, tarColIndex + 1); //SSに入力

}

// メールの作成
function setMailFromSs_TriggerRepeat() {

  let startTime = new Date(); // 時間計測用の開始時間取得
  let functionName = arguments.callee.name;
  deleteTrigger(functionName); // 時間切れで設定したトリガーを削除

  let mailTempArr = f_sh_shToArr(mailListSheet, 2); // SSからテンプレートの読み込み
  let mailFrom = mailTempArr[0][1]; // 送信元がある際は
  let mailSubjectTemplate = mailTempArr[1][1];
  let mailBodyTemplate = mailTempArr[2][1];

  let mailListArr = func_getMailListArr(mailListSheet, startRowIndex); // メールの送付先・内容などの配列
  let flagArr = f_arr_colNameToColArr(mailListArr, 0)                   // A列部分の入力用配列
  let attachColumnIndex = f_arr_colNameToIndex(mailListArr, "添付ファイルID"); // 添付ファイルIDの列インデックスを取得 

  // 行ごとにメール作成
  for (let i = 1; i < mailListArr.length; i++) {
    // 配信フラグがあれば送信
    if (flagArr[i][0] != '' &#038;&#038; flagArr[i][0] != '済') {

      let mailSubject = mailSubjectTemplate;
      let mailBody = mailBodyTemplate;

      let mailTo = mailListArr[i][1];
      let options = {};
      options['cc'] = mailListArr[i][2];
      options['bcc'] = mailListArr[i][3];
      if (mailFrom != ''){
        options['from'] = mailFrom;
      }

      // 件名と本文の追加
      for (let k = 4; k < mailListArr[0].length; k++) {
        mailSubject = mailSubject.split("<" + mailListArr[0][k] + ">").join(mailListArr[i][k]);
        mailBody = mailBody.split("<" + mailListArr[0][k] + ">").join(mailListArr[i][k]);
        if (mailListArr[0][k] == '') {
          break;
        }
      }

      // 添付ファイルの設定
      let attachFileNameArr = mailListArr[i][attachColumnIndex].split('\n');
      let attachFileArr = [];
      for (let x = 0; x < attachFileNameArr.length; x++) {
        if (attachFileNameArr[x] != '') { // 空文字列なら何もしない
          attachFileArr.push(DriveApp.getFileById(attachFileNameArr[x]).getBlob());
        }
      }
      options['attachments'] = attachFileArr;

      setMail(mailTo, mailSubject, mailBody, options, 'create');
      // Utilities.sleep(1000 * 10); // 10秒スリープ
      flagArr[i][0] = "済";
    }

    // 経過時間を取得して25分以上ならトリガーをセットして終了
    let setTriggerFlg = ifTimeOverSetTrigger(functionName, startTime);
    if (setTriggerFlg == true) {
      // ステータスをシートに入力
      flagArr = f_arr_arrToSh(flagArr, mailListSheet, startRowIndex, 1);
      return; // 終了
    }
  }

  // ステータスをシートに入力
  flagArr = f_arr_arrToSh(flagArr, mailListSheet, startRowIndex, 1);
  // 25分以内に完了したらトリガーを解除して終了
  deleteTrigger(functionName);

}

function sendDrafts() {
  var drafts = GmailApp.getDrafts(); // 下書きフォルダ内の最初の下書きメッセージ

  for (let i = drafts.length - 1; i >= 0; i--) {
    //if (drafts[i].getMessage().getSubject().indexOf("7312") != -1 ) {
    console.log(drafts[i].getMessage().getSubject());
    let msg = drafts[i].send();
    Utilities.sleep(3000);
    //}
  }
}

function func_getMailListArr(mailListSheet, startRowIndex) {

  let mailListArr = f_sh_shToArr(mailListSheet, 21); // メールの送付先・内容などの読み込み
  // メールのリスト部分を残すために5行分削除
  for (let i = 0; i < startRowIndex - 1; i++) {
    mailListArr.shift();
  }
  return mailListArr;

}

function setMail(mailTo, mailSubject, mailBody, options, createOrSend) {

  options['htmlBody'] = mailBody.replace(/\n/g, '<br>');
  //メール送信
  if (createOrSend == 'create') {
    GmailApp.createDraft(mailTo, mailSubject, mailBody, options); //to , Subject , Body の順
  } else if (createOrSend == 'send') {
    GmailApp.sendEmail(mailTo, mailSubject, mailBody, options); //to , Subject , Body の順
  }
}

// 二次元配列のカラム名からインデックス番号を返す
function f_arr_colNameToIndex(tarArr, tarColName) {

  let retColIndex;
  for (let k = 0; tarArr[0].length; k++) {
    if (tarArr[0][k] == tarColName) {
      retColIndex = k;
      break;
    }
  }
  return retColIndex;

}

// 二次元配列のカラム番号から必要なカラムを抜き出す
function f_arr_colNameToColArr(tarArr, tarColIndex, tarColumnsCount) {

  if (tarColumnsCount == undefined){
    tarColumnsCount = 1
  }

  let retArr = [];
  for (let i = 0; i < tarArr.length; i++) {
    retArr.push([]);
    for (let x = 0; x< tarColumnsCount;x++){
      retArr[i].push(tarArr[i][tarColIndex + x]);
    }
  }
  return retArr;

}

// 配列をシートに転記
// 一応、格納後のシートを再度配列に格納できるように配列を返すように設定
function f_arr_arrToSh(tarArr, tarSheet, tarRow, tarColumn) {
  
  tarSheet.getRange(tarRow, tarColumn, tarArr.length, tarArr[0].length).setValues(tarArr);
  tarArr = tarSheet.getRange(tarRow, tarColumn, tarArr.length, tarArr[0].length).getValues();
  return tarArr;

}

// 子フォルダ名から中に格納されているファイル一覧をObj型（Key：ファイル名　Item：ファイルID）で返す
function f_drive_getFileObjInChildFolder(tarChildFolderName) {

  let tarChildFoldeId = f_drive_getChildFolderId(tarChildFolderName);
  let files = DriveApp.getFolderById(tarChildFoldeId).getFiles();

  let retObj = {};

  while (files.hasNext()) {
    let file = files.next();
    retObj[file.getName()] = file.getId();
  }

  return retObj;

}

//子フォルダ名からフォルダIDを返す
function f_drive_getChildFolderId(tarChildFolderName) {

  let fileId = tarSs.getId(); // このファイルのID
  let folderIterator = DriveApp.getFileById(fileId).getParents(); // 親フォルダを取得
  let parentFolder = folderIterator.next();                       // 親フォルダの最初の1つ目を取得
  // let parentFolderId = parentFolder.getId();                   // 親フォルダのIDを取得
  // let childFolder = DriveApp.getFolderById(parentFolderId);

  let childFolders = parentFolder.getFolders(); //子フォルダ一覧を取得
  let tarChildFolderId;
  while (childFolders.hasNext()) {
    let childFolder = childFolders.next();
    let childFolderId = childFolder.getId();
    let childFolderName = DriveApp.getFolderById(childFolderId).getName();

    if (tarChildFolderName == childFolderName) {
      tarChildFolderId = childFolderId;
      break;
    }
  }
  return tarChildFolderId;
}

function f_sh_shToArr(tarSheet, columnsCount) {

  if (columnsCount == undefined){
    columnsCount = f_sh_getTarLastColumn(tarSheet);
  }
  retArr = tarSheet.getRange(1, 1, f_sh_getTarLastRow(tarSheet), columnsCount).getDisplayValues();
  return retArr;

}

//最終行取得
function f_sh_getTarLastRow(sheet) {

  //最終行の取得フロー開始
  let tarValues = sheet.getRange('A:Z').getValues().filter(String);  //A-Z列の値を全て取得
  let tarValuesFilterData = [];

  //配列の中で空欄のものを除外して、lengthを算出・最終行取得
  for (let i = 0; i < tarValues.length; i++) {
    if (tarValues[i].join("") != "") { //空文字列で行内のセルの値を結合
      tarValuesFilterData.push(tarValues[i]);
    }
  }
  return tarValuesFilterData.length;               //空白の要素を除いた長さを取得

}

//最終列取得
function f_sh_getTarLastColumn(sheet) {

  // 最初の10行が空欄以外の数をカウント
  let tarArr = sheet.getRange("1:10").getValues().filter(String);
  let tarArrFilterData = [];

  //配列の中で空欄のものを除外して、lengthを算出・最終行取得
  for (let k = 0; k < tarArr[0].length; k++) {
    let checkStr = "";
    for (let i = 0; i < tarArr.length; i++) {
      checkStr = checkStr + tarArr[i][k]; 
    }
    if (checkStr !=""){
      tarArrFilterData.push(tarArr[0][k]);
    }
  }
  return tarArrFilterData.length;               //空白の要素を除いた長さを取得

}

// 長い時間のかかるコードに対して、途中で終了してトリガーをセット・再開するための関数
function ifTimeOverSetTrigger(functionName, startTime) {

  let afterTime = new Date();
  let progressTime = afterTime - startTime;

  // 25分以上経過していたらトリガーをセット
  let tarMinute = 25;
  if (progressTime >= 1000 * 60 * tarMinute) {
    sendLineNotify(functionName + '_' + tarMinute + '分経過なのでトリガーをセットして終了');
    setTriggerOneMinite(functionName); //　1分後にトリガーセット
    return true;
  }

  return false;

}

// 1分後に実行するトリガーの設定
function setTriggerOneMinite(functionName) {

  //トリガーを一度削除
  let triggers = ScriptApp.getProjectTriggers();
  for (let trigger of triggers) {
    if (trigger.getHandlerFunction() == functionName) {
      ScriptApp.deleteTrigger(trigger);
    }
  }

  // 1分後に実行
  let tarTime = 1
  ScriptApp.newTrigger(functionName).timeBased().after(tarTime * 60 * 1000).create();

}

// トリガーを削除
function deleteTrigger(functionName) {

  let triggers = ScriptApp.getProjectTriggers();
  for (let trigger of triggers) {
    if (trigger.getHandlerFunction() == functionName) {
      ScriptApp.deleteTrigger(trigger);
    }
  }
}</pre>
</div>
<h2>自作共通関数のご紹介</h2>
<h3>自作共通関数のメリット</h3>
<p>スプレッドシートの操作をする際に、API呼び出しの回数を減らしてコードの実行をスムーズにするため、スプレッドシートの内容を配列やオブジェクト型に格納して加工し、加工した配列・オブジェクト型変数をスプレッドシートに戻す操作を良く行います。</p>
<p>このため、スプレッドシート⇔配列やオブジェクト型変数を行き来するための関数を複数使用していて、上記コードでは以下の自作共通関数を使用しています。<br />
名前の付け方は「f_○○_」の形式に統一していて、○○の部分は対象となるものを記入します。（「arr：配列、obj：オブジェクト、sh：シート」など）</p>
<h3>f_arr_colNameToIndex(tarArr, tarColName)</h3>
<p>二次元配列のカラム名からインデックス番号を取得する関数です。<br />
特定の配列を参照する際に使用します。</p>
<h3>f_arr_colNameToColArr(tarArr, tarColIndex, tarColumnsCount)</h3>
<p>二次元配列の特定の二次元配列を抜き出して新たな二次元配列を作成する関数です。<br />
1列でも複数列でも二次元配列を作成することもできます。</p>
<h3>f_arr_arrToSh(tarArr, tarSheet, tarRow, tarColumn)</h3>
<p>二次元配列をシートに格納します。<br />
格納したシートを再度参照しやすいように二次元配列を返す形にしています。</p>
<h3>f_drive_getFileObjInChildFolder(tarChildFolderName)</h3>
<p>ドライブの中で子フォルダのフォルダ名から中に含まれているファイルの情報をオブジェクト型（Key：ファイル名　Item：ファイルID）で返します。</p>
<h3>f_drive_getChildFolderId(tarChildFolderName)</h3>
<p>ドライブの中で子フォルダ名からフォルダ名IDを返す関数です。<br />
f_drive_getFileObjInChildFolderの関数で使用します。</p>
<h3>f_sh_shToArr(tarSheet, columnsCount)</h3>
<p>シートのA列から指定した分の列数を対象として二次元配列に作成して取得します。</p>
<h3>f_sh_getTarLastRow(sheet)</h3>
<p>スプレッドシートの最終行を取得する関数です。<br />
フォームなどを使用していた場合最終行を取得するのがうまくいかない場合があるので空白行が出てくるまでの数をカウントします。</p>
<h3>f_sh_getTarLastColumn(sheet)</h3>
<p>スプレッドシートの最終列を取得する関数です。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/gas-createdrafthtmlmail2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Yahoo乗り換え検索を用いて所要時間確認（GASバージョン）</title>
		<link>https://officevba.info/norikaegas/</link>
					<comments>https://officevba.info/norikaegas/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Wed, 10 Jul 2024 12:20:54 +0000</pubDate>
				<category><![CDATA[Google Apps Scripts]]></category>
		<category><![CDATA[GAS]]></category>
		<category><![CDATA[Parser]]></category>
		<category><![CDATA[乗り換え検索]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2496</guid>

					<description><![CDATA[IEでのスクレイピングができなくなったので別の方法を色々と検討中 以前にVBAとIEを用いて乗り換え検索での所要時間を取得するツールを使っていましたが、IEが使えなくなったので代替手段を検討しておりまして、前回はこちらで [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>IEでのスクレイピングができなくなったので別の方法を色々と検討中</h2>
<p>以前にVBAとIEを用いて乗り換え検索での所要時間を取得するツールを使っていましたが、IEが使えなくなったので代替手段を検討しておりまして、前回は<a href="https://officevba.info/norikaepython/" target="_blank">こちら</a>でPythonのBeautifulSoupを使ったコードをご紹介しました。</p>
<p>Pythonは使える環境にある人は便利なのですが、プログラミングを知らない多くの人に共有する際には環境構築が面倒だったり、バージョン違いによる挙動の差を考慮しないといけなかったり制約が多いです。</p>
<p>そこでもっと手軽に共有できる方法を求めて、Googleアカウントさえあれば環境構築もなく無料で使用できるGASでコードを考えてみました。<br />
今回はYahoo乗り換え検索を用いて所要時間を確認するGASのコードと使い方をご紹介します。</p>
<h2>スプレッドシートについて</h2>
<h3>スプレッドシートの概要説明</h3>
<p>もともと大量の経路について所要時間を調べる目的のツールですので、スプレッドシートは1行ごとに調べたい経路を記載しておいて、<br />
その情報をGASで読み取って乗り換え検索ページに入力する運用を想定しています。</p>
<p>取得した結果については同じスプレッドシートの右側に記載欄を用意して入力されるようにしています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/07/vba172_1.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2024/07/vba172_1-700x73.jpg" alt="" width="700" height="73" /></a></p>
<h3>条件入力列（A～L列）</h3>
<p>検索用の条件入力は以下の通りです。</p>
<h4>①出発</h4>
<p>駅名もしくは住所などを記載します。大宮駅（埼玉・京都）など同じ駅名などがある場合は住所を記載した方が確実です。</p>
<h4>②到着</h4>
<p>出発と同じく駅名もしくは住所などを記載します。</p>
<h4>③出発・到着：時間などを記載する際の条件入力欄です。</h4>
<p>「出発」（出発時間で検索する）、「到着」（到着時間で検索する）、「始発」、「終電」、「指定なし」を選択します。</p>
<h4>④日付</h4>
<p>検索したい日付を入力します。「ダイヤ改正の影響」と「平日・休日」での違いも含んで結果を受け取るために正確に入力した方が精度が高くなります。</p>
<h4>⑤時間（空欄なら9時）</h4>
<p>検索条件の出発時間・到着時間を入力する欄です。<br />
もともと会社への到着時間を想定して作成したので、空白だった場合③の条件次第で朝9時（出発・到着）を検索します。</p>
<h4>⑥飛行機・⑦新幹線・⑧有料特急・⑨高速バス・⑩路線バス・⑪フェリー</h4>
<p>飛行機・新幹線などの移動手段を使用するかしないかを選択します。<br />
空欄だった場合使用しないことになります。（私は「〇」を入力して使用するフラグにしています。）</p>
<h4>⑫優先</h4>
<p>検索の際に優先する項目です。「到着が早い順」、「料金が安い順」、「乗り換え回数順」の中から選択します。</p>
<h3>結果入力（M～T列）</h3>
<p>結果については1ページ目に表示される3種類の所要時間と交通費、および検索で使用したURLを記載します。</p>
<h2>GASのコードの紹介</h2>
<p>GASのコードについては以下の通りです。</p>
<p>今回はネットで情報収集しやすかった「Parser」というライブラリ（ライブラリID：1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw）を使用します。</p>
<p>ライブラリの追加は<a rel="noopener" href="https://ex-ture.com/blog/2023/01/24/gas%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6web%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0%E3%82%92%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86/" target="_blank">こちらのページ</a>を参考にさせていただきました。</p>
<p>タグの取得手順はIEとVBAの操作やSeleniumなどよりはPythonで紹介したBeautifulSoupに近い印象でした。<br />
（文字列で取得する方法は慣れていなくて理解するのが難しかったです。）</p>
<div class="GASCode">
<pre>function getRoute() {

  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let tarSheet = ss.getSheets()[0];

  let ssArr = func_shToArr(tarSheet);

  for (i = 1; i < ssArr.length; i++) {
    let tarRowArr = [ssArr[i]]; //1行分を対象として抜き出して、行数1の二次元配列に格納
    let tarUrl = func_getUrl(tarRowArr);

    let response = UrlFetchApp.fetch(tarUrl);
    let content = response.getContentText("utf-8");

    let body = Parser.data(content).from('<body').to('</body>').build()
    // console.log(body);

    let boxInfo = Parser.data(body).from('<li class="time">').to('</li').build()
    tarRowArr[0][12] = boxInfo.replace(/<\/span>/g, "").replace(/<span.*>/g, "").replace(/<.*>/g, " "); // 条件入力

    let navi = Parser.data(content).from('<div class="navPriority">').to('</div').build()
    let pTimeArr = Parser.data(navi).from('<li class="time">').to('</li>').iterate()
    let pFareArr = Parser.data(navi).from('<li class="fare">').to('</li>').iterate()

    for (x = 0; x < 3; x++) {
      // 時間・余計なタグ情報を削除
      pTimeArr[x] = pTimeArr[x].replace(/<\/span>/g, "").replace('<span class="mark">', "").replace('<span class="small">', " ").replace(/<!-- -->/g, " ");
      tarRowArr[0][13 + 2 * x] = pTimeArr[x];

      // 料金・余計なタグ情報を削除
      pFareArr[x] = pFareArr[x].replace(/<\/span>/g, "").replace('<span class="mark">', "").replace(/<.*>/g, " ");
      tarRowArr[0][14 + 2 * x] = pFareArr[x];

    }

    tarRowArr[0][19] = tarUrl;
    arrToSh(tarRowArr, tarSheet, i + 1, 1); // 行ごとに入力（エラーがあったときに途中まではきちんと入力されているのを確認できるように。）

  }

}

// スプレッドシートの情報から検索条件を取得・GET送信なのでURLのパラメータを設定して返す
function func_getUrl(tarRowArr) {

  出発 = tarRowArr[0][0];
  到着 = tarRowArr[0][1];

  if (tarRowArr[0][2] == "出発") {
    検索タイプ = "1";
  } else if (tarRowArr[0][2] == "到着") {
    検索タイプ = "4";
  } else if (tarRowArr[0][2] == "始発") {
    検索タイプ = "3";
  } else if (tarRowArr[0][2] == "終電") {
    検索タイプ = "2";
  } else if (tarRowArr[0][2] == "指定なし") {
    検索タイプ = "5";
  } else {
    検索タイプ = "5";
  }

  if (tarRowArr[0][3] != "") {
    日付 = tarRowArr[0][3];
  } else {
    let date = new Date();;
    日付 = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd');
  }

  if (tarRowArr[0][4] != "") {
    時間 = tarRowArr[0][4];
  } else {
    時間 = "09:00";
  }

  if (tarRowArr[0][5] != "") {
    飛行機 = "1";
  } else {
    飛行機 = "0";
  }

  if (tarRowArr[0][6] != "") {
    新幹線 = "1";
  } else {
    新幹線 = "0";
  }

  if (tarRowArr[0][7] != "") {
    有料特急 = "1";
  } else {
    有料特急 = "0";
  }

  if (tarRowArr[0][9] != "") {
    高速バス = "1";
  } else {
    高速バス = "0";
  }

  if (tarRowArr[0][9] != "") {
    路線バス = "1";
  } else {
    路線バス = "0";
  }

  if (tarRowArr[0][10] != "") {
    フェリー = "1";
  } else {
    フェリー = "0";
  }

  if (tarRowArr[0][11] == "到着が早い順") {
    検索結果の表示順 = "0";
  } else if (tarRowArr[0][11] == "料金が安い順") {
    検索結果の表示順 = "1";
  } else if (tarRowArr[0][11] == "乗り換え回数順") {
    検索結果の表示順 = "2";
  } else {
    検索結果の表示順 = "0";
  }

  let tarUrl = "https://transit.yahoo.co.jp/search/result?flatlon=&#038;" +
    "from=" + encodeURI(出発) +
    "&#038;tlatlon=" +
    "&#038;to=" + encodeURI(到着) +
    "&#038;via=&#038;via=&#038;via=" +
    "&#038;y=" + 日付.substring(0, 4) + //Utilities.formatDate(日付,"yyyy")
    "&#038;m=" + 日付.substring(5, 7) + //Utilities.formatDate(日付,"MM")
    "&#038;d=" + 日付.substring(8, 10) +
    "&#038;hh=" + 時間.substring(0, 2) +
    "&#038;m2=" + 時間.substring(4, 5) +
    "&#038;m1=" + 時間.substring(3, 4) +
    "&#038;type=" + 検索タイプ +
    "&#038;ticket=" + "ic" +
    "&#038;al=" + 飛行機 +
    "&#038;shin=" + 新幹線 +
    "&#038;ex=" + 有料特急 +
    "&#038;hb=" + 高速バス +
    "&#038;lb=" + 路線バス +
    "&#038;sr=" + フェリー +
    "&#038;s=" + 検索結果の表示順 +
    "&#038;expkind=" + "1" + "&#038;ws=" + "3"

  return tarUrl;
}

//最終行を取得する関数
function func_getTarLastRow(sheet) {

  //最終行の取得フロー開始
  let tarValues = sheet.getRange('A:Z').getValues().filter(String);  //A-Z列の値を全て取得
  let tarValuesFilterData = [];

  //配列の中で空欄のものを除外して、lengthを算出・最終行取得
  for (let i = 0; i < tarValues.length; i++) {
    if (tarValues[i].join("") != "") { //空文字列で行内のセルの値を結合
      tarValuesFilterData.push(tarValues[i]);
    }
  }
  return tarValuesFilterData.length;               //空白の要素を除いた長さを取得

}

//最終列を取得する関数
function func_getTarLastColumn(sheet) {

  // 最初の10行が空欄以外の数をカウント
  let tarArr = sheet.getRange("1:10").getValues().filter(String);
  let tarArrFilterData = [];

  //配列の中で空欄のものを除外して、lengthを算出・最終行取得
  for (let k = 0; k < tarArr[0].length; k++) {
    let checkStr = "";
    for (let i = 0; i < tarArr.length; i++) {
      checkStr = checkStr + tarArr[i][k]; 
    }
    if (checkStr !=""){
      tarArrFilterData.push(tarArr[0][k]);
    }
  }
  return tarArrFilterData.length;               //空白の要素を除いた長さを取得

}

// シートを配列に格納する関数
function func_shToArr(tarSheet, columnsCount) {

  if (columnsCount == undefined){
    columnsCount = func_getTarLastColumn(tarSheet);
  }
  retArr = tarSheet.getRange(1, 1, func_getTarLastRow(tarSheet), columnsCount).getDisplayValues();
  return retArr;

}

// 配列をシートに格納する関数
function arrToSh(tarArr, tarSheet, tarRow, tarColumn) {
  tarSheet.getRange(tarRow, tarColumn, tarArr.length, tarArr[0].length).setValues(tarArr);
}</pre>
</div>
<h2>実行結果について</h2>
<p>上記のコードを実行すると以下画像のようにA～L列の内容に応じてM～T列に結果が表示されます。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/07/vba172_2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2024/07/vba172_2-700x72.jpg" alt="" width="700" height="72" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/norikaegas/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Yahoo乗り換え検索を用いて所要時間確認（Pythonバージョン）</title>
		<link>https://officevba.info/norikaepython/</link>
					<comments>https://officevba.info/norikaepython/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 22 Jan 2024 14:09:09 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[BeautifulSoup]]></category>
		<category><![CDATA[Selenium]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<category><![CDATA[乗り換え]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2488</guid>

					<description><![CDATA[IEが動かなくなってもスクレイピングをしたい 私はよくVBAを使ってIEを操作するスクレイピングを好んで使っていたのですが、IEのサポートが終了して使えない機能が出てきました。 （開いている画面を取得して操作できたりなど [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>IEが動かなくなってもスクレイピングをしたい</h2>
<p>私はよくVBAを使ってIEを操作するスクレイピングを好んで使っていたのですが、IEのサポートが終了して使えない機能が出てきました。<br />
（開いている画面を取得して操作できたりなどPythonのBeautifulSoup・SeleniumやGASではできないこともあったので便利だったのですが仕方ないですね…。）</p>
<p>以前に<a href="https://officevba.info/norikaevba2/" target="_blank">こちら</a>でご紹介した乗り換え検索・所要時間算出ツールもIEをベースに作っていて、画面遷移がないので使えなくはないみたいでしたがアラート表示が出て継続使用に不安が出てきましたので代替のツールを作ってみました。</p>
<p>（ひょっとすると他の乗り換え検索もですが、）Yahooの乗り換え検索は駅・バス停間の所要時間だけでなく、住所を入力すれば住所地からの徒歩の時間も算出してくれるので、おおよその所要時間を把握するのにとても便利です。</p>
<p>今回はPythonのBeautifulSoupを使用して出発地・到着地間の所要時間を算出するコードを書いてみました。</p>
<h2>モジュールのインストール</h2>
<p>今回はBeautifulSoupとopenpyxlを使用するので、あらかじめ以下のモジュールをインストールしておきます。</p>
<div class="PythonCode">
<pre>
rem コマンドプロンプト
pip install beautifulsoup4
pip install openpyxl</pre>
</div>
<h2>用意する条件読み込み用・出力用のExcelファイル</h2>
<p>検索条件を入力したり、取得した情報を書き出すためにExcelのファイルを使用します。<br />
書式は以下のようなものを準備していて、A～L列（背景色なし）に所要時間を調べたい条件を入力してPythonのコードを実行するとM～S列（背景色水色）の欄に調べた情報が出力されるものになります。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/01/vba171_1.png"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2024/01/vba171_1-700x131.png" alt="" width="700" height="131" class="alignnone size-large wp-image-2486" srcset="https://officevba.info/wp-content/uploads/2024/01/vba171_1-700x131.png 700w, https://officevba.info/wp-content/uploads/2024/01/vba171_1-300x56.png 300w, https://officevba.info/wp-content/uploads/2024/01/vba171_1-768x144.png 768w, https://officevba.info/wp-content/uploads/2024/01/vba171_1-1536x288.png 1536w, https://officevba.info/wp-content/uploads/2024/01/vba171_1-2048x384.png 2048w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h2>作成したPythonのコード</h2>
<div class="PythonCode">
<pre>
# Python
from bs4 import BeautifulSoup

import os
import datetime
from enum import IntEnum

import glob
import openpyxl
import urllib.parse

import requests

t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
print(now.strftime('%Y%m%d_%H%M%S'))

def getRoute():
    for path in glob.glob(os.getcwd() + r'\py乗り換え検索.xlsx'):
        lwb = openpyxl.load_workbook(filename=path)
        lsh = lwb.worksheets[0]#シート1を選択

        tarRow = 2
        while not lsh.cell(tarRow,1).value is None: #3スタートでセルの値がなくなるまで繰り返す
            print(getUrl(lsh, tarRow))
            response = requests.get(getUrl(lsh, tarRow))
            response.encoding = response.apparent_encoding
            bs = BeautifulSoup(response.text, 'html.parser')
            lsh.cell(tarRow,  int(col.条件入力)).value = bs.select("span.time")[0].text
            lsh.cell(tarRow,  int(col.時間1)).value = bs.select("li.time")[0].text
            lsh.cell(tarRow,  int(col.料金1)).value = bs.select("li.fare")[0].text
            lsh.cell(tarRow,  int(col.時間2)).value = bs.select("li.time")[1].text
            lsh.cell(tarRow,  int(col.料金2)).value = bs.select("li.fare")[1].text
            lsh.cell(tarRow,  int(col.時間3)).value = bs.select("li.time")[2].text
            lsh.cell(tarRow,  int(col.料金3)).value = bs.select("li.fare")[2].text

            tarRow += 1
        lwb.save(path)
        lwb.close

class col(IntEnum):

    出発 = 1
    到着 = 2
    出発到着条件 = 3
    日付 = 4
    時間 = 5
    飛行機 = 6
    新幹線 = 7
    有料特急 = 8
    高速バス = 9
    路線バス = 10
    フェリー = 11
    優先 = 12
    条件入力 = 13
    時間1 = 14
    料金1 = 15
    時間2 = 16
    料金2 = 17
    時間3 = 18
    料金3 = 19

def getUrl(lsh, tarRow):
    
    # return lsh.cell(tarRow,1).value
    出発 = lsh.cell(tarRow,  int(col.出発)).value
    到着 = lsh.cell(tarRow,  int(col.到着)).value

    if lsh.cell(tarRow,  int(col.出発到着条件)).value == "出発":
        検索タイプ = "1"
    elif lsh.cell(tarRow,  int(col.出発到着条件)).value == "到着":
        検索タイプ = "4"
    elif lsh.cell(tarRow,  int(col.出発到着条件)).value == "始発":
        検索タイプ = "3"
    elif lsh.cell(tarRow,  int(col.出発到着条件)).value ==  "終電":
        検索タイプ = "2"
    elif lsh.cell(tarRow,  int(col.出発到着条件)).value ==  "指定なし":
        検索タイプ = "5"
    else:
        検索タイプ = "5"

    if lsh.cell(tarRow,  int(col.日付)) != "" :
        日付 = lsh.cell(tarRow,  int(col.日付)).value
    else:
        日付 = now.strftime('%Y%m%d')
    
    if lsh.cell(tarRow,  int(col.時間)) != "" :
        時間 = lsh.cell(tarRow,  int(col.時間)).value
    else:
        時間 = "9:00"
   
    if lsh.cell(tarRow,  int(col.飛行機)) != "" :
        飛行機 = "1"
    else:
        飛行機 = "0"
   
    if lsh.cell(tarRow,  int(col.新幹線))!= "" :
        新幹線 = "1"
    else:
        新幹線 = "0"
    
    if lsh.cell(tarRow,  int(col.有料特急)) != "" :
        有料特急 = "1"
    else:
        有料特急 = "0"
   
    if lsh.cell(tarRow,  int(col.高速バス)) != "" :
        高速バス = "1"
    else:
        高速バス = "0"
    
    if lsh.cell(tarRow,  int(col.路線バス)) != "" :
        路線バス = "1"
    else:
        路線バス = "0"
   
    if lsh.cell(tarRow,  int(col.フェリー)) != "" :
        フェリー = "1"
    else:
        フェリー = "0"

    if lsh.cell(tarRow,  int(col.優先)) == "到着が早い順":
        検索結果の表示順 = "0"
    elif lsh.cell(tarRow,  int(col.優先)) == "料金が安い順":
        検索結果の表示順 = "1"
    elif lsh.cell(tarRow,  int(col.優先)) == "乗り換え回数順":
        検索結果の表示順 = "2"
    else:
        検索結果の表示順 = "0"

    tarUrl = ("https://transit.yahoo.co.jp/search/result?flatlon=&#038;" + 
    "from=" + urllib.parse.quote(出発) +
    "&#038;tlatlon=" +
    "&#038;to=" + urllib.parse.quote(到着) +
    "&#038;via=&#038;via=&#038;via=" +
    "&#038;y=" + 日付.strftime('%Y') +
    "&#038;m=" + 日付.strftime('%m') +
    "&#038;d=" + 日付.strftime('%d') +
    "&#038;hh=" + 時間.strftime('%H') +
    "&#038;m2=" + 時間.strftime('%M')[1:] +
    "&#038;m1=" + 時間.strftime('%M')[:1] +
    "&#038;type=" + 検索タイプ +
    "&#038;ticket=" + "ic" +
    "&#038;al=" + 飛行機 +
    "&#038;shin=" + 新幹線 +
    "&#038;ex=" + 有料特急 +
    "&#038;hb=" + 高速バス +
    "&#038;lb=" + 路線バス +
    "&#038;sr=" + フェリー +
    "&#038;s=" + 検索結果の表示順 +
    "&#038;expkind=" + "1" + "&#038;ws=" + "3")

    return tarUrl

def sendLineNotify(sendMessage):
    api = "https://notify-api.line.me/api/notify"
    token = "QDSErjbTdCeR5R0mfT7wpKD7HgNQa6MkVyfgLi4vazR"
    headers = {"Authorization" : "Bearer "+ token}

    message = sendMessage
    payload = {"message" :  message}
    post = requests.post(api, headers = headers, params=payload)

def sendGoogleChat(sendMessage):

    webhookUrl = "https://chat.googleapis.com/v1/spaces/AAAAtxHBaG8/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&#038;token=vwSGCEHFEuWS5ffnp3GXWDH8lPG19VKQQOij1feZyzo"
    requests.post(webhookUrl, json={'text': sendMessage})

getRoute()
</pre>
</div>
<p>こちらのプログラムを実行すると、同じフォルダに格納されているExcelの検索条件を元に、Yahoo乗り換え案内のページを開き、所要時間と交通費を抽出します。<br />
あまり多い件数を試していないので推測ですが1,000件くらいは問題なく出力できると思います。</p>
<p>Yahoo乗り換え案内で入力するパラメータはすべてURLに記載されるGET通信なので、パラメータの記載方法さえわかっていればSeleniumを使わずにBeautifulSoupだけで処理できることがわかりました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/norikaepython/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WindowsAPIを用いてウインドウの一覧表を作成するVBAコード（再帰処理）</title>
		<link>https://officevba.info/recursion-getwindow/</link>
					<comments>https://officevba.info/recursion-getwindow/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 03 Dec 2023 02:20:41 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[WindowsAPI]]></category>
		<category><![CDATA[再帰処理]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2479</guid>

					<description><![CDATA[ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい 以前こちらの記事で（https://officevba.info/window-list/）ウインドウの一覧を取得するコードをご紹介しまし [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい</h2>
<p>以前こちらの記事で（https://officevba.info/window-list/）ウインドウの一覧を取得するコードをご紹介しました。</p>
<p>このコードは階層構造になっている最上段のウインドウのみを取得するものだったのですが、ウインドウの内部をクリックしたり操作したりする際は子ウインドウや孫ウインドウなど下層にあるウインドウを順に取得しないといけないことも多く、一回で必要なすべてのウインドウの情報を取得することができませんでした。</p>
<p>子ハンドルや孫ハンドルを含めてウインドウハンドルを取得できない理由はウインドウの階層が一律でなく一般的なループ処理では対応できないことなのですが、以前フォルダとファイルを全て取得する処理で学んだ再帰処理を使うことで様々な階層のウインドウハンドルを全て取得できるようになりました。</p>
<p>今回は再帰処理を用いて開いているウインドウのすべてのハンドルを取得するコードをご紹介します。</p>
<h2>ウインドウハンドルとは？</h2>
<p>Windowsは開いたウインドウや、ウインドウに配置されているパーツなどそれぞれに一つずつにハンドルという数値型の値を割り当てて制御しているらしいです。</p>
<p>「ウインドウ」ハンドルと言ってもVBAでいうところのオブジェクトのような感じで、エクスプローラーの開いた画面のような見た目がウインドウのものから、テキストボックスやアイコン、ステータスバーなど色々なものに割り当てられていて、理論上はウインドウハンドルさえ取得できればWindowsAPIを用いて対象を自由に操作できるらしいです。（私自身が何でもできるわけではありませんのでらしいと記載しましたがかなり機能は豊富そうです。）</p>
<p>上記の通りWindowsAPIを用いて操作対象のウインドウを指定する際、このウインドウハンドルを指定することが大事なのですが、ウインドウハンドルはウインドウを開くたびにランダムに新しい数値が割り当てられるため、プログラムを実行する過程でウインドウハンドルを取得する手段がないとWindowsAPIでは操作ができないということになります。</p>
<h2>再帰処理とは？</h2>
<p>再帰処理はプロシージャの中に自分自身を呼び出すコードを記載することで、ループ処理時の階層が一定でなくても各階層に同じ処理を行うことができることです。</p>
<p>よく使われるのはあるフォルダの中に様々な階層のフォルダが格納されていてそれぞれにファイルが格納されている際に、各階層を順に探ってファイルとフォルダの一覧を書き出したり、ファイルをコピーしたりするのに使われます。</p>
<p>ちなみに上記のような処理をしたい場合、Pythonの標準モジュールのglobを使えば引数を一つ指定するだけで再帰処理を実行することが可能です。<br />
VBAではコードを書いて実装しないといけないですが書き方に慣れが必要なので、この辺りはPythonの方が新しい言語でより洗練されている気がします。</p>
<h2>再帰処理を用いたウインドウハンドルを取得するVBAコードについて</h2>
<p>ウインドウが階層構造になっていて、かつウインドウごとに一定の階層ではないというのが再帰で解決できるぴったりの環境で、以前ウインドウハンドルの一覧表を作成するコードを勉強していた時には再帰処理の書き方を知らなかったので実装できず、作成したコードはウインドウの最上位だけを取得するのみでした。</p>
<p>必要なウインドウハンドルを取得するため、必要な場合は一層下ったものを探る処理を何度も繰り返して取得していましたが、再帰処理を使えば一回で全部解決させることができますし、今回の以下のようなコードの書き方では階層の情報も把握できるのでかなり便利になります。</p>
<div class="VBACode">
<pre>
'ウインドウの中の次または前のウインドウハンドルを取得する
Declare Function GetNextWindow Lib "user32" _
    Alias "GetWindow" _
    (ByVal hWnd As Long, _
    ByVal wFlag As Long) As Long

'ウインドウのキャプションタイトルを取得する
Declare Function GetWindowText Lib "user32" _
    Alias "GetWindowTextA" _
    (ByVal hWnd As Long, ByVal lpString As String, _
    ByVal cch As Long) As Long

'ウインドウが可視かどうかを取得する
Declare Function IsWindowVisible Lib "user32" _
    (ByVal hWnd As Long) As Long

'ウインドウのクラス名を取得する
Declare Function GetClassName Lib "user32" _
    Alias "GetClassNameA" _
    (ByVal hWnd As Long, _
    ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long
    
'ウインドウハンドルの子を取得する
Declare Function FindWindowEx Lib "User32.dll" _
    Alias "FindWindowExA" ( _
    ByVal hWndParent As Long, _
    ByVal hwndChildAfter As Long, _
    ByVal lpszClass As String, _
    ByVal lpszWindow As String) As Long
 
'ウインドウハンドルを取得する
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2

Sub 再帰でWindow一覧のキャプションとクラスを取得する()
    
    With ActiveSheet
        .Cells(1, 1) = "ハンドル"
        .Cells(1, 2) = "クラス名"
        .Cells(1, 3) = "キャプション名"
    End With
    
    Dim k As Long: k = 2
    Dim hWnd As Long: hWnd = FindWindow(vbNullString, vbNullString) '引数をvbNullStringにすると1つめのウインドウを取得する

    Call ウインドウの取得recursion(hWnd, k)

End Sub

Sub ウインドウの取得recursion(ByRef hWnd As Long, ByRef k As Long)
    
    Dim strClassNameCh As String * 100
    Dim strCaptionCh As String * 80
    
    Do
        If IsWindowVisible(hWnd) Then
            
            GetWindowText hWnd, strCaptionCh, Len(strCaptionCh)
            GetClassName hWnd, strClassNameCh, Len(strClassNameCh)
            
            Dim i As Long: i = ActiveSheet.UsedRange.Rows.Count + 1
            
            With ActiveSheet
                .Cells(i, 1).Value = hWnd
                .Cells(i, k).Value = Left(strClassNameCh, InStr(strClassNameCh, vbNullChar) - 1)
                .Cells(i, k + 1).Value = Left(strCaptionCh, InStr(strCaptionCh, vbNullChar) - 1)
            End With
            
            DoEvents
            
            Dim hWndCh As Long: hWndCh = FindWindowEx(hWnd, 0, vbNullString, vbNullString)
            Call ウインドウの取得recursion(hWndCh, k + 2)
        
        End If
        
        hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)
        
    Loop Until hWnd = GetNextWindow(hWnd, GW_HWNDLAST)
            
End Sub


</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/recursion-getwindow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ExcelVBAで再帰処理を用いてフォルダ一覧を取得</title>
		<link>https://officevba.info/recursion-getfolder/</link>
					<comments>https://officevba.info/recursion-getfolder/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 06 Nov 2022 11:25:08 +0000</pubDate>
				<category><![CDATA[ファイル・フォルダ操作]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[再帰処理]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2469</guid>

					<description><![CDATA[VBAで再帰処理を実行 最近、ExcelVBAの再帰処理の方法を人から教えてもらったので、以前断念したフォルダ一覧の取得をVBAで試してみました。 使い方の基本さえわかっていればかなり便利に使用できますのでこちらでご紹介 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>VBAで再帰処理を実行</h2>
<p>最近、ExcelVBAの再帰処理の方法を人から教えてもらったので、以前断念したフォルダ一覧の取得をVBAで試してみました。<br />
使い方の基本さえわかっていればかなり便利に使用できますのでこちらでご紹介します。</p>
<h2>再帰処理とは</h2>
<p>再帰処理とはあるプログラムの中で自分自身を呼び出して繰り返し処理を行うことです。<br />
繰り返しの数があらかじめ定まっていない、ネストの階層がわからない場合など一般的な繰り返しが適用しにくい場合に有効な処理となります。</p>
<h2>再帰処理の実例</h2>
<p>再帰処理はいつでもどこでも使うものというより特定の状況で知っておくとめちゃくちゃ便利なものだと思います。<br />
今回は再帰処理を用いて「フォルダの一覧を取得」するコードをご紹介します。</p>
<div class="VBACode">
<pre>Sub フォルダとファイル一覧を取得()

    '参照設定「MicrosoftScriptingRuntime」を有効にしておく
    Dim fso As New FileSystemObject
    '参照設定「Windows Script Host Object Model」を有効にしておく
    Dim wsh As New WshShell
    
    Dim tarPath As String: tarPath = wsh.SpecialFolders("Desktop") &#038; "\マクロ"
    Dim tarFolder As Folder: Set tarFolder = fso.GetFolder(tarPath)
    Call recursion(tarFolder)
    
    Set fso = Nothing
    Set wsh = Nothing
    
End Sub

Sub recursion(ByRef f As Folder)

    Dim tarSubFolder As Folder
    
    For Each tarSubFolder In f.SubFolders
        Debug.Print tarSubFolder.Path
        Call recursion(tarSubFolder)
    Next

End Sub</pre>
</div>
<p>デスクトップにある「マクロ」ファイルの下部にあるサブフォルダを全階層にわたり取得してイミディエイトに出力するコードになります。</p>
<p>Subプロシージャの「recursion」（再帰）の中で、 ①サブフォルダを取得して、②そのフォルダオブジェクトを引数にして自分自身をCallで呼び出す作りになっています。 </p>
<p>Subプロシージャが自分自身を呼び出すことができることに自分では考えが至らず、教わったときに衝撃を受けました。</p>
<p>この処理は対象となるフォルダが多いと終了するまでに時間がかかるのでご注意ください。</p>
<p>普通にデスクトップのパスを文字列で指定してもいいのですが、Windowsのユーザー名に依存せずにデスクトップを指定するためにWSH(Windows Scripting Host)のSpecialFoldersを使用しています。</p>
<p>Debug.printでイミディエイトウィンドウに表示する代わりに、配列に入れたりワークシートに出力することでより高速に処理ができるようになります。</p>
<p>ちなみにPythonではglobというライブラリを使用すれば、以下の1行で再帰処理を実行して全フォルダを取得することができます。</p>
<div class="PythonCode">
<pre>print(glob.glob('C:/Users/<ユーザー名>/Desktop/マクロ/**', recursive=True))</pre>
</div>
<p>この辺りはまた別の記事でご紹介できればと思います。<br />
今回の目的だけならPythonを使った方が便利そうですが、VBAでも再帰処理ができるというのは大きな発見でした。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/recursion-getfolder/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pythonでスプレッドシート操作-③Pythonサンプルコード</title>
		<link>https://officevba.info/spreadsheetoperation/</link>
					<comments>https://officevba.info/spreadsheetoperation/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 18 Sep 2022 13:41:10 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2456</guid>

					<description><![CDATA[Pythonでスプレッドシートを操作するサンプルコード 前回までにGoogle Cloud Platform（GCP）のAPIを有効化し、認証情報を登録する手順についてご説明しました。 Pythonでスプレッドシート操作 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Pythonでスプレッドシートを操作するサンプルコード</h2>
<p>前回までにGoogle Cloud Platform（GCP）のAPIを有効化し、認証情報を登録する手順についてご説明しました。</p>
<p><a href="https://officevba.info/enablegoogleapiforpython/" target="_blank">Pythonでスプレッドシート操作-①APIの有効化</a><br />
<a href="https://officevba.info/certificationgoogleapiforpython/" target="_blank">Pythonでスプレッドシート操作-②認証情報の登録</a></p>
<p>今回は前回までで設定した認証情報を用いてスプレッドシートを編集するPythonのサンプルコードをご紹介します。</p>
<h2>2通りの認証方法でのPythonコード</h2>
<p>認証情報の登録するには「OAuth2.0クライアントID」と「サービスアカウント」の2通りがあります。</p>
<p>どちらの方法を使っているかによってPythonのコードも変わります。<br />
それぞれのコードについてご紹介します。</p>
<h2>OAuth2.0クライアントIDを使用してのGoogleスプレッドシート操作のPythonサンプルコード</h2>
<h3>事前に用意しておくもの</h3>
<p>事前に用意しておくのは「クライアントID」と「クライアントシークレット」の文字列です。<br />
今回はPythonコード内に直接記入します。</p>
<p>別ファイルを読み込むようにしても大丈夫だと思いますが、手軽さを求めてのこのフローなのでコード内に記入する手順としています。<br />
また、ライブラリ「pydata_google_auth」と「gspread」を事前にインストールしておく必要があります。</p>
<h3>OAuth2.0クライアントIDを使用してスプレッドシートを操作するPythonサンプルコード</h3>
<p>pythonのサンプルコードは以下の通りです。</p>
<p>初回の起動時は後で紹介するように認証のステップを手動で操作して認証を通す必要があります。</p>
<div class="PythonCode">
<pre>import pydata_google_auth, os
import gspread

CLIENT_ID = 'クライアントIDを入力'
CLIENT_SECRET = 'クライアントシークレットを入力'

scoped_credentials = pydata_google_auth.get_user_credentials(
  [
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/spreadsheets'
  ],
  client_id=CLIENT_ID,
  client_secret=CLIENT_SECRET
)

gc = gspread.authorize(scoped_credentials)

#共有設定したスプレッドシートキーを変数[SPREADSHEET_KEY]に格納する。
SPREADSHEET_KEY = '操作したいスプレッドシートIDを入力'

#共有設定したスプレッドシートの「テスト」シートを開く
workbook = gc.open_by_key(SPREADSHEET_KEY)
Sheet = workbook.worksheet('テスト')
Sheet.update_cell(1, 1, "〇")</pre>
</div>
<h3>OAuth2.0クライアントIDを使用した際の初回認証</h3>
<p>初回実行時、ブラウザで認証情報の確認と承認のステップが入り、許可をするとコードが実行されます。<br />
まずブラウザが起動するので、アカウントを選択します。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba168/vba168-1.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba168/vba168-1.jpg" style="max-height: 300px" alt="vba168-1スプレッドシート操作認証" /></a></p>
<p>続いて「このアプリはgoogleで確認されていません」という警告メッセージが表示されます。</p>
<p>こちらは「続行」をクリックします。<br />
「安全なページに戻る」をクリックしたらPythonのコードが動きません。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba168/vba168-2.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba168/vba168-2.jpg" style="max-height: 300px" alt="vba168-2スプレッドシート操作認証" /></a></p>
<p>続いて認証情報の登録の際に設定したスコープが表示されますので、両方にチェックをつけて続行をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba168/vba168-3.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba168/vba168-3.jpg" style="max-height: 300px" alt="vba168-3スプレッドシート操作認証" /></a></p>
<p>認証情報が取得されたのでウインドウを閉じてください。と表記がでたら完了です。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba168/vba168-4.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba168/vba168-4.jpg" style="max-height: 300px" alt="vba168-4スプレッドシート操作認証" /></a></p>
<h2>サービスアカウントを使用してのGoogleスプレッドシート操作のPythonサンプルコード</h2>
<h3>事前に用意しておくもの</h3>
<p>サービスアカウントの認証には鍵ファイルが必要になります。<br />
鍵ファイルはPythonサンプルコードから取得しやすい場所に格納しておきます。</p>
<p>その他事前に「oauth2client」「gspread」をインストールしておく必要があります。</p>
<h3>サービスアカウントを使用したPythonサンプルコード</h3>
<p>こちらのコードはたぬさんの記事<a rel="noopener" href="https://tanuhack.com/operate-spreadsheet/" target="_blank">https://tanuhack.com/operate-spreadsheet/</a>を参考にさせていただきました。<br />
すごくわかりやすくまとめてくださっていて勉強させていただきました。</p>
<div class="PythonCode">
<pre>import gspread

#ServiceAccountCredentials：Googleの各サービスへアクセスできるservice変数を生成します。
from oauth2client.service_account import ServiceAccountCredentials 
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']

#認証情報設定
#ダウンロードしたjsonファイル名をクレデンシャル変数に設定
credentials = ServiceAccountCredentials.from_json_keyfile_name(“認証ファイルのパスを入力”, scope)

#OAuth2の資格情報を使用してGoogle APIにログイン
gc = gspread.authorize(credentials)

#共有設定したスプレッドシートキーを変数[SPREADSHEET_KEY]に格納
SPREADSHEET_KEY = 'スプレッドシートIDを入力'

#共有設定したスプレッドシートのシート1を開く
workbook = gc.open_by_key(SPREADSHEET_KEY)
Sheet = workbook.worksheet('テスト')
Sheet.update_cell(1, 1, "〇")</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/spreadsheetoperation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pythonでスプレッドシート操作-②認証情報の登録</title>
		<link>https://officevba.info/certificationgoogleapiforpython/</link>
					<comments>https://officevba.info/certificationgoogleapiforpython/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sat, 17 Sep 2022 15:20:57 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2416</guid>

					<description><![CDATA[Pythonでスプレッドシートを操作するための準備 Pythonでスプレッドシートを操作する場合、Google Cloud Platform（GCP）のAPIを有効化し、認証を通り抜ける必要があります。 前回はGoogl [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Pythonでスプレッドシートを操作するための準備</h2>
<p>Pythonでスプレッドシートを操作する場合、Google Cloud Platform（GCP）のAPIを有効化し、認証を通り抜ける必要があります。</p>
<p><a href="https://officevba.info/enablegoogleapiforpython/" target="_blank">前回</a>はGoogle Cloud Platform（GCP）のAPIを有効化するステップについて手順をご紹介しました。<br />
今回は認証情報の取得についての記事を書きます。</p>
<h2>Googleの認証を通す2通りの方法のご紹介</h2>
<h3>Googleの認証を通す2通りの方法の特徴と違いについて</h3>
<p>私が調べた限り、特定の人しかアクセスできない環境のスプレッドシートにアクセスするための認証を通す方法は以下の2通りあります。</p>
<p>それぞれ特長を記載します。（専門知識はないので誤っていたらすみません。）</p>
<h3>①OAuth2.0クライアントIDの取得</h3>
<p>認証を直接通すためのクライアントIDとクライアントシークレットという文字列を作成・取得し、この2つの要素を持って認証を通す方法になります。<br />
クライアントIDとクライアントシークレットだけ取得すればよいので後で紹介するサービスアカウントよりも手軽に使用可能です。</p>
<p>ただしセキュリティ的におすすめではないのか、2022年秋以降に作成したアプリケーションを公開する場合は使用できないようです。<br />
（テスト環境で数人が使うだけなら問題なさそうです。）</p>
<p>また、初回のアクセス時に手動で認証画面を操作する必要があります。</p>
<h3>②サービスアカウントでの認証</h3>
<p>プログラムに権限を割り当てるためのアカウントを作成し、そのアカウントで認証を通す方法です。</p>
<p>だれか別の人とスプレッドシートを共有するときと同じ操作で、対象のファイルごとに権限を付与することができ、また認証用のファイルを使用することからセキュリティとして強固なようです。</p>
<p>最初の設定が若干面倒くさいこと、GoogleWorkSpaceアカウントなどでは管理者の設定次第で共有できない運用になっていたりするところがネックです。</p>
<h2>①OAuth2.0クライアントIDの取得の手順</h2>
<p>まず「認証情報を作成」をクリックして、「OAuthクライアントID」を選択します。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-1_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-1_certification.jpg" style="max-height: 200px" alt="vba167_1-1_GCP認証フロー" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-2_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-2_certification.jpg" style="max-height: 200px" alt="vba167_1-2_GCP認証フロー" /></a></p>
<p>表示された画面で、アプリケーションの種類を選択します。<br />
今回はPythonでPCを用いてのアクセスに使用するため「デスクトップアプリ」を選択します。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-3_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-3_certification.jpg" style="max-height: 200px" alt="vba167_1-3_GCP認証フロー" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-4_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-4_certification.jpg" style="max-height: 200px" alt="vba167_1-4_GCP認証フロー" /></a></p>
<p>次の画面で名前を変更できますが、特にこだわりがなければそのままでもよい気がします。<br />
作成ボタンをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-5_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-5_certification.jpg" style="max-height: 200px" alt="vba167_1-5_GCP認証フロー" /></a></p>
<p>OAuthクライアントを作成しましたという画面が表示され、クライアントIDとクライアントシークレットが表示されているので、右端のアイコンをクリックして文字列をコピーしてメモ帳などに控えておきます。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-6_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-6_certification.jpg" style="max-height: 200px" alt="vba167_1-6GCP認証フロー" /></a></p>
<p>元の画面に戻るとOAuth2.0クライアントIDに作成したアプリケーションの名前が表示されます。<br />
以上でOAuthクライアントIDの作成が完了です。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_1-7_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_1-7_certification.jpg" style="max-height: 200px" alt="vba167_1-7GCP認証フロー" /></a></p>
<h2>②サービスアカウントの登録手順</h2>
<h3>サービスアカウントの登録は3ステップ</h3>
<p>サービスアカウントの登録は以下の3ステップで実施します。<br />
OAuthクライアントIDの登録と比べて少し手順が多いですが、慣れると5分もかからずに設定可能です。</p>
<h3>01.サービスアカウントの追加</h3>
<p>まずは認証情報を作成からサービスアカウントを選択します。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-1_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-1_certification.jpg" style="max-height: 200px" alt="vba167_2-1GCP認証フロー" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-2_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-2_certification.jpg" style="max-height: 200px" alt="vba167_2-2GCP認証フロー" /></a></p>
<p>サービスアカウントの詳細の中にサービスアカウント名・サービスアカウントID・サービスアカウントの説明を入力する欄が表示されます。</p>
<p>必須入力はサービスアカウントIDだけですので、サービスアカウントIDを入力します。</p>
<p>サービスアカウントのプロジェクトへのアクセスとか、ユーザーからのサービスアカウントへのアクセスとかはとりあえず使わないので省略して完了をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-3_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-3_certification.jpg" style="max-height: 400px" alt="vba167_2-3GCP認証フロー" /></a></p>
<p>元の画面に戻り、作成したサービスアカウントが表示されるようになります。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-4_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-4_certification.jpg" style="max-height: 200px" alt="vba167_2-4GCP認証フロー" /></a></p>
<h3>02.鍵の追加</h3>
<p>続いてこのサービスアカウントで使用する鍵（公開鍵・秘密鍵？）のペアを作成します。</p>
<p>私もよく理解していませんが、Google側とこちらで突合可能なものを作成するみたいです。<br />
リンクになっているサービスアカウントの名前の部分をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-4_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-4_certification.jpg" style="max-height: 400px" alt="vba167_2-4GCP認証フロー" /></a></p>
<p>サービスアカウントの詳細画面にて、上の方に記載されているキーをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-5_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-5_certification.jpg" style="max-height: 400px" alt="vba167_2-5GCP認証フロー" /></a></p>
<p>表示される画面で「鍵を追加」をクリックし、「新しい鍵を作成」をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-6_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-6_certification.jpg" style="max-height: 200px" alt="vba167_2-6GCP認証フロー" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-7_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-7_certification.jpg" style="max-height: 200px" alt="vba167_2-7GCP認証フロー" /></a></p>
<p>鍵のタイプを聞かれるのでJSONを選択し、作成をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-8_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-8_certification.jpg" style="max-height: 200px" alt="vba167_2-8GCP認証フロー" /></a></p>
<p>以下の表示が出て秘密鍵がダウンロードされますのでPythonからアクセスしやすい場所に保存します。<br />
（ファイル名は変更しない方が良さそうです。私が未熟なだけかもしれませんが、鍵が使えなくなりました。）</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_2-9_certification.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_2-9_certification.jpg" style="max-height: 200px" alt="vba167_2-9GCP認証フロー" /></a></p>
<h3>03.対象ファイルの共有設定</h3>
<p>上記のサービスアカウントと秘密鍵のファイルを使用して認証を通すのですが、スプレッドシートを操作するにはスプレッドシート自体のサービスアカウントへの共有設定も必要です。</p>
<p>手順は以下の通りです。<br />
対象のスプレッドシートを右クリックして共有を選択します。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_3-1_share.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_3-1_share.jpg" style="max-height: 400px" alt="vba167_3-1共有設定" /></a></p>
<p>ユーザーやグループを追加の欄にサービスアカウントのe-mailを入力します。<br />
ダウンロードしたjsonファイルの中に記載されているものをコピペで大丈夫です。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_3-2_share.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_3-2_share.jpg" style="max-height: 200px" alt="vba167_3-2共有設定" /></a></p>
<p>権限を「編集者」にして「共有」ボタンをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_3-3_share.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_3-3_share.jpg" style="max-height: 200px" alt="vba167_3-3共有設定" /></a></p>
<p>再度ファイルを右クリックして共有を選択し、サービスアカウントが登録されていればOKです。</p>
<p><a href="https://officevba.info/wp-content/uploads/vba167/vba167_3-4_share.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/vba167/vba167_3-4_share.jpg" style="max-height: 200px" alt="vba167_3-4共有設定" /></a></p>
<h2>Google側の認証設定はここまで</h2>
<p>以上で認証情報のGoogle側での設定は完了です。<br />
次はこの設定を使用して認証を通すPythonのコードをご紹介します。<br />
→<a href="https://officevba.info/spreadsheetoperation/" target="_blank">Pythonのサンプルコードの紹介ページ</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/certificationgoogleapiforpython/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pythonでスプレッドシート操作-①APIの有効化</title>
		<link>https://officevba.info/enablegoogleapiforpython/</link>
					<comments>https://officevba.info/enablegoogleapiforpython/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 11 Sep 2022 13:29:18 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2410</guid>

					<description><![CDATA[PythonでGoogleスプレッドシートを操作するための認証設定 GoogleスプレッドシートとExcelの連携にPythonを使う 最近私の仕事環境でGoogleのスプレッドシートを使うことが増えました。 Googl [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>PythonでGoogleスプレッドシートを操作するための認証設定</p>
<h2>GoogleスプレッドシートとExcelの連携にPythonを使う</h2>
<p>最近私の仕事環境でGoogleのスプレッドシートを使うことが増えました。<br />
GoogleフォームやGoogleサイトを作ってデータを集計・公開したり、GASで作ったスクリプトをAPIとして公開して利用することができたり、すごく便利なのですが、ローカルにデータをダウンロードしたり、ローカルのデータをアップロードするのに手間がかかる（手作業が必要になる）ことが課題でした。</p>
<p>業務の都合上、ExcelやAccessのVBAで加工処理が必要なものがまだまだ多くあり、Excel（ローカル）⇔スプレッドシート（Googleドライブ）のデータ連携をしたいと思うことが多くなりました。</p>
<p>ここ最近スクレイピングのやりやすさなどを求めてPythonを勉強していて、業務でもPythonが使用できる環境だったこともあり、Pythonを使ってローカルデータ（Excel）とGoogleドライブデータ（スプレッドシート）の連携するプログラムを考えてみました。</p>
<p>今回はその連携プログラムを作成した際に最も時間のかかったGoogleの認証を通す方法についてご紹介します。<br />
この記事ではGoogleの認証を通すためのGCPの設定（APIの有効化）についてご紹介します。</p>
<h2>Google Cloud Platform（GCP）のAPI有効化の手順について</h2>
<h3>API有効化の手順は3ステップ</h3>
<p>私も詳細よく把握していない部分はありますが、以下の3ステップでGoogle Cloud Platform（GCP）のAPIを有効化できます。</p>
<h3>①プロジェクトの作成</h3>
<p>まずはプロジェクトというものを作成します。<br />
認証を通す設定において、一番基礎になるもののようです。</p>
<p>とりあえず適当に名前を付けてプロジェクトを作成し、使用するAPIを選んだり、認証方法を選択できるようにします。</p>
<p>GCPのトップ画面（<a rel="noopener" href="https://console.cloud.google.com/home/" target="_blank">https://console.cloud.google.com/home/</a>）からプロジェクトの選択、新しいプロジェクトをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/2be1c9ff57a94f73695214337edb463b.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/2be1c9ff57a94f73695214337edb463b-700x260.jpg" alt="" width="700" height="260"/></a></p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/da117e0d1f97b6078c0a5421fb0ad197.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/da117e0d1f97b6078c0a5421fb0ad197-700x561.jpg" alt="" width="700" height="561" /></a></p>
<p>規約に同意するチェックを入れて同意して続行をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/772ca42b87af13b053de862669b9286b.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/772ca42b87af13b053de862669b9286b-700x748.jpg" alt="" width="700" height="748" class="alignnone size-large wp-image-2396" srcset="https://officevba.info/wp-content/uploads/2022/09/772ca42b87af13b053de862669b9286b-700x748.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/772ca42b87af13b053de862669b9286b-281x300.jpg 281w, https://officevba.info/wp-content/uploads/2022/09/772ca42b87af13b053de862669b9286b.jpg 732w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>プロジェクトの名前を入力して作成をクリックします。<br />
プロジェクトの名前はとりあえず適当でも大丈夫だと思います。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/3082379cf49f69b425e8c1662838b654.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/3082379cf49f69b425e8c1662838b654-700x521.jpg" alt="" width="700" height="521" class="alignnone size-large wp-image-2397" srcset="https://officevba.info/wp-content/uploads/2022/09/3082379cf49f69b425e8c1662838b654-700x521.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/3082379cf49f69b425e8c1662838b654-300x223.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/3082379cf49f69b425e8c1662838b654.jpg 722w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>以下画面が表示されたらOKです。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8.png"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8-700x259.png" alt="" width="700" height="259" class="alignnone size-large wp-image-2398" srcset="https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8-700x259.png 700w, https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8-300x111.png 300w, https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8-768x284.png 768w, https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8-1536x568.png 1536w, https://officevba.info/wp-content/uploads/2022/09/ac749081ece503f3b072f576e33b2eb8.png 1725w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3>②有効化するAPIの選択</h3>
<p>プロジェクトを作成したら、続いてプロジェクトに必要なAPIを有効化します。<br />
GCPのAPI設定画面（<a rel="noopener" href="https://console.cloud.google.com/apis/" target="_blank">https://console.cloud.google.com/apis/</a>）で必要なAPIを有効化します。</p>
<p>「+APIとサービスの有効化」をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095.png"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095-700x259.png" alt="" width="700" height="259" class="alignnone size-large wp-image-2399" srcset="https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095-700x259.png 700w, https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095-300x111.png 300w, https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095-768x284.png 768w, https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095-1536x568.png 1536w, https://officevba.info/wp-content/uploads/2022/09/5104d01f03740f45f22413cebe2b8095.png 1725w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>スプレッドシートを操作する場合、スプレッドシートAPIとGoogleドライブAPIを有効化します。<br />
順序は関係なく、該当のものを選択して有効化のボタンをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e-700x209.jpg" alt="" width="700" height="209" class="alignnone size-large wp-image-2400" srcset="https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e-700x209.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e-300x90.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e-768x229.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/38e73708cb3868f171dc09db8d9f2e9e.jpg 1350w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f-700x231.jpg" alt="" width="700" height="231" class="alignnone size-large wp-image-2401" srcset="https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f-700x231.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f-300x99.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f-768x254.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/9621bd96d5da6ef7f3b787e483976c8f.jpg 881w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928-700x247.jpg" alt="" width="700" height="247" class="alignnone size-large wp-image-2402" srcset="https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928-700x247.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928-300x106.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928-768x271.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/27eab837601d8da364057bc44e9ae928.jpg 970w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3>③OAuth同意画面の設定</h3>
<p>このプロジェクトで作成したアプリケーションからAPIを用いてアクセスするときの認証画面の設定を行います。</p>
<p>まずアプリ名と連絡先の登録を行います。<br />
自身で使うだけならそこまで深く考えなくても大丈夫そうです。</p>
<p>「保存して次へ」をクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640-700x1049.jpg" alt="" width="700" height="1049" class="alignnone size-large wp-image-2403" srcset="https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640-700x1049.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640-200x300.jpg 200w, https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640-768x1151.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/abe72cb9254945a3f77862cb6ec1b640.jpg 794w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>続いて使用許可するスコープを追加します。<br />
スコープを追加または削除ボタンをクリックするとAPIの一覧が表示されます。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-700x297.jpg" alt="" width="700" height="297" class="alignnone size-large wp-image-2404" srcset="https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-700x297.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-300x127.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-768x326.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-1536x652.jpg 1536w, https://officevba.info/wp-content/uploads/2022/09/9c68a9e05cba2651546a655fe531bb59-2048x869.jpg 2048w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>今回は表示されたスコープの中から先ほど有効化したスプレッドシートAPIとGoogleドライブAPIを選択します。<br />
名前の似たものが多く混乱しやすいですが、URLが「/auth/spreadsheets」「/auth/drive」のものにチェックを入れます。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/c629b1f95cb262bf287f9cbf9db74666.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/c629b1f95cb262bf287f9cbf9db74666.jpg" alt="" width="676" height="54" class="alignnone size-full wp-image-2405" srcset="https://officevba.info/wp-content/uploads/2022/09/c629b1f95cb262bf287f9cbf9db74666.jpg 676w, https://officevba.info/wp-content/uploads/2022/09/c629b1f95cb262bf287f9cbf9db74666-300x24.jpg 300w" sizes="(max-width: 676px) 100vw, 676px" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/d833658895a0719fa86ea70c449b7fe2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/d833658895a0719fa86ea70c449b7fe2.jpg" alt="" width="687" height="56" class="alignnone size-full wp-image-2406" srcset="https://officevba.info/wp-content/uploads/2022/09/d833658895a0719fa86ea70c449b7fe2.jpg 687w, https://officevba.info/wp-content/uploads/2022/09/d833658895a0719fa86ea70c449b7fe2-300x24.jpg 300w" sizes="(max-width: 687px) 100vw, 687px" /></a></p>
<p>更新をクリックすると気密性の高いスコープにAPIが追加されます。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/cd1f6c1ee6fe03d903477ed9e527dbac.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/cd1f6c1ee6fe03d903477ed9e527dbac.jpg" alt="" width="515" height="283" class="alignnone size-full wp-image-2407" srcset="https://officevba.info/wp-content/uploads/2022/09/cd1f6c1ee6fe03d903477ed9e527dbac.jpg 515w, https://officevba.info/wp-content/uploads/2022/09/cd1f6c1ee6fe03d903477ed9e527dbac-300x165.jpg 300w" sizes="(max-width: 515px) 100vw, 515px" /></a></p>
<p>続いてテストユーザーの登録です。<br />
私は自分しか使用しないのでこの部分は飛ばして保存して次へをクリックしました。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98-700x422.jpg" alt="" width="700" height="422" class="alignnone size-large wp-image-2408" srcset="https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98-700x422.jpg 700w, https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98-300x181.jpg 300w, https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98-768x463.jpg 768w, https://officevba.info/wp-content/uploads/2022/09/90621acbf8e077c7e645ceedd81b7b98.jpg 834w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>以下の画面が表示されて、ダッシュボードに戻るをクリックすればOKです。</p>
<p><a href="https://officevba.info/wp-content/uploads/2022/09/4da69defdaac7f8625d0abc197eb0b42.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2022/09/4da69defdaac7f8625d0abc197eb0b42.jpg" alt="" width="692" height="986" class="alignnone size-full wp-image-2409" srcset="https://officevba.info/wp-content/uploads/2022/09/4da69defdaac7f8625d0abc197eb0b42.jpg 692w, https://officevba.info/wp-content/uploads/2022/09/4da69defdaac7f8625d0abc197eb0b42-211x300.jpg 211w" sizes="(max-width: 692px) 100vw, 692px" /></a></p>
<p>続いて認証方法の選択とそれぞれの認証方法での設定の準備を行います。<br />
この部分は次の記事でご紹介します。</p>
<p><a href="https://officevba.info/certificationgoogleapiforpython/" target="_blank">Pythonでスプレッドシート操作-②認証情報の登録</a><br />
<a href="https://officevba.info/spreadsheetoperation/" target="_blank">Pythonでスプレッドシート操作-③Pythonサンプルコード</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/enablegoogleapiforpython/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
