LoginSignup

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

VBA_httpリクエストを利用してWebページのCSVデータをExcelシートに形式を変換して貼付

解決したいこと

VBAでWeb上のCSVデータを1時間ごとにExcelのシートにEXcelの形式で貼付を行いたいです。
httpリクエストを利用して、Web上のCSVデータを取得しようと考えています。
しかし、httpリクエストでCSVデータを取得した後、Excelのシートにrange("A1")で
貼付を行うと、1つのセルの中にCSVデータが入力されてしまいます。
そこで、VBAでメモ帳を開いて、そこに一度CSVデータを貼付し、その後、Split関数を
用いてExcelに再度コピペをしようと考えました。
手順としましては、「CSVのテキストファイルを新たにC:のフォルダに作成する」→「作成したファイルを開く」→「開いたファイルにhttpリクエストで取得したCSVテキストデータを張り付ける」→「再度張り付けたCSVテキストデータを配列にいれて、Excelのシートに書き出す」を考えています。

現状、2点に関して教えて頂きたいです。
①CSVファイルが新たに作成されない。
②そもそもhttpリクエストで取得したCSVデータはどのようにして扱うのか?どこかのフォルダにも保存されていないようですし、あらたに作成したCSVファイルに貼り付けをしようとしても、ファイルの場所が不明なのでCSVの文字列をコピーもできない。

宜しくお願い致します。

発生している問題・エラー

ファイルが見つかりません。

例)


または、問題・エラーが起きている画像をここにドラッグアンドドロップ

該当するソースコード

ソースコードを入力

例)

Sub test2()

    Dim httpReq As XMLHTTP60
    Dim i As Long, j As Long
    Dim strLine As String
    Dim arrLine As Variant
    Dim ws As Worksheet
    Dim t As String
    Dim fso As Object
    Dim tso As Object
    Dim strPath As String
    Dim tFile As FileSystemObject

    Call test3

    Set httpReq = New XMLHTTP60
    httpReq.Open "GET", "https://○○○reportFormat=CSV&○○○&maxIntradayDays=1&spanType=Intraday&startDateIntraday=2021%2F5%2F8&startHourIntraday=11&startMinuteIntraday=0&endDateIntraday=2021%2F5%2F8&endHourIntraday=12&endMinuteIntraday=0"
    httpReq.send

    Do While httpReq.readyState < 4
        DoEvents
    Loop

    t = httpReq.responseText


    Set fso = CreateObject("Scripting.FileSystemObject")

    With fso.GetFile("C:\Users\memo.txt").OpenAsTextStream(8).WriteLine("t")

                                                 .Close

    End With

    Set fso = Nothing

    Open ("C:\Users\memo.txt") For Input As #1


    i = 1
    Do Until EOF(1)

    Line Input #1, strLine
    arrLine = Split(strLine, ",")

    For j = 0 To UBound(arrLine)
    Worksheets("Paste01").Cells(i, j + 1).Value = arrLine(j)

    Next j
    i = i + 1
    Loop
    Close #1

Set tFso = Nothing
Set tFile = Nothing
Set httpReq = Nothing
End Sub

Sub test3()
   On Error Resume Next

    Dim fso             As New FileSystemObject     '// FileSystemObjectクラス
    Dim ts              As TextStream               '// TextStreamクラス
    Dim sFilePath                                   '// ファイルパス

    '// ファイルパスを指定
    sFilePath = "C:\Users\memo.txt"

    '// ファイルを作成
    Set ts = fso.CreateTextFile(Filename:=sFilePath, Overwrite:=True, Unicode:=False)

    '// ファイルを閉じる
    ts.Close

    '// エラー発生時
    If Err.Number <> 0 Then
        '// エラー内容を出力
        Debug.Print Err.Number & " " & Err.Description
    End If

End Sub


自分で試したこと

そもそもですが、httpリクエストを利用する前は、「ハイパーリンクでURLをクリックできるようにする」→「URLをクリックして開く」→「開いた時点で自動的にC:のユーザーフォルダにCSVデータがexcel形式で保存されるので、C:フォルダを開く」→「開いたフォルダのなかから、一番新しい日付のCSVファイルを選択して開く」→「コピペでExcelシートに貼付」の手順で試していました。しかし、URLを開くことで画面がExcelからWebページに切り替わってしまう点、CSVが保存されるフォルダがC:ユーザーの中にある為、別の人が利用するとフォルダが変わってしまう為ワイルドカードをフォルダパスに入れましたが、上フォルダが見つかりません。のエラーがでてCSVファイルを開くことが出来ない、という理由からこの方法を断念しました。

0

5Answer

①CSVファイルが新たに作成されない。

自分で試したことに書かれている手順だとURLを開いたタイミングでwebブラウザが(リクエストを実行)開き、webブラウザが処理できない形式のファイル(csv)を自動的にダウンロードしているのだと思います。

VBA(Excelマクロで使用されている言語)などのプログラム言語でリクエストを実行した場合は言語側で処理が可能なので新たにダウンロードされることはありません。

