【.NET】HARファイルのCSV変換


はじめに

これは、Visual Basic Advent Calendar 2018の8日目の記事となります。

同僚から「HARファイルをCSVに変換して欲しい」旨の依頼メールが届いた。
HARファイルってなんやねんってところから調べてた。

HARファイル

HAR(HTTP アーカイブ)は、複数の HTTP セッション ツールでキャプチャ済みデータをエクスポートする場合に使用されるファイル形式で、中身はテキスト形式のJSONオブジェクトです。

Windows 7のIEのデベロッパーツールでネットワークのキャプチャを取得した場合、保存した際にはCSVとXMLの2つの形式が選択できました。しかし、Windows 10のIEではHAR形式のみとなりました。
他のブラウザと同様になるように統一されたんでしょうね。

変換調査

「har csv」で検索して見つけたのが下記サイトになります。
サイトのリクエスト情報をエクセルで見れるようにする
Windowsだからなのかエラーが出たのでやめました。

次にHow to import HAR file to excel - stackoverflowの中で紹介されていた「har2csv」
HTTP Archive (har) to CSV converter (har2csv)

これはJavaで作成されていて、130MByteあるHARファイルで変換しようとしたが、メモリエラーが発生、それではと2件くらいのデータにしてみたが、フォーマット形式が一致しないのかエラーになって変換できなかった。2013年と5年前のツールなので対応していないのかも知れない。

Convert JSON to CSV
オンラインツール、HARファイルの中身はJSONなので試しに2件くらいのデータにやってみたらCSV出力できたので悪くはない。しかし、130MByteとデータが大きかったのでやめました。

変換

ツール探しは諦めて、JSONのCSV変換くらいならとC#で自作することにしました。
JsonからC#のクラスモデルを作成してくれる「json2csharp」というサイトがあったので、HARファイルの1件データを抽出してクラスモデルを作成。

Windows 7のIEでCSV出力の項目があればいいので、とりあえずこれでCSV変換が出来たのですが、今後のことを考えて、HARファイルの正式なクラスのようなのがあるはずだと調べたところ、「HarSharp」を見つけました。
https://github.com/giacomelli/HarSharp

NuGetで「HarSharp」を検索して、ライブラリを導入しました。

C#版
string path = "test.har";
var har = HarConvert.DeserializeFromFile(path);

List<string> hederList = new List<string>();
hederList.Add("開始日時");       // startedDateTime
hederList.Add("URL");            // request.url
hederList.Add("プロトコル");     // "HTTP"固定
hederList.Add("メソッド");       // request.method
hederList.Add("結果");           // response.status
hederList.Add("種類");           // headers.Content-Type
hederList.Add("受信");           // response.bodySize
hederList.Add("所要時間");       // time
hederList.Add("イニシエーター"); // 不明
hederList.Add("待機");           // timings.blocked
hederList.Add("開始");           // timings.send
hederList.Add("要求");           // timings.wait
hederList.Add("応答");           // timings.receive
using (StreamWriter sw = new StreamWriter(path.replace(".har",".csv"), false, Encoding.GetEncoding("utf-8")))
{
    // ヘッダー
    sw.WriteLine(string.Join(",", hederList));

    foreach (Entry ent in har.Log.Entries)
    {
        List<string> dataList = new List<string>();
        dataList.Add(ent.StartedDateTime.ToLocalTime().ToString());
        dataList.Add(ent.Request.Url.ToString());
        dataList.Add("HTTP");
        dataList.Add(ent.Request.Method);
        dataList.Add(ent.Response.Status.ToString());
        string conType = "";
        foreach(Header headers in ent.Response.Headers)
        {
            if(headers.Name == "Content-Type")
            {
                conType = headers.Value;
                break;
            }
        }
        dataList.Add(conType);
        dataList.Add(ent.Response.BodySize.ToString());
        dataList.Add(ent.Time.ToString());
        dataList.Add("");
        dataList.Add(ent.Timings.Blocked.ToString());
        dataList.Add(ent.Timings.Send.ToString());
        dataList.Add(ent.Timings.Wait.ToString());
        dataList.Add(ent.Timings.Receive.ToString());

        sw.WriteLine(string.Join(",", dataList));
    }
}

※プロトコルは"HTTP"固定、イニシエーターが何か不明なので空文字にしました。

2018/12/08 VB.NET版を追記

VB.NET版
Dim path As String = "test.har"
Dim har = HarConvert.DeserializeFromFile(path)

Dim hederList As New List(Of String)()
hederList.Add("開始日時")       ' startedDateTime
hederList.Add("URL")            ' request.url
hederList.Add("プロトコル")     ' "HTTP"固定
hederList.Add("メソッド")       ' request.method
hederList.Add("結果")           ' response.status
hederList.Add("種類")           ' headers.Content-Type
hederList.Add("受信")           ' response.bodySize
hederList.Add("所要時間")       ' time
hederList.Add("イニシエーター") ' 不明
hederList.Add("待機")           ' timings.blocked
hederList.Add("開始")           ' timings.send
hederList.Add("要求")           ' timings.wait
hederList.Add("応答")           ' timings.receive
Using sw As New StreamWriter(path.replace(".har", ".csv"), False, Encoding.GetEncoding("utf-8"))
    ' ヘッダー
    sw.WriteLine(String.Join(",", hederList))

    For Each ent As Entry In har.Log.Entries
        Dim dataList As New List(Of String)()
        dataList.Add(ent.StartedDateTime.ToLocalTime().ToString())
        dataList.Add(ent.Request.Url.ToString())
        dataList.Add("HTTP")
        dataList.Add(ent.Request.Method)
        dataList.Add(ent.Response.Status.ToString())
        Dim conType As String = ""
        For Each headers As Header In ent.Response.Headers
            If headers.Name = "Content-Type" Then
                conType = headers.Value
                Exit For
            End If
        Next
        dataList.Add(conType)
        dataList.Add(ent.Response.BodySize.ToString())
        dataList.Add(ent.Time.ToString())
        dataList.Add("")
        dataList.Add(ent.Timings.Blocked.ToString())
        dataList.Add(ent.Timings.Send.ToString())
        dataList.Add(ent.Timings.Wait.ToString())
        dataList.Add(ent.Timings.Receive.ToString())

        sw.WriteLine(String.Join(",", dataList))
    Next
End Using

最後に

Windows 10のIEでもデベロッパーツールのところはWindows 7とは違っているんですね。
今回はHARファイルって存在を知れたことと、JsonからC#のクラスモデルを作成してくれるサイトを知れたのが良かったかな。