本講義のテキストのコード Sub GetRequestSimple1() Dim url As String url = "http://www.exvba.com/ws/dombasic.html"
Dim xh As New WinHttp.WinHttpRequest xh.Open "GET", url, False xh.send
Dim sCode As String sCode = xh.Status If sCode <> 200 Then MsgBox "リクエストに失敗しました" & vbNewLine & sCode End If
'htmlをDOMとして取得する。そのための変数を宣言。 Dim oHTml As New MSHTML.HTMLDocument oHTml.body.innerHTML = xh.ResponseText 'htmlボディーをDOMとして取得
'所定のIDの要素を取得します Dim oH1 As MSHTML.HTMLHeadElement Set oH1 = oHTml.getElementById("h1_01") Debug.Print oH1.outerHTML Debug.Print oH1.innerHTML Debug.Print oH1.innerText
'HTMLBody全体を取得します Dim oH As MSHTML.HTMLBody Set oH = oHTml.getElementsByTagName("body")(0) Debug.Print oH.outerHTML Debug.Print oH.innerHTML Debug.Print oH.innerText
'H2タグのついた要素すべてを順番に調べます Dim oH2 As MSHTML.HTMLHeadElement For Each oH2 In oHTml.getElementsByTagName("h2") Debug.Print oH2.outerHTML Debug.Print oH2.innerHTML Debug.Print oH2.innerText Next End Sub
上記テキストコードの中で以下の部分を 'HTMLBody全体を取得します Dim oH As MSHTML.HTMLBody Set oH = oHTml.getElementsByTagName("body")(0) Debug.Print oH.outerHTML ↓ 変更後 'HTMLBody全体を取得します Dim oH As MSHTML.HTMLBody Set oH = oHTml.getElementsByTagName("body")(0) Debug.Print StrConv(oH.outerHTML, vbUnicode)
Sub GetRequestSimple1_revised()
Dim url As String
url = "http://www.exvba.com/ws/dombasic_shift_jis.html"
Dim xh As New WinHttp.WinHttpRequest 'HTTPリクエストを制御するクラスのインスタンスを生成
xh.Open "GET", url, False
xh.send
Dim sCode As String
sCode = xh.Status
If sCode <> 200 Then 'ステータスコードを調べる
MsgBox "リクエストに失敗しました" & vbNewLine & sCode
End If
Dim sHead As String
Dim sBody As String
sHead = xh.GetAllResponseHeaders
' sBody = xh.ResponseText
sBody = StrConv(xh.responseBody, vbUnicode) 'ここを変更した
Worksheets("response").Range("B1").Value = sHead
Worksheets("response").Range("B2").Value = sBody
Debug.Print xh.GetResponseHeader("Content-Length")
End Sub
Option Explicit
'概要:
'ADODB.Stream オブジェクトを使って文字化けを解消する
'必要なライブラリ:
'[1] Microsoft ActiveX Data Object x.x Library -> 文字コード変更のため(x.x の部分は一番大きいバージョンを選べばまず間違いなくOK)
'[2] Microsoft WinHTTP Services, version 5.1 -> HTTPリクエストをするため
'[3] Microsoft HTML Object Library -> コンテンツのDOMを解析するため
'以下の2つは、UTF8エンコードされたページ、Shift_JISエンコードされたページ
'後者は、このデモスクリプトの参照先として新たに作りました。ただし、ブラウザで見ると、文字化けするものと思います。
Const S_UTF8_URL As String = "http://www.exvba.com/ws/dombasic.html"
Const S_SHIFTJIS_URL As String = "http://www.exvba.com/ws/dombasic_shift_jis.html"
'まずは、以下の2つのプロシージャで感触を確かめてください。
Public Sub get_utf8_content()
'utf8エンコードされたコンテンツを取得し、エクセルシートに出力してみます
Dim url As String
Dim response_body() As Byte '型に注意! Byte型の配列です。
response_body = get_body(S_UTF8_URL)
'responseBody の文字コードを utf-8 とみなして処理し、最終的に出力します
change_and_message "A", response_body, "utf-8" '明示的に変換後の文字コードを指定する場合。"utf-8"は一例。候補たる文字列は、レジストリの HKEY_CLASSES_ROOT\MIME\Database\Charset を参照のこと。
End Sub
Public Sub get_shift_jis_content()
'shift_jisエンコードされたコンテンツを取得し、エクセルシートに出力してみます
Dim url As String
Dim response_body() As Byte '型に注意! Byte型の配列です。
response_body = get_body(S_SHIFTJIS_URL)
'responseBody の文字コードを 自動判別して処理し、最終的に出力します
change_and_message "B", response_body, "_autodetect" '文字コードを自動判別する場合
End Sub
Private Function get_body(url) As Byte() '型に注意! Byte型の配列です。
Dim xh As New WinHttp.WinHttpRequest
xh.Open "GET", url, False
xh.send
Dim sCode As String
sCode = xh.Status
If sCode <> 200 Then
MsgBox "リクエストに失敗しました" & vbNewLine & sCode
End If
get_body = xh.responseBody
End Function
Private Function change_and_message(data_column As String, response_body() As Byte, char_set As String) As String
Dim msg1 As String, msg2 As String 'msgboxでの途中経過出力用
msg1 = response_body 'Byte()を文字列に変換(msg1 as string だから)
MsgBox msg1
'Microsoft ActiveX Data Object x.x Library を使います
'すごく乱暴に書くと、以下の流れ:
'ADODB.Stream型のインスタンスについて
' Openする
' 入力するデータにかかる設定をする
' データを流し込む
' 出力するデータにかかる設定をする
' データを出力する
' Closeする
Dim ado_stream As New ADODB.Stream
ado_stream.Open 'Openする
ado_stream.Position = 0 'すごく乱暴に書くとおまじない
ado_stream.Type = 1 '受け取るデータの型を指定。 1:adTypeBinary(バイト型) 2:adTypetext(テキスト型)
ado_stream.Write response_body 'データを流し込む
ado_stream.Position = 0 'すごく乱暴に書くとおまじない。ときどき0でないことも。文字コードの話は深いのでこれ以上今は書けません。
ado_stream.Type = 2 '出力するデータの型を指定。 2:adTypetext(テキスト型)
ado_stream.charset = char_set '"_autodetect" とか "utf-8" とかを指定する部分。何が入るかはケースバイケース。
msg2 = ado_stream.ReadText 'データを出力する
ado_stream.Close 'Closeする
MsgBox msg2
Range(data_column & 1).Value = msg2
End Function
'ということで、本丸。shift_jisのウェブページを取得して解析してみる
Public Sub analyze_shift_jis_content()
Dim url As String
Dim response_body() As Byte '型に注意! Byte型の配列です。
Dim new_response_body As String
response_body = get_body(S_SHIFTJIS_URL)
new_response_body = change_encode(response_body, "_autodetect")
analyze_response_body new_response_body
End Sub
Private Function change_encode(response_body() As Byte, char_set As String) As String
Dim result_text As String
Dim ado_stream As New ADODB.Stream
ado_stream.Open
ado_stream.Position = 0
ado_stream.Type = adTypeBinary
ado_stream.Write response_body
ado_stream.Position = 0
ado_stream.Type = adTypeText
ado_stream.charset = char_set
result_text = ado_stream.ReadText
ado_stream.Close
change_encode = result_text
End Function
Private Sub analyze_response_body(new_response_body As String)
Dim oHTml As New MSHTML.HTMLDocument
oHTml.body.innerHTML = new_response_body
'所定のIDの要素を取得します
Dim oH1 As MSHTML.HTMLHeadElement
Set oH1 = oHTml.getElementById("h1_01")
Debug.Print oH1.outerHTML
Debug.Print oH1.innerHTML
Debug.Print oH1.innerText
'所定の要素の次の要素を取得します
Debug.Print oH1.NextSibling.outerHTML
Debug.Print oH1.NextSibling.innerHTML
Debug.Print oH1.NextSibling.innerText
'HTMLBody全体を取得します
Dim oH As MSHTML.HTMLBody
Set oH = oHTml.getElementsByTagName("body")(0)
Debug.Print oH.outerHTML
Debug.Print oH.innerHTML
Debug.Print oH.innerText
'HtmlBodyの子要素のうち4番目のものを取得します
Debug.Print oH.ChildNodes(3).outerHTML
Debug.Print oH.ChildNodes(3).innerHTML
Debug.Print oH.ChildNodes(3).innerText
'H2タグのついた要素すべてを順番に調べます
Dim oH2 As MSHTML.HTMLHeadElement
For Each oH2 In oHTml.getElementsByTagName("h2")
Debug.Print oH2.outerHTML
Debug.Print oH2.innerHTML
Debug.Print oH2.innerText
Next
End Sub
受講生さんの投稿
(投稿ID: 4694)
お世話になっております。
本日は少し長いですが、よろしくお願いいたします。
DOMとして取得したレスポンスボディの文字化けに関する質問です。
以下のコードは本講座のエクセルテキストの01_WinHttp_基本の中にあるm01010_getモジュールのGetRequestSimple1()サブプロシージャです。
Sub GetRequestSimple1()
Dim url As String
url = "http://www.exvba.com/ws/dombasic.html"
Dim xh As New WinHttp.WinHttpRequest 'HTTPリクエストを制御するクラスのインスタンスを生成
xh.Open "GET", url, False
xh.send
Dim sCode As String
sCode = xh.Status
If sCode <> 200 Then 'ステータスコードを調べる
MsgBox "リクエストに失敗しました" & vbNewLine & sCode
End If
'まずは、レスポンスヘッダー、レスポンスボディをざっくり取得してみよう
Dim sHead As String
Dim sBody As String
sHead = xh.GetAllResponseHeaders
sBody = xh.ResponseText
Worksheets("response").Range("B1").Value = sHead
Worksheets("response").Range("B2").Value = sBody
'レスポンスヘッダーの所定の項目を取得したいときは以下の要領
Debug.Print xh.GetResponseHeader("Content-Length")
End Sub
当然問題なく動いてレスポンスヘッダ、ボディともにちゃんと取れます。
しかし、対象のurlをShift_JISで記載されているページにしたら、とれてきたレスポンスボディの日本語部分が文字化けしていました。
そこで自分なりに調べて、
sBody = xh.ResponseText の部分を
→ sBody = StrConv(xh.ResponseBody, vbUnicode) に変更したら
文字化けが解消しました。
ここまでだったらよかったのですが、次に、本講義テキストの02_WinHttp_DOM解析ファイルのm02010_get_DOMモジュールの
GetRequestSimple1()プロシージャを使って、上記と同じようにurlを
Shift_Jisで記載されているページにしたら、取得したレスポンスはやはり文字化けします。
しかし、こちらは自力では解決できませんでした。上記で解決したコードも試しましたが、よけい変な文字になってしまいました。
たとえば以下のようにしてみました。
本講義のテキストのコード
Sub GetRequestSimple1()
Dim url As String
url = "http://www.exvba.com/ws/dombasic.html"
Dim xh As New WinHttp.WinHttpRequest
xh.Open "GET", url, False
xh.send
Dim sCode As String
sCode = xh.Status
If sCode <> 200 Then
MsgBox "リクエストに失敗しました" & vbNewLine & sCode
End If
'htmlをDOMとして取得する。そのための変数を宣言。
Dim oHTml As New MSHTML.HTMLDocument
oHTml.body.innerHTML = xh.ResponseText 'htmlボディーをDOMとして取得
'所定のIDの要素を取得します
Dim oH1 As MSHTML.HTMLHeadElement
Set oH1 = oHTml.getElementById("h1_01")
Debug.Print oH1.outerHTML
Debug.Print oH1.innerHTML
Debug.Print oH1.innerText
'所定の要素の次の要素を取得します
Debug.Print oH1.NextSibling.outerHTML
Debug.Print oH1.NextSibling.innerHTML
Debug.Print oH1.NextSibling.innerText
'HTMLBody全体を取得します
Dim oH As MSHTML.HTMLBody
Set oH = oHTml.getElementsByTagName("body")(0)
Debug.Print oH.outerHTML
Debug.Print oH.innerHTML
Debug.Print oH.innerText
'HtmlBodyの子要素のうち4番目のものを取得します
Debug.Print oH.ChildNodes(3).outerHTML
Debug.Print oH.ChildNodes(3).innerHTML
Debug.Print oH.ChildNodes(3).innerText
'H2タグのついた要素すべてを順番に調べます
Dim oH2 As MSHTML.HTMLHeadElement
For Each oH2 In oHTml.getElementsByTagName("h2")
Debug.Print oH2.outerHTML
Debug.Print oH2.innerHTML
Debug.Print oH2.innerText
Next
End Sub
上記テキストコードの中で以下の部分を
'HTMLBody全体を取得します
Dim oH As MSHTML.HTMLBody
Set oH = oHTml.getElementsByTagName("body")(0)
Debug.Print oH.outerHTML
↓
変更後
'HTMLBody全体を取得します
Dim oH As MSHTML.HTMLBody
Set oH = oHTml.getElementsByTagName("body")(0)
Debug.Print StrConv(oH.outerHTML, vbUnicode)
としましたが、余計、文字化けがひどくなってしまった例です。
根本的なところが分かっていないと思いますが、ご指導いただけますと幸いです。
よろしくお願いいたします。
小川 慶一さんのコメント
(コメントID: 6528)
がんばってますね。
対処法のひとつは、StrConv関数を実行する対象を変更することです。
以下を参考にしてください。
使いこなすために必要な知識が高度(発展編2レベル+外部連携レベル+さらにα)なので講座では紹介していませんが、ADODB.Stream のインスタンスを使って文字コードを変更して出力することもできます。
これだと、種々の文字コードに対して対応できます。
以下はサンプルです。
冗長だとかえって分かりにくいので、受講生さんの現状のスキルで全体を追えるかということは考えず、サンプルコードでは構造化にあまり遠慮をしていません。追いかけるのは大変かもしれませんが、まずは読み込んで動作確認してみて、としてみてください。
上で示したような小手先の方法では対処できないという場合は僕も ADODB.Stream を使います。
こういうコーディングは日常的にやるようなことではないので、細かいオプションの指定については僕も脳内には入れていません。僕が書く場合も、調べ物をしつつ、試行錯誤しつつ、(あるいは、過去に自分が書いたコードを参考にしつつ)という感じです。
> 小川さん
>
> お世話になっております。
> 本日は少し長いですが、よろしくお願いいたします。
> DOMとして取得したレスポンスボディの文字化けに関する質問です。
>
> 以下のコードは本講座のエクセルテキストの01_WinHttp_基本の中にあるm01010_getモジュールのGetRequestSimple1()サブプロシージャです。
受講生さんのコメント
(コメントID: 6530)
お世話になっております。
講義内容を超えるご回答に大変恐縮しておりますと同時に大変感謝しております。
> 受講生 さん:
> 'ADODB.Stream オブジェクトを使って文字化けを解消する
教えていただいたというか、わざわざ作ってくださったマクロで見事に
文字化けせず、DOMとしてHTMLボディを取得できました。理解するのはだいぶ先になりそうですが、とにかく実装して利用させていただきます。
> 対処法のひとつは、StrConv関数を実行する対象を変更することです。
こちらに関しては、自分なりにいろいろ試しましたが、解消しませんでしたが、もしこちらで解消するなら'ADODB.Stream オブジェクトを
利用するよりも自分では理解しやすいですし、コードも単純になりそうですので、マクロ全体の理解が深まったところで、もう一度チャレンジしたいと思います。今回は、いただいたマクロで解決しましたので、チャレンジはしばらく後になりそうですが。
最後に確認のための質問です。
今回DOMとしてレスポンスを取得(1)しようとして、文字化けの問題に遭遇しました。DOMを使わずに取得したHTMLコンテンツ(2)の場合は、文字化けの問題があったとしてもStrConv関数でなんとかなりました。ということは、DOMを使った場合(1)と、使わない場合(2)で、大きな違いがあるということでしょうか。とすれば、何が違うのか。ご指導いただけますと幸いです。
小川 慶一さんのコメント
(コメントID: 6533)
こんにちは。
> > 対処法のひとつは、StrConv関数を実行する対象を変更することです。
> こちらに関しては、自分なりにいろいろ試しましたが、解消しませんでしたが、
解消しませんでしたか...。
念のため書きますが、以下のとおり、 GetRequestSimple1_revised でのStrConvの対象は、responseBodyのほうです。それでもダメでしたでしょうか。(僕の環境ではうまくいきます)
「Web連携」の「基礎」の講座ですので。
お伝えできる内容には限界があります。
今回紹介したスクリプトのようなものを自力でなんとか作れるようになるには、前のコメントでも書きましたが、それなりに事前知識が必要です。
(あと、試行錯誤をいとわないで済むだけのPC操作スキルも)
> 今回DOMとしてレスポンスを取得(1)しようとして、文字化けの問題に遭遇しました。
「DOMとしてレスポンスを取得」はしていません。
以下の[1]→[2]の手順で仕事が進みます。そして、[2]より以前に、[1]の段階で文字化けしています。
[1] レスポンスを取得する
[2] レスポンスのDOMを解析する
各行で行っている作業に対して、日本語でご自身でコメントをつけてみてください。
全体の流れを頭の中で理解するのに良いかと思います。
あと、前回投稿の2つ目で示したモジュールのように、機能ごとに複数のプロシージャに分割してみるとかもよい学びになります。
受講生さんのコメント
(コメントID: 6539)
お世話になっております。
> 「DOMとしてレスポンスを取得」はしていません。
> 以下の[1]→[2]の手順で仕事が進みます。そして、[2]より以前に、[1]の段階で文字化けしています。
>
> [1] レスポンスを取得する
> [2] レスポンスのDOMを解析する
以上を意識して再度挑戦しまして、どうにか解消できました。
関連する箇所のコードを以下のようにしました。
' sBody = xh.ResponseText' ←とにかくこれだと文字化け
sBody = StrConv(xh.ResponseBody, vbUnicode) 'ここを変更した
Dim oHTml As New MSHTML.HTMLDocument
oHTml.body.innerHTML = sBody '←文字化け解消後のsBodyを左辺に格納
> 各行で行っている作業に対して、日本語でご自身でコメントをつけてみてください。
これも今後行っていこうと思います。
ありがとうございました。
小川 慶一さんのコメント
(コメントID: 6541)
無事解決したようで良かったです。
複数の手順や機能組み合わせになっているものについては、なるべくそれぞれ分離して取り扱える状態にするのが理解のコツです。
今回僕が示したプロシージャ分割の粒度も参考にしてください。
ひきつづき、よい学びを (^^
> 小川慶一さん:
>
> お世話になっております。
>
> > 「DOMとしてレスポンスを取得」はしていません。
> > 以下の[1]→[2]の手順で仕事が進みます。そして、[2]より以前に、[1]の段階で文字化けしています。
> >
> > [1] レスポンスを取得する
> > [2] レスポンスのDOMを解析する
>
> 以上を意識して再度挑戦しまして、どうにか解消できました。
> 関連する箇所のコードを以下のようにしました。
>
> ' sBody = xh.ResponseText' ←とにかくこれだと文字化け
> sBody = StrConv(xh.ResponseBody, vbUnicode) 'ここを変更した
>
> Dim oHTml As New MSHTML.HTMLDocument
> oHTml.body.innerHTML = sBody '←文字化け解消後のsBodyを左辺に格納
>
> > 各行で行っている作業に対して、日本語でご自身でコメントをつけてみてください。
>
> これも今後行っていこうと思います。
> ありがとうございました。