意図的にプログラムからIEのアプリケーションなどを使用してhttpリクエストを実行した場合はダウンロードする動きをするかもしれませんが...


②そもそもhttpリクエストで取得したCSVデータはどのようにして扱うのか?

後述のコードにてコメント書きました


Excelのシートにrange("A1")で貼付を行うと、1つのセルの中にCSVデータが入力されてしまいます。

Rangeは読んで字のごとくなのですが範囲選択して使用するメソッドです。
なのでRange("A1:C6")等の範囲を選択して使用します。
range("A1")とした場合はA1の範囲を選択しているためA1にしか値を入れることができません。

後述コードではCellsで書き出しを行っています。


ある種の代替コードになってしまいますが以下のような実装はどうでしょう?
[処理内容]
1.CSVのデータをhttpリクエストで取得してくる
2.取得したデータを分割しながらシートに出力する

Option Explicit '変数宣言の強制

Sub TEST()
    Dim ws As Worksheet '出力先のシート
    Set ws = Worksheets("Sheet1")

    'Microsoft XML, v6.0の参照設定が必要
    Dim httpReq As XMLHTTP60

    'データの取得
    Set httpReq = New XMLHTTP60
    httpReq.Open "GET", "http://XXXXXXX//YYYYYY.csv"
    httpReq.send

    'リクエストが終了するまで待機
    Do While httpReq.readyState < 4
        DoEvents
    Loop

    '②の回答
    'httpReq.responseTextでhttpリクエストで取得した内容が取り出せます。
    'htmlを取得した場合htmlのコードが表示されます

    '取得結果を改行コード単位に分割する
    Dim line
    line = Split(httpReq.responseText, vbCrLf)

    'ループ内で使う変数群
    Dim csList            '一行の内容をカンマで分割したものを格納する配列
    Dim indexA, indexB    'ループカウンタ
    Dim gyouIndex, retuIndex '行と列のインデックス
    'インデックス初期化
    gyouIndex = 1
    retuIndex = 1

    ' メモ:分割した配列の長さを取得する関数 -> UBound

    '各行を取り出しながら処理する
    For indexA = 0 To UBound(line)
        'ログ表示
        Debug.Print "処理対象の行    >>>" & line(indexA)

        '行の内容をカンマ単位に分割する
        csList = Split(line(indexA), ",")

        'カンマで分割したものを取り出しながら処理する
        For indexB = 0 To UBound(csList)
            'ログ表示
            Debug.Print "処理対象の要素  >>>" & csList(indexB)

            'ワークシートに出力する
            ws.Cells(gyouIndex, retuIndex).Value = csList(indexB)

            '出力の列数をカウントアップする
            retuIndex = retuIndex + 1
        Next indexB

        '出力の列数を初期化する
        retuIndex = 1
        '出力の行数をカウントアップする
        gyouIndex = gyouIndex + 1
    Next indexA


End Sub

0

@da-mac さん
ご丁寧にご説明頂きありがとうございます!

①、②について理解できました!
③についてですが、Rangeで範囲指定をしても、その範囲指定したセルの中一つ一つに全CSVデータが入っていました・・・

頂いたコードを試してみました!!
期待通り、セルの中にカンマでくぎられた一つの単語が入力されておりました。
感動です。

1点追加で質問なのですが、

For indexB = 0 To UBound(csList)

        Debug.Print csList(indexB)

        ws.Cells(gyouIndex, retuIndex).Value = csList(indexB)


        retuIndex = retuIndex + 1

Next indexB

のコードでループから抜け出す事が出来ませんでした。
その為、A1から行は変わらず、列のみが変化するという挙動になりました。

自分の行いたい挙動としましては、CSVデータの行毎に、Excelでも行を改行させて入力する
というものです。
頂いたコードを見る限りでは、@da-macさんも同様の挙動を想定したものなのかなと思いました。(間違っていたらすみません)
調べて見ましたが、Debug.Print line(indexA)でイミディエイトウィンドウを確認すると全てのCSVデータが表示されていたので、ここが
原因かなと思い、ましてLine input を使って1行ずつ読み込もうとしましたが
そもそもファイル形式ではないので利用できずでした。
次元の概念が合っているか不安ですが、 For indexA = 0 To UBound(line,1)で試してみましたが
1行目のみを取得する事もできずでした。

こちら何か策はありますでしょうか?
もしお時間等ございましたらご教示ください。宜しくお願い致します。

0

③についてですが、Rangeで範囲指定をしても、その範囲指定したセルの中一つ一つに全CSVデータが入っていました・・・

RangeもCells同様の処理を実現することが可能ですが個人的にはループさせたりしながら出力する場合はCellsがおすすめです。

追加の質問でおっしゃられているのは下記コードの部分を抜粋して使用したら抜け出せなくなった ということでしょうか?少し状況がつかめないです...モウシワケナイ


For indexB = 0 To UBound(csList)

        Debug.Print csList(indexB)

        ws.Cells(gyouIndex, retuIndex).Value = csList(indexB)


        retuIndex = retuIndex + 1
Next indexB

サンプルとして記載させて頂いたソースでは


    For indexA = 0 To UBound(line) '<1>

        For indexB = 0 To UBound(csList) '<2>

            retuIndex = retuIndex + 1 '<3>
        Next indexB

        retuIndex = 1 '<4>
        gyouIndex = gyouIndex + 1 '<5>

    Next indexA

<1> [外ループ]一行取り出して処理を行う
<2> [内ループ]一行に含まれる文字をカンマで分割して処理する
<3> [内ループ]のインデックスを加算して一行に含まれる要素が空になるまで[内ループ]が回る
<4> [外ループ]次の行出力に備え列のインデックスを1に戻す
<5> [外ループ]次の行出力に備え行のインデックスを加算する
次の行データがあるなら<1>に戻る

という処理の動きになるので行は移動するかと思います
(下図のような動きです)

A4won2VO.jpg

サンプルソースには以下のようなログ出力を入れていますので
併せ確認頂けると幸いです。


   'ログ表示
   Debug.Print "処理対象の行    >>>" & line(indexA)
   'ログ表示
   Debug.Print "処理対象の要素  >>>" & csList(indexB)

0

Comments

  1. ご回答いただき、ありがとうございます!
    コードがあっているとなると、元のCSVデータにそもそも行がなく1行のデータ形式になっている等の原因なのでしょうか・・・

    抜け出せなくなったというよりは、Excelのシートで出力された際に、1行目のみに入力されるという現象が起こったため、ループから抜け出せなくなっているのかと思いました。。

    もう少し、色々調べて見ます。。。
  2. >元のCSVデータにそもそも行がなく1行のデータ形式になっている等の原因なのでしょうか・・・

    流石に行のないCSVをプログラム側が勝手に判断して改行するなんて言うことはできないですね

    >抜け出せなくなったというよりは、Excelのシートで出力された際に、1行目のみに入力されるという現象が起こったため、ループから抜け出せなくなっているのかと思いました。。

    エクセルで無限ループの状態になった場合、エクセルが操作できなくなり(応答していませんの状態)そのまま落ちることが多いです
    (Ctrl+Breakで処理を中断すれば止まりますが...)

    もしくはDebug.Printをループに挟んで表示が止まらない(無限に出続ける)ことなどを確認してください。


    もしかしてですが、用途として何回も実行して継ぎ足し(1回目実行で出力、2回目実行で1回目出力の次の行から....)出力されるようなイメージをされている感じでしょうか?


  3. もしかしてですが、用途として何回も実行して継ぎ足し(1回目実行で出力、2回目実行で1回目出力の次の行から....)出力されるようなイメージをされている感じでしょうか?
    →はい、そのようなイメージを抱いています。
    イミディエイトウィンドウにCSVの1行ずつ表示されていくようなイメージです
  4. vbCrLfをvbLfに変更して回したりもしてみましたが、やはり同様の現象は変わらずですね。。。

すみません、ちょこちょこ昨日からいただいたコードをいじくっていたのですが、下記のコードで自分の考えていた事が実行されました!

Sub test5()

Dim ws As Worksheet
Set ws = Worksheets("Paste01")

Dim httpRe As XMLHTTP60

Set httpReq = New XMLHTTP60
httpReq.Open "get", Worksheets("Setting").Range("B20").Value
httpReq.send

Do While httpReq.readyState < 4
    DoEvents
Loop

Dim line
line = Split(httpReq.responseText, vbLf)

Dim csList
Dim indexA, indexB


For indexA = 0 To UBound(line)

    Debug.Print line(indexA)

    csList = Split(line(indexA), ",")

    For indexB = 0 To UBound(csList)

    Debug.Print csList(indexB)

    ws.Cells(indexA + 1, indexB + 1).Value = csList(indexB)



    Next indexB


Next indexA

End Sub

変数にしたgyouindexとretuindexでExcelのCellを指定していたと思いますが、それを外し、indexA,indexBでCellを指定するとCSVの行毎にExcelのセルに文字が入っていきました。

もしかして、イメージしていた挙動と異なっていましたでしょうか??
私の伝え方が悪かったと思われます。。。

0

おめでとうございます!思われてた挙動になったのですね!!

indexA,indexBを利用してCellsの出力先を設定するというのもアリなのですが、
行列の操作はgyouindex,retuindex 配列の操作(ループのインデックス)はindexA,indexB
というように分離しているほうがのちの拡張が楽になるのでそいった形にしていました。
例)特定のデータは出力したくない、シートへの出力の開始位置を調整したい etc...

今回の場合の挙動としてはCellsに指定してしている行列の各インデックスがループで使用しているインデックスと内容が一致するためそういった動きになるのだと思います。

vbCrLfをvbLfに変更して回したりもしてみましたが、やはり同様の現象は変わらずですね。。。

これは挙動にかかわりますね。こちらで想定データ(CSVデータ)の改行コードいじってみましたが動きが変わりますね

こちらで実行してる感じではインデックス系の変更では動きに変わりなかったので、改行コード絡みのほうが怪しいかもしれないです。

0

Your answer might help someone💌