May 14, 2014

IEに表示されているPDFをVBScriptでダウンロードして保存する



webにあるpdfファイルがプラグインでブラウザウィンドウ内に直接表示されている場合に、VBScriptを使ってローカルに保存する方法を示します。

なお、ここではpdfファイルを例に説明していますが、別にどんなファイル形式であっても同じ考え方を適用できます。例えば、画像ファイル(png, tiff, jpgファイルなど)や音声ファイル(mp3)であってもほとんど同じコードで処理できます。


ダウンロードしたいファイルのURIは分かるか?

ファイルのURIが分かっている場合は比較的簡単に対処することができます。

例えば、ダウンロードしたいファイルのURIがDOMオブジェクトのsrc属性として書かれている場合、このURIに対してWinHTTPオブジェクトを使ってStreamを作成し、ADODB.Streamオブジェクトに書き込み、ファイルに保存します。
pdfDownloadFromURI

Sub pdfDownloadFromURI() 
    remoteFileName = "http://www.server.com/remote.pdf" 'ダウンロードしたいpdfのアドレス
    localFileName = "C:\local.pdf"

    Set http = CreateObject("WinHttp.WinHttpRequest.5.1") 

    http.Open "GET", remoteFileName, False 
    http.Send 

    '取ってきたストリームをファイルに書き込む 
    With CreateObject("ADODB.Stream") 
        .Type = 1 'BINARY 
        .Open 
        .Write http.ResponseBody 
        .SaveToFile localFileName, 2 ' SAVE CREATE OVERWRITE 
        .Close 
    End With 
End Sub

URIが分からない場合にどうするか

例えば、申込みフォームで送信ボタンを押すと、申込み結果を示す印刷用のpdfが返ってくるような場合、フォームのPOSTに対する応答がpdfファイルになっているので、上記の対応はできません。pdfのURIを特定することができないからです。

そこで、まず送信されるフォームをWinHTTPオブジェクトで取得し、解析して送信するクエリーを作ります。といっても、単にquery変数にnameとvalueを付け加えていっているだけです。
(POSTするクエリーは送信されるフォームに存在するフィールドのnameとvalueを=でつなげた形式です。実際にはURLエンコードされていますがここでは割愛します。要はname1=value1&name2=value2....がクエリーになります。)

WinHTTPを使ってqueryをtargetURIにPOSTすると、目的のpdfをResponseBodyに受け取ることができます。こうすると、URIが分かっている場合と同じ処理で、ADODB.Streamで受け取ってファイルに保存することができます。
pdfDownloadByPOSTRequest

Sub pdfDownloadByPOSTRequest() 
    localFileName = "C:\local.pdf"

    sourceURI = "http://www.server.com/applyForm.html" 
      '例えばこのページでボタンを押すとpdfが表示されるとする
    targetURI = "http://www.server.com/result"
      'sourceURIのページでボタンを押したときのフォームの送信先

    Set http = CreateObject("WinHttp.WinHttpRequest.5.1") 

    http.Open "GET", sourceURI, False 
    http.Send 

    '取ってきた内容をDOM操作できるようにHTMLオブジェクト化する
    Set HTML = CreateObject("HTMLfile") 
    HTML.Write (http.ResponseText) 

    'フォームの中身を連結してクエリーを作る 
    'ここでは簡易的にinputタグを全部連結しているが、
    '本当は必要なフォームの中だけ処理すればよいし、
    'この解析処理でtargetURIも調べるべき
    query = "" 
    For Each inputTag In HTML.getElementsByTagName("input")
        query = query & inputTag.Name & "=" & inputTag.Value & "&" 
    Next 

    'POSTしてResposeBodyとしてpdfファイルの入っているストリームを得る 
    http.Open "POST", targetURI, False 
    http.Send query 
         
    '取ってきたストリームをADODB.Streamに流し込みファイルに保存する
    With CreateObject("ADODB.Stream") 
        .Type = 1 'BINARY 
        .Open 
        .Write http.ResponseBody 
        .SaveToFile localFileName, 2 ' SAVE CREATE OVERWRITE 
        .Close 
    End With 
End Sub

余談:解答にたどり着くまでの苦労話

いずれの場合でもpdfを読み込んでから保存するんだ、と考えるのではなく、
pdfを直接保存するんだ、と考えれば自然に上記のようなコードに至るはずなのですが…

予備知識もなく行き当たりばったりで組むとこうなる、の見本です。
  1. InternetExplorer.Applicationオブジェクトにプラグインを使ってpdfを表示させると、
    DocumentオブジェクトはObject/AcroPDFとなっていることを発見!!
  2. オブジェクトブラウザでAcroPDFLib(Adobe Acrobat Browser Control Type Library 1.0)を開いて、Saveメソッドを探すが…無い…
    execCommandメソッドがあるが、使い方が分からない…なんてこった
  3. 色々検索して、あれやこれをみて絶望する…無理なのか…?
    (AcroPDFにはSaveメソッドはありません、そして、execCommandでもSaveできません。srcプロパティはありますが、フォームのtarget属性が設定されてしまうので、役に立たないです。)
  4. そもそもStreamが取れればADODB.Streamに書き込んでSaveToFileで保存できる、と気づく。
  5. あれぇ?初めから普通にPOSTしてResponseBodyを取ればいいだけか!!
  6. もう少し、調べると、こことかここでは上で書いたのと同じ方法で解決している。
  7. 先人に学ぶものだ…と思い知る
  8. 今に至る

No comments :

Post a Comment