質問: Nothing End Sub ```
回答: 検索クエリの入ったセルを選択(複数選択可)した状態でMainプロシージャを実行すれば、選択済みのすべてのセルの右隣に、クエリで検索して見つかった1つ目の画像を貼り付けます。 ``` 'Option Explicitは宣言されていない変数を認めないようにするための特殊な宣言です。 'これがないといきなり新しい変数を作ることができるようになるため、手軽な反面ミスタイプに気づきにくくなります。 Option Explicit 'ここが起点になります。 Private Sub Main() Dim cell As Range 'Selection とは選択範囲を表す特殊なオブジェクトです。 'このオブジェクトはセルを選択していればRangeオブジェクトになるし、 '画像を選択していればShapeオブジェクトになるなど、実行時まで型が判別できません。 'For Each ~ Nextは範囲を持ったオブジェクト(配列や連想配列など)をすべて舐めるための構文です。 'Excel VBAにおいてはRangeオブジェクトにも使えるため、このようにしています。 For Each cell In Selection Call GoogleSearch(cell) Next End Sub 'Google画像検索して貼り付けるまでの一連の流れを取りまとめるためのプロシージャです。 Private Sub GoogleSearch(ByRef cell As Range) Dim query As String 'セルに含まれる値を取り出し、文字列型にします。数字や時刻であっても文字列になります。 query = CStr(cell.Value2) Dim html As String '指定のURLにアクセスして、サーバから返ってくるHTMLをテキストで取得します。 html = FetchHtml("https://www.google.co.jp/search?tbm=isch&q=" & query) Dim nextUrl As String 'HTMLテキストを探索して、最初に見つかる画像URLを取り出します。 nextUrl = FindFirstUrlFromGoogleImageSearch(html) '上で得た画像URLにアクセスし、ファイルをダウンロードして一時フォルダに保存します。 DownloadFileToTempDir nextUrl '一時フォルダに保存された画像をシートに貼り付けます。 AddPicture cell End Sub '変数[url]にアクセスしてHTMLをテキストで返します。 Private Function FetchHtml(ByVal url As String) As String 'JavaScriptではXMLHttpRequestと呼ばれるオブジェクトです。 Dim xhr As Object Set xhr = CreateObject("MSXML2.XMLHTTP") 'GETリクエストを非同期で要求するよう接続をオープンします。 xhr.Open "GET", url, True '要求を送信します。 xhr.send 'Do (While|Until) ~ Loopは与えられた条件が成り立っている間、あるいは成り立つまで繰り返します。 Do Until xhr.readyState = 4 'DoEventsはウィンドウメッセージを処理させる命令です。 '待機中、画面が応答なしになるのを防ぐ、くらいに思っておけばいいでしょう。 DoEvents Loop '応答結果を返り値に設定します。 FetchHtml = xhr.responseText 'オブジェクト解放 Set xhr = Nothing End Function Private Sub DownloadFileToTempDir(ByVal url As String) 'ref: http://www.ka-net.org/blog/?p=4855 '定数宣言です。 '定数とは、変数と違って一度定義したら変えられない特殊な変数のようなものです。 Const adTypeBinary = 1 Const adSaveCreateOverWrite = 2 Dim xhr As Object Set xhr = CreateObject("MSXML2.XMLHTTP") xhr.Open "GET", url, True 'HTTPヘッダを設定します。詳しくはネットワーク系の基礎を勉強してください。 xhr.setRequestHeader "Pragma", "no-cache" xhr.setRequestHeader "Cache-Control", "no-cache" xhr.setRequestHeader "If-Modified-Since", "Thu, 01 Jun 1970 00:00:00 GMT" xhr.send Do Until xhr.readyState = 4 DoEvents Loop 'ADODB.Streamはデータストリームを汎用的に扱うためのAPI群を提供します。 With CreateObject("ADODB.Stream") '保存するものは画像なので、扱うデータはバイナリであることを設定しています。 'ファイルをメモ帳で開いて文字化けしていなかったら「テキスト」、それ以外はすべて「バイナリ」くらいの認識でいいです。 .Type = adTypeBinary 'ストリームをオープンします。 .Open 'ストリームにデータを書き込みます。中身はバイナリなので人間には読めません。 .Write xhr.responseBody 'ストリームの中身をファイルに出力します。 .SaveToFile Environ("TEMP") & "\vbatemp", adSaveCreateOverWrite 'ストリームを閉じます。 .Close End With 'オブジェクト解放 Set xhr = Nothing End Sub 'HTMLテキストから最初に見つかる画像URLを返します。 Private Function FindFirstUrlFromGoogleImageSearch(ByVal html As String) As String Dim partOfHtml As String 'Long型は32bit長のビットで表せる整数です。VB.NETでのLong(こちらは64bit)とは別物です。 Dim idx As Long 'HTMLソースの何文字目に "imgurl=" という文字列が含まれるのかを idx に格納します。 idx = InStr(html, "imgurl=") 'idx + 7 番目から後の文字列を抽出します。 partOfHtml = Mid(html, idx + 7) '抽出後の文字列の何番目に "&" という文字列が含まれるのかを idx に格納します。 idx = InStr(partOfHtml, "&") '最初から idx - 1 番目までを抽出して、返り値に設定します。 FindFirstUrlFromGoogleImageSearch = Left(partOfHtml, idx - 1) End Function '与えられたRange型(セル)の右隣に、あらかじめ保存された画像を貼り付けます。 Private Sub AddPicture(ByRef cell As Range) Dim shape As shape 'ここの詳しいパラメータの機能は私もよくは知りません。 Set shape = ActiveSheet.Shapes.AddPicture( _ Filename:=Environ("TEMP") & "\vbatemp", _ 'Excelでの画像貼り付けには複数の方法があり、 'ファイルへのリンクとするのかExcelファイル自体に画像を含ませるのかを選択できます。 'ここではExcelファイルに埋め込んでいます。 LinkToFile:=False, _ SaveWithDocument:=True, _ 'シートのA1の左上隅を頂点として、右にどのくらいずらすのかを指定します。 Left:=cell.Left + cell.width, _ 'シートのA1の左上隅を頂点として、下にどのくらいずらすのかを指定します。 Top:=cell.Top, _ '貼り付ける画像の縦幅、横幅を指定します。 'ここでは両方とも 0 に指定していますが、下でさらに別の設定をしています。 width:=0, _ height:=0) '貼り付けられた画像の縦幅横幅を、画像そのものの大きさに一致するようにします。 shape.ScaleHeight 1, msoTrue shape.ScaleWidth 1, msoTrue 'オブジェクト解放 Set shape = Nothing End Sub ```