potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# 8 HTMLのスクリプト要素の一部をJSONとして抜き出す

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。

C# 8.0でHTMLのスクリプト要素の一部をJSONとして抜き出す方法の覚書です。この投稿ではSystem.Text.Json.JsonDocumentクラスを用いて不定形のJSONを扱います。スクリプト要素の一部がJSONの規則を満たさない場合は扱えないことに注意してください。なお、定形のJSONを構造体などとして扱う場合はSystem.Text.Json.JsonSerializerクラスを使用してください。

HTMLファイルの特定のSCRIPT要素からJSONを抜き出して参照する

using System.Linq;
using System.Text.Json;
using AngleSharp.Html.Dom; // AngleSharpをインストールしてください。
using AngleSharp.Html.Parser;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var source = "<html><head><script src=\"\"></script></head><body>" +
                "<script src=\"\"></script>" +
                "<script defer>let x = {\"abc\":\"abc\", \"def\":false, \"ghi\": false, \"items\":{\"data1\": 0, \"data2\": \"value\", \"data3\": [0, 1, 2], \"data4\":{ \"data41\": 41}}, \"xyz\": false};</script>" +
                "<script>var y = 0;</script></body></html>";

            var parser = new HtmlParser();
            // TODO:ここでHTMLを読み込みます。
            //      必要に応じてファイルやインターネットから読み込んでください。
            var doc = default(IHtmlDocument);
            doc = parser.ParseDocument(source);

            // TODO:ここで目的のSCRIPT要素を取得します。
            //      ここではBODY要素子孫でdefer属性を持つSCRIPT要素を取得します。
            var scriptElements = doc.QuerySelectorAll("body script[defer]");
            if (scriptElements.Length != 1)
            {
                return;
            }

            // TODO:ここでは前後が不変であると仮定してitems部分をJSONとして抜き出します。
            //      head、tail、i1、i2の内容は取得する内容に合わせて調整してください。
            var scriptElementData = scriptElements.First().TextContent;
            var headString = "\"def\":false, \"ghi\": false, \"items\":{\"data1\":";
            var tailString = "}, \"xyz\": false";
            var i1 = scriptElementData.IndexOf(headString) + "\"def\":false, \"ghi\": false, ".Length;
            var i2 = scriptElementData.IndexOf(tailString, i1 + headString.Length); // 今回は"}"を含めない
            // TODO:JSONが正常に抜き出せたかをここで確認してください。
            //      JSONではキーにも前後の「"」が必要なことに注意してください。
            var jsonRaw = "{" + scriptElementData.Substring(i1, i2 - i1+ 1) + "}";
            var jsonDoc = JsonDocument.Parse(jsonRaw);

            //
            var items = jsonDoc.RootElement.GetProperty("items");
            var itemsRawText = items.GetRawText();
            // {"data1": 0, "data2": "value", "data3": [0, 1, 2], "data4":{ "data41": 0}}

            var itemsPropNames = items.EnumerateObject().Select(prop => prop.Name).ToArray();
            // string[4] {"data1", "data2", "data3", "data4"}

            var data1 = items.GetProperty("data1");
            // ValueKind = Number : "0"
            var data4 = items.GetProperty("data4");
            // ValueKind = Object : "{ "data41": 41}"
            var data41 = items.GetProperty("data4").GetProperty("data41");
            // ValueKind = Number : "41"
        }
    }
}