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" } } }