業務効率化

SeleniumVBAによるCopilot返答取得

SeleniumVBAによるCopilot返答を取得、シートに転記するするマクロです。以前Google翻訳をSeleniumで実施https://gakuhenn.com/seleniumvba-translate/しましたが、今回はCopilot返答を取得し、纏めや文末合わせ等の翻訳に限らない柔軟な戻り値を取得できます。

ブラウザでのCopilotURLを使用するため、Microsoftt365 Copilotプランに加入している必要があります。

ちなみにSeleniumVBAGoogle翻訳はMS公式のTranslate関数により沈められました。さらに、公式によりCopilot関数の正式導入が間もなくあるそうで、今回のマクロも公式に潰される未来が見えますが正式導入より早く発表させることで何とか今マクロの意義を作りたいです。What's New in Excel (June 2025) | Microsoft Community Hub

記事の内容

・挙動

・コード

・ポイント機能

挙動

①UF上での回答取得と②セル範囲による回答取得との2種類があります。

①UF上での回答取得

UF上で入力した質問に対して、Copilot返答がUF上に表示されます。質問はセル範囲選択でも入力可能です。

続けて送信を押すと、現在開いているインスタンスを再利用して同じチャット内のやり取りができます。質問テキストボックスには、直接テキスト入力だけでなく、選択したセル値を質問とすることができます。

②セル範囲による回答取得

選択したブックのセル範囲それぞれとプロンプトをインプットとしてCopilotに投げかけ、それぞれの返答を取得します。

返答した取得は選択したセル右列に転記されます。また、プロンプトの内容は右上に表示され、どんなプロンプトに対する返答か把握できます。

実行中は状況がステータスバーに表示されます。大体フローを5分割し、フローごとに配置した簡素な分割のため、完全な進捗状況は荒はしていませんが、実行中であることを確認できます。

コード

・SeleniumVBAのダウンロード:Releases · GCuser99/SeleniumVBA

・SeleniumVBAのダウンロードからの手順:[Excel VBA]WebDriver経由でブラウザ操作(SeleniumVBA使い方メモ) #ExcelVBA - Qiita

SeleniumVBAをダウンロードし、標準プロシージャとUFを作成し、下記2コードを貼り付けてください。

➤コードはこちら

Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" ( _
        ByVal hwnd As LongPtr) As Long
#Else
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function SetForegroundWindow Lib "user32" ( _
        ByVal hwnd As Long) As Long
#End If

Public RequestType As String '簡易取得返答か、複数セル質問か判断処理用
Private driver As SeleniumVBA.WebDriver, WebKey As New WebKeyboard, CpilotTargetBox As Object 'コパイロットに送るボックス用
Private statuscnt As Long, statuscntfori As Long
     


'Private
Function UFpCopilot(Optional Inputrng As Range, Optional InputQ As Variant) As String '2508 UF要subテスト
                                                                  
                                                                  
If RequestType = "簡易質問" Then
    
    If InputQ = "" Then
        
        MsgBox "Error:質問を入力してください"
        Exit Function
    
    End If
        
    

ElseIf RequestType = "セル質問" Then
    
    Dim transrng As Range, rng As Range
     On Error Resume Next
      If Inputrng Is Nothing Then
        'Set Inputrng = Application.InputBox("セルを選択してください", , , , , , , 8)
        'If Inputrng Is Nothing Then
            MsgBox "Error:セルを選択してください"
            Exit Function
     On Error GoTo 0
      End If

      
     On Error Resume Next
      If IsMissing(InputQ) Then
        'Set InputQorNo = Application.InputBox("質問を入力してください", , , , , , , 2)
        'If Inputrng Is Nothing Then
            MsgBox "Error:質問かテンプレ質問Noを入力してください"
            Exit Function
     On Error GoTo 0
      End If
      

End If
                                                                        
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''エラー処理

  

If CopilotUF.CopilotTryx = 1 Then

    Dim mngr As SeleniumVBA.WebDriverManager
    Dim keys As SeleniumVBA.WebKeyboard
    'Dim Prompts As String
    Dim rlt As String
    Dim timer As Long
    '英訳の場合true
    Dim flag As Boolean: flag = False

    Set driver = SeleniumVBA.New_WebDriver
    Set keys = SeleniumVBA.New_WebKeyboard
    Set mngr = SeleniumVBA.New_WebDriverManager
    
    Dim Fullprompts As String, Valsinnrng As String
 
   'WebDriverを自動更新するWebDriverManagerクラスを使用する   '0917なぜかエラーが起きたため削除すると機能
   'mngr.AlignEdgeDriverWithBrowser
    
    'Edgeを選択してブラウザを開く
    driver.StartEdge
   
    Dim caps As WebCapabilities, invisible As Boolean
    ' invisibleをなくしてもいいように見えるがなくすと画面で出てきてしまう
    driver.OpenBrowser caps, invisible = True
      
    'ブラウザCopilotを開く
    
    driver.NavigateTo ThisWorkbook.Worksheets("Info").Range("C3").Value '"https://m365.cloud.microsoft/chat/?auth=2&home=1" '←ブラウザでのコパイロット画面のURLに置き換える
    
    '最小化するとUFへの返答取得表示ができないためブックをActivate
    'driver.ActiveWindow.Minimize
    
    BringExcelToFront
    
   
End If


    
    driver.Wait 500

    
    If RequestType = "簡易質問" Then
         
        If statuscnt = 0 Then
            
            statuscnt = 100 / 5
            statuscntfori = 100 / 5
        
        Else
            statuscnt = statuscnt + statuscntfori / 5
                      
        End If
        
        Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"

        
        Fullprompts = InputQ
        UFpCopilot = GetCopilotRes(Fullprompts)
        
    
    ElseIf RequestType = "セル質問" Then
    
        Dim arr As Variant, outArr As Variant
        Dim ws As Worksheet, i As Long
                
        Set ws = Inputrng.Parent
        'ws.Columns(Inputrng.Columns.Count).Columns   '.Insert Shift:=xlToRight
        ws.Columns(Inputrng.Columns(Inputrng.Columns.Count).Column + 1).Insert Shift:=xlToRight
              
        With ws.cells(Inputrng.row - 1, Inputrng.Columns(Inputrng.Columns.Count).Column + 1)
            
            .Value = "Copilot結果_指示:" & InputQ
            .WrapText = False
            
        End With
        
 
        Inputrng.Offset(1, -1).WrapText = True
        
            
            If Inputrng.Count = 1 Then
            
            
                If Replace(Inputrng.Value, " ", "", , , vbTextCompare) = "" Then
                
                    Inputrng.Offset(, 1) = "-"
                
                Else
                
                    If statuscnt = 0 Then
                        statuscnt = 100 / 5
                        statuscntfori = 100 / 5
                    Else
                        statuscnt = statuscnt + statuscntfori / 5
                        
                    End If
                    
                    Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"
                    
        
                    Valsinnrng = Replace(Inputrng.Value, vbLf, "")
                    Fullprompts = InputQ & "--" & Valsinnrng
                    Inputrng.Offset(, 1) = GetCopilotRes(Fullprompts)
                    
                End If
            Else
    
            
                arr = Inputrng.Value ' 一気に読み込み
                ReDim outArr(1 To UBound(arr, 1), 1 To 1)    
        
                For i = 1 To UBound(arr, 1)   'プロンプトには テンプレか質問 VBCRLF +rngを入れる。 VBCRLFは改行される それかーーでも良いかも
                    'VBCRLFなら簡易質問のフロー利用可能だが遅延するかも 多分記載なければそのままよいかも

                    If statuscnt = 0 Then
                  
                        statuscnt = ((i / UBound(arr, 1)) * 100) / 5
                        statuscntfori = ((i / UBound(arr, 1)) * 100) / 5
                    Else
                        statuscnt = statuscnt + statuscntfori / 5
                        
                    End If
                    
                    Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"

                   
                    
                    
                    
                    If Replace(arr(i, 1), " ", "", , , vbTextCompare) = "" Then
        
                        arr(i, 1) = "-"
                        
                        statuscnt = (statuscntfori / 5) * 4 + statuscnt
                        
                        Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"
        
                        
                    Else
        
                        Valsinnrng = Replace(arr(i, 1), vbLf, "")
                        
                        If i = 1 Then 'プロンプトは最初のみ指示で以降はその指示を基本にInputだけを送る
                        
                            Fullprompts = InputQ & "---" & Valsinnrng
                             
                        Else
                        
                            Fullprompts = "次---" & Valsinnrng
                        
                        End If
                                
                        outArr(i, 1) = GetCopilotRes(Fullprompts)
           
                    
                    End If
                    
           
                Next
                
                ws.cells(Inputrng.row, Inputrng.Columns(Inputrng.Columns.Count).Column + 1).Resize(UBound(outArr, 1), 1).Value = outArr

            End If
            
     
    End If
    
    Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"

    Application.StatusBar = False
    statuscnt = 0
    statuscntfori = 0
 
    
 End Function


Private Function GetCopilotRes(Prompts As Variant) As String

             
' ページが完全に読み込まれるまで待機
Do While driver.ExecuteScript("return document.readyState") <> "complete"
    DoEvents
    Application.Wait 500   ' 待機(必要に応じて調整)
Loop

'MsgBox "準備完了"
       'driver.FindElement(By.ID, "m365-chat-editor-target-element").SendKeys Prompts
       
       'クリップボードにコピーしコピーする方法。Webだと改行がエンターと認識されるため使用不可
       Dim texts As Variant
       
       'texts = UserForm1.clipboard.GetText()
       
       'texts = CreateObject("htmlfile").parentWindow.clipboardData.GetData("text")
       

       'driver.FindElementByXPath("//button[@aria-label='Send']").Click
       
        'driver.FindElement(By.ID, "m365-chat-editor-target-element").Click
        
        'If CpilotTargetBox Is Nothing Then
            Set CpilotTargetBox = driver.FindElement(By.ID, "m365-chat-editor-target-element")
        'End If
        

Dim txt As String, arr As Variant, i As Long
' 選択範囲をタブ区切りの文字列に変換

'If Not rngtoCopilot Is Nothing Then
'    If rngtoCopilot.cells.Count = 1 Then

'        txt = rngtoCopilot.Value
'    Else
'        arr = rngtoCopilot.Value
'        For i = 1 To UBound(arr, 1)
'            txt = txt & Join(WorksheetFunction.index(arr, i, 0), vbTab) & vbCrLf
'        Next i
'    End If
'End If
' 改行を入れる(複数行の場合)
'MsgBox txt

'UserForm1.clipboard.SetText txt
'UserForm1.clipboard.PutInClipboard

'target.SendKeys WebKey.CtrlKey + "v"


        Dim lines() As String
lines = Split(Prompts, vbCrLf)  ' 改行で分割

If UBound(lines) > 0 Then
    For i = LBound(lines) To UBound(lines)
        CpilotTargetBox.SendKeys lines(i)
        CpilotTargetBox.SendKeys WebKey.ShiftKey & WebKey.EnterKey ' 行末で Enter を送る
    Next
Else
    CpilotTargetBox.SendKeys lines(i)
End If




'Dim txt As String
'txt = Replace(texts, vbCrLf, "\n")  ' JS で改行として扱う

'driver.ExecuteScript "arguments[0].innerText = arguments[1];", target, txt
        'driver.FindElement(By.ID, "m365-chat-editor-target-element").SendKeys Replace(texts, vbLf, vbLf, , , vbTextCompare)
        
        
        
          '09下記で機能していたが、2回目の送信時に動かないこと多くなった
        '送が含まれていれば可能
       'driver.FindElement(By.XPath, "//button[contains(@aria-label, '送')]").Click
    
       driver.FindElementByXPath("//button[@type='submit' and contains(@class, 'fai-SendButton')]").Click
'       driver.FindElementByXPath("//button[@type='submit' and @aria-label='送信']").Click
'       driver.FindElementByXPath("//button[@aria-label='Send']").Click

statuscnt = statuscnt + statuscntfori / 5
Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"

Dim h6Element As Object
Dim respo As Object

''''''''''''''''''''''''''''''''''''''''''''08 AI返信中の判断処理 停止ボタンがあると待つ判定
Dim isResponding As Boolean
Dim stopButton As Object
  
On Error Resume Next

Set stopButton = driver.PageToHTMLDoc.QuerySelector(".fai-SendButton__stopBackground")
On Error GoTo 0

If Not stopButton Is Nothing Then
    isResponding = True
    'MsgBox "Copilotは返信中です(停止ボタンが表示されています)"
Else
    isResponding = False
    'MsgBox "Copilotの返信は完了しています(停止ボタンなし)"
End If

Do
    Set stopButton = driver.PageToHTMLDoc.QuerySelector(".fai-SendButton__stopBackground")
    DoEvents
    Application.Wait Now + TimeSerial(0, 0, 1)
    
    'MsgBox "返答待ち"
Loop While Not stopButton Is Nothing

''''''''''''''''''''''''
statuscnt = statuscnt + statuscntfori / 5

Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"



'全返答を文字として取得 HTMLではない
'Dim fullResponse As String
'fullResponse = driver.FindElement(By.CssSelector, "div.fai-CopilotMessage__content").GetText
'返答のテキストのみ取得
'GetCopilotRes = fullResponse

statuscnt = statuscnt + statuscntfori / 5

Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"


Dim responses As Object
Dim latestResponse As String
Dim replyIndex As Integer

' 返信を取得
Set responses = driver.FindElements(By.CssSelector, "div.fai-CopilotMessage__content")
' "CopilotMessage__content" を含むDiv
'Set responses = driver.FindElementsByXPath("//div[contains(@class, 'CopilotMessage__content')]")

' 最新の返信のインデックス(0ベース)
replyIndex = responses.Count

' 最新の返信を取得
latestResponse = responses.Item(replyIndex).GetText

statuscnt = statuscnt + statuscntfori / 5

Application.StatusBar = "               " & "★★実行中です・・・ " & statuscnt & "%" & " / 100%" & "★★"

GetCopilotRes = latestResponse


BringExcelToFront

  

End Function

Sub BringExcelToFront()
    Dim hwnd As LongPtr
    ' Excelのウィンドウタイトルを取得(現在のブック名)
    hwnd = FindWindow("XLMAIN", Application.caption)
    If hwnd <> 0 Then
        SetForegroundWindow hwnd
    End If
End Sub
➤UF用コード

Option Explicit

Public clipboard As Object, CopilotTryx As Long:
 
Private Sub ComboBox1_Change()

Dim temp As String
temp = "テンプレ"


        Dim ReqKeyNoi As Long, PromptWs As Worksheet, Prompts As String
        Dim Wants As String, Fullprompts As String
        Set PromptWs = ThisWorkbook.Worksheets("Info")
        
        On Error Resume Next
        ReqKeyNoi = CLng(Left(ComboBox1.Value, InStr(1, ComboBox1.Value, ":", vbTextCompare) - 1))
        
            ReqKeyNoi = WorksheetFunction.match(ReqKeyNoi, PromptWs.Range("B:B"), 0)
            If Err.Number <> 0 Then
                MsgBox "登録されていない番号です。"
                On Error GoTo 0
                
                Exit Sub
            End If
            
        On Error GoTo 0
        
        Wants = PromptWs.cells(ReqKeyNoi, 3).Value
        Prompts = PromptWs.cells(ReqKeyNoi, 4).Value
        Fullprompts = PromptWs.Range("C4")

        Dim TargetArr As Variant, InputArr As Variant
        TargetArr = Array("#Wants#", "#Prompts#")
        InputArr = Array(Wants, Prompts)
        Dim i As Long
    
        For i = LBound(TargetArr) To UBound(TargetArr)
    
            Fullprompts = Replace(Fullprompts, TargetArr(i), InputArr(i))
    
        Next


    TextBox2 = Fullprompts
    
    
End Sub

Private Sub CommandButton1_Click()

On Error Resume Next
Set rngtoCopilot = Application.InputBox("コパイロットに渡す対象セル範囲を入力", , , , , , , 8)
If rngtoCopilot Is Nothing Then Exit Sub

On Error GoTo 0

If Not rngtoCopilot Is Nothing Then
    'Dim fullAddress As String
    'fullAddress = "'[" & rngtoCopilot.Worksheet.Parent.Name & "]" & rngtoCopilot.Worksheet.Name & "'!" & rngtoCopilot.Address
     'Set rngtoCopilot = Application.Range(fullAddress)
     
    If OptionButton1 = True Then    '簡単質問
        If Not rngtoCopilot Is Nothing Then  '簡単質問でセル範囲選択時質問
            Dim rowRange As Range
            Dim cell As Range
            Dim allText As String

            For Each rowRange In rngtoCopilot.rows
               Dim rowText As String
               rowText = ""
   
                  For Each cell In rowRange.cells
                  
                      rowText = rowText & Replace(cell.text, vbLf, " ", , , vbTextCompare) & "," ' タブ区切り(必要に応じてカンマなどに変更可)
             
                  Next cell
    
                       allText = allText & Left(rowText, Len(rowText) - 1) & vbCrLf  ' 最後の区切り文字を削除して改行
                  Next rowRange
                  
                  If TextBox1 = "" Then
                    TextBox1.text = allText
                  Else
                    TextBox1.text = TextBox1.text & vbCrLf & allText
                  End If
                  
                    'clipboard.SetText allText
                    'clipboard.PutInClipboard
         Else  '簡単質問で選択セルが一つの場合
            
            If TextBox1 = "" Then
                TextBox1 = rngtoCopilot.text
            Else
                TextBox1.text = TextBox1.text & vbCrLf & TextBox1
            End If
            
         End If
        
    ElseIf OptionButton2 = True Then    'セル質問の場合
    
        '選択した範囲のブック名から情報を代入 'rngtoCopilot.Address(external:=True)
        TextBox1.text = "【Book】=" & rngtoCopilot.Worksheet.Parent.Name & "_" & "【Sheet】=" & rngtoCopilot.Worksheet.Name & "_ " & "【Cell】=" & rngtoCopilot.Address

    End If

End If



End Sub


Private Sub CommandButton2_Click()
    
    Dim CopilotRes As String
    CopilotTryx = CopilotTryx + 1
    
'rngtoCopilot.Copy

    If ●UFCopilot.RequestType = "簡易質問" Then
    
        CopilotRes = UFpCopilot(, TextBox1)
        '簡易質問の場合はUFに回答表示する
        If TextBox2.text = "" Then
            
            TextBox2 = CopilotRes
        
        Else
            
            TextBox2 = TextBox2 & vbCr & CopilotRes
        
        End If
    
    Else ' セル質問 セル質問の場合は選択したセル隣に返答を転記する
    
        If rngtoCopilot Is Nothing Then
            MsgBox "セル範囲を選択してください。"
            Exit Sub
        ElseIf TextBox2 = "" Or Len(TextBox2.Value) <= 1 Then
            If MsgBox("質問:プロンプトが空欄ですがよろしいですか?", vbYesNo) <> vbYes Then
                Exit Sub
            End If
        End If
    
        'Set rngtoCopilot = Range(TextBox1.text)
        Call UFpCopilot(rngtoCopilot, TextBox2)
        
        MsgBox "完了しました。"
       
    
    End If
    
End Sub

Private Sub CommandButton3_Click()
    If clipboard Is Nothing Then Set clipboard = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    
    clipboard.SetText TextBox2.Value
   clipboard.PutInClipboard
End Sub

Private Sub Label1_Click()

End Sub

Private Sub Label3_Click()

End Sub

Private Sub Label4_Click()

End Sub

Private Sub OptionButton1_Click()
    
    
    Label3.caption = "質問かセル範囲を入力"
    Label1.caption = "Copilot返答"
    
    Label4.Visible = False
    ComboBox1.Visible = False

    ●UFCopilot.RequestType = "簡易質問"
    TextBox2 = ""


End Sub

Private Sub OptionButton2_Click()

    Label3.caption = "セル範囲を選択"
    Label1.caption = "プロンプトを入力"
    
    Label4.Visible = True
    ComboBox1.Visible = True
    
    ●UFCopilot.RequestType = "セル質問"
    TextBox2 = ""

    

End Sub


Private Sub UserForm_Initialize()

    If clipboard Is Nothing Then Set clipboard = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")

Dim i As Long

For i = 1 To 50
    ComboBox1.AddItem i & ":" & ThisWorkbook.Worksheets("Info").Range("C" & 8 + i - 1).Value
Next


With TextBox2
    .MultiLine = True
    .ScrollBars = fmScrollBarsVertical
    .EnterKeyBehavior = True
    .WordWrap = True
    .maxLength = 0 ' 無制限(ただし内部制限あり)
End With

With TextBox1
    .MultiLine = True
    .ScrollBars = fmScrollBarsVertical
    .EnterKeyBehavior = True
    .WordWrap = True
    .maxLength = 0 ' 無制限(ただし内部制限あり)
    
End With

    ●UFCopilot.RequestType = "セル質問"
    
    Me.width = 350
    Me.height = 280
    


End Sub

Private Sub ComboBox1_Enter()
    ' リストがある場合、自動でドロップダウンを開く
    If Me.ComboBox1.ListCount > 0 Then
        Me.ComboBox1.DropDown
    End If
End Sub

ポイント機能

・UF上での回答取得とセル範囲による回答取得

・セル質問時の配列による返答高速化

UF上での回答取得とセル範囲による回答取得

UFではUF上での回答取得とセル範囲による回答取得を選択できます。前者はUFのテキストボックスに入れた値を質問し、返答がUFの返答ボックスに表示されます。後者は、シート上の選択した範囲を質問とし、さらにプロンプトを質問入力テキストボックスに入力した上で二つの文字列を質問し、返答を選択したセルの右に記載します。

UF上での回答取得=UF上で質問と回答取得する

セル範囲質問では、プロンプトが選択したセルの右上のセルに記載され、それぞれのセル右に返答が転記されます。これにより、返答がどのようなプロンプトかを把握することができます。

セル範囲による回答取得=選択したセル範囲の右に回答転記

テンプレはInfoシートに追加可能です。やりたい事概要と、プロンプト詳細列に記載された値がプロンプトに登録された、#Wants##Prompts#と置き換えられます。##のキーワードは変更しないでください。

セル質問時の配列による返答高速化

セル質問時の返答取得は、セル分の値と返答格納する配列を宣言し、最後に転記しています。

arrはプロンプトとなるセル値格納する配列で、outArrは返答を格納する配列です。arrに選択セル範囲の値Inputrng.valueを格納し、outArr腫れ一をReDimでarr配列文の二次元配列とします。列は一列なので一次元でもよいように思えますが、セル値に転記する際は二次元である必要があるため上記のような再定義をしています。

そして、セル範囲ごとのForループインデックスであるiを引数に、Copilot返答取得関数の戻り値を格納していきます。

セル質問時の返答は選択したセルに応じて返答を得るまでの処理が多くなります。エクセル上で値をセルにコピーする際は、単純にValueプロパティに代入すればよいのですが、これは代入の回数が増えるほど処理負荷が重く、よって処理に時間が掛かってしまいます。その為、一括ペーストすることで高速化しています。

-業務効率化