それわVBA案件ですね

エクセルVBAネタを書いています

PDF文書の翻訳ツールを作る3 (DeepL翻訳サイトにテキストデータを投げる)

筆者地方ではスギ花粉はピークを過ぎたそうです。
んが、筆者はスギは軽くってヒノキの方がつらいのです。。
そう。本当の戦いはこれから始まるのです。


こんちくわ 壁|ω・)ノ

さて、前回の記事の続きです。
今回はいよいよ翻訳する文字列をDeepLサイトに投げる処理について紹介します。



でわいきます ̄▽ ̄)ノ



Contents



前回の記事では・・・

クリップボードから文字情報を引っ張り出しすFuncrionプロシージャについて書きました。コーディングのポイントは以下の通り。

  • クリップボードからのデータの出し入れにはMSfromsライブラリの DataObjectクラスのメンバーであるGetFromClipboardメソッドと GetText メソッドを利用する
  • その下準備として、Microsoft Forms 2.0 Object Library を有効にするための参照設定を行う
  • クリップボードの中身を空にするのにApplication.CutCopyMode=Falseが使える
  • クリップボードの中身が空かどうかの判定は Application.ClipboardFormats(1) の値を参照する



これで、Functionプロシージャ ClipBordContents()作ったのでした。




クリップボードから取得した文字列をDeepLサイトに投げる

いきなりですが、出来上がったコードです

Private Sub DeepL翻訳()
    'ClipBordContents関数で取り出したクリップボードの中身targetTextに格納する
    Dim targetText As String: targetText = ClipBordContents()

    Dim URL As String: URL = "https://www.deepl.com/ja/translator"
    Dim objIE As SHDocVw.InternetExplorer: Set objIE = New SHDocVw.InternetExplorer    
    objIE.Visible = True
    
    Call objIE.navigate(URL)
    Do While objIE.Busy = True Or objIE.readyState <> 4
        DoEvents
    Loop
    
    'Deeplサイトへの
    'クラス名は"lmt__textarea" または "lmt__textarea lmt__source_textarea" でもおk
    Dim objInpTxt As MSHTML.HTMLHeaderElement
    Set objInpTxt = objIE.document.getElementsByClassName("lmt__textarea lmt__source_textarea lmt__textarea_base_style")(0)
    objInpTxt.Value = targetText

    
    SendKeys "{ENTER}"

End Sub


短いコードですがそれぞれの内容について紹介しますね


IEを起動してサイトにアクセスする

VBAを使ったスクレイピングについてはいろんなところで紹介されていますのであまり詳しくは書きませんが、記事で足りない情報はGoogle先生にお伺いするとよいと思います。


まず最初に、VBAを使ったインターネットエクスプローラー(IE)の操作には、Microsoft Internet Controls(SHDocVw.DLL)ライブラリの InternetExplorerクラスメンバーを使います。また、後述するHTMLに対する操作ではMicrosoft HTML Object Library(MSHTML.DLL)ライブラリを利用しますので、参照設定する下準備をしておきます。

f:id:FukuCyndiP:20210313234433p:plain


コーディングは InternetExplorerクラスメンバーを憑依させたオブジェクト(インスタンス) objIE を生成 (New) した後に objIE.xxxx と記述してメンバーを呼び出しながら進めていきます(インスタンスの名前はなんでもおk)。

コード的には、以下の2つの構成がほぼお約束的な書き方になっています。

1.IEインスタンスを生成してIEウィンドウを表示

' InternetExplorerクラスから、インスタンスobjIEを生成(New)
' .Visibleメソッドで IEウィンドウを表示する
    Dim objIE As SHDocVw.InternetExplorer: Set objIE = New SHDocVw.InternetExplorer
    objIE.Visible = True


 2.目的のサイトにアクセスして、読み込み待ちをする

' .Navigateメソッド(引数URL:アクセスしたいサイトのURL)、目的のサイトにアクセスする
' .Busyまたは .ReadyStateプロパティで読み込み完了したか否かを判定して、読み込み未完の場合はOSに処理を渡す
    Call objIE.navigate(URL)
    Do While objIE.Busy = True Or objIE.readyState <> 4 'objIE.readyState < READYSTATE_COMPLETEでもおk
        DoEvents
    Loop


.Busyは 文字通りIEが忙しい状態かどうか。
.Readystate はサイトの読み込み状態を示します(Readystateプロパティの戻り値については以下の通り)

  

列挙体記述 定数 説明
READYSTATE_UNINITIALIZED 0 未完了状態
READYSTATE_LOADING 1 IEオブジェクトのロード中
READYSTATE_LOADED 2 IEオブジェクトのロード完了。ただし、操作不可能
READYSTATE_INTERACTIVE 3 IEオブジェクトの操作可能状態
READYSTATE_COMPLETE 4 IEオブジェクトの全データ読み込み完了



DoEventsについては以下のサイトが参考になります

news.mynavi.jp




DeepLの翻訳ウィンドウにテキストを入れる

今回のコードのメインエベント的な部分ではあるのですが、実わ理解不十分なところもあって、手探りでこうしたらなんかうまくいった的な感じなコードの紹介になってしまいます。

すまそん。。。



で、クリップボードから取り出した文字列情報はすでに変数に代入済み。ぢゃぁ、それをDeepLサイトの所定の位置に投入するためには、HTMLで記述されているDeepLサイトのどの要素をどういじればいいのか?と、いうのがHTML知識がほぼゼロな筆者には大問題なわけです。  


f:id:FukuCyndiP:20210313225932p:plain




でもEdgeの機能いじくり倒すことで、大体コレ的なものをつかむことができました。

恥ずかしげもなく大公開すると、


手順1.Edgeの開発者ツールを起動します

f:id:FukuCyndiP:20210313230021p:plain




手順2.開発者ツールをポチすると、なんかコードが右にずらーっと出現します。
思わず気が遠くなりそうになりますが、意識をつないでその上にあるマウスポインタの絵をクリックします

f:id:FukuCyndiP:20210313230106p:plain




すると、該当するコード部分がハイライトされます。

f:id:FukuCyndiP:20210313230129p:plain


この状況でもまだまだ気持ちを保つのにエネルギーが必要な状況ではありますが、落ち着いて示された先を見ると、

<textarea class="lmt__textarea lmt__source_textarea lmt__textarea_base_style" ...... lang></text area>


というHTML特有の機能単位を示す記述のパターン(<xxx>...</xxx>)が目に入って、この辺のclassをいじくればいいのかもしれない・・・ と、希望の糸が見えてきます。

と、いうわけでそんな根拠のない見通しとGoogle先生のご助言を基に試行錯誤して作成したのが以下のコードになります。


    Dim objInpTxt As MSHTML.HTMLHeaderElement
    'クラス名は"lmt__textarea" または "lmt__textarea lmt__source_textarea" でもおk
    Set objInpTxt = objIE.document.getElementsByClassName("lmt__textarea lmt__source_textarea lmt__textarea_base_style")(0)
    objInpTxt.Value = targetText 'targetText:翻訳したいテキストを格納


” lmt_textarea lmt_source_textarea lmt__textarea_base_style” クラスで指定されるオブジェクトをgetElementsByClassName メソッドで特定して、そこに翻訳したい文字列を代入するような記述になりました。

実行すると、DeepL画面の目的の場所に翻訳したい文字列が入ったので、これでヨシ!!

"(0)" がオブジェクトの要素ゼロ番を意味するのか、配列のインデックス番号なのか結局わからなかったけろヨシ!

識者の方のご意見お待ちしております<(_ _)>


Enterする

DeepLサイトは、翻訳したい文章をウィンドウにコピペすると自動で翻訳を開始します。つまり "翻訳ボタンがない" のですね。

試行錯誤しつつもめでたく所定の枠に目的の文書を導入することに成功した訳ですが、そのままだと何時までたっても翻訳が始まらず、ぽっつーんな感じになります。でも、追加で "Enter" すれば翻訳が始まるため、

  

  SendKeys "{ENTER}"  



ハイ。Enterキーをコード的に押すヤツですね。
これを最後に差し込むことで、ちゃんと翻訳を始めように仕向けることができました。

本当は自動翻訳開始のコマンドを探したかったのですが、ビジュアル頼みの解読では手がかりすらつかむことができず-ω-)



まとめ  

今回の記事ではクリップボードから取得した文字列情報を DeepLサイトの所定の位置に挿入して翻訳を開始させるコードを紹介しました。いくつかの参照設定とそのメンバーを使うことで、IEを起動 → サイトを開いて → 好きな操作をする というこがVBAでちゃんとできることを自分なりに確認することができました。

一方でDeepLサイト操作など、頼みのGoogle先生がほとんどご存じないとなると一気に苦戦するわけでして、、当たり前ではありますがHTMLについてのある程度の知識は必要ですね。 今回は#ノンプロ研 御大 タカーシさんの隣itのシリーズ記事をあれこれ参照しながら試行錯誤させていただきました。

tonari-it.com

時間はかかりましたが何とかなるものですね。

さて今回の記事で、英文PDFの文章をコピーしてそれをそのままDeepLサイトに自動で放り込んで翻訳してもらう本体コードはできました。

次回以降の記事ではもう一歩踏み込んで、DeepL翻訳を利用する上で気が付いた課題とそれに合わせたコードの追加や、使いやすさを追求した工夫について紹介したいと思います。


でわまた~  ̄▽ ̄)ノシ