title: How I can parse HTML from Blazor-based website? C#
body: <h1>THE PROBLEM</h1>
  <p>There is a web page with students' schedules: <a href="https://education.khai.edu/union/schedule/" rel="nofollow noreferrer">https://education.khai.edu/union/schedule/</a>.</p>
  <p>When I make a request by the link I get &quot;empty&quot; HTML. It means that responded HTML contains JS-scripts only, no schedule HTML:</p>
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html lang=&quot;ru-UA&quot;&gt;
  &lt;head&gt;
  
      ...
  
      &lt;!--Blazor:{&quot;sequence&quot;:1,&quot;type&quot;:&quot;server&quot;,&quot;descriptor&quot;:&quot;CfDJ8MBhIHG65FtKlX56pWtbUtQZ6T285HAgOYeCRtbbe9JO4U4cZsYQJ9xvkrUrO01rnP\u002BgDnPMCl0MnI0E/fW58mYoqDZ3J1ztRz/DKm9\u002BABDrmL5ArBFfTFdeO82HHavNnd1E10j7gHBU9uqKmOW2otP1y5s/a\u002BnMT/P2jvdetcCcDQvdfLnX2/w747D4dYNA1MuoeBRlst63xJlks\u002BYeAfhhNMi1s961JEi777JANAEi\u002B9g\u002BNf7aS9sLn\u002BbJZ4m0IBrUnCcHbu3idntWrD/GDpgDVCwhrIhUIPhs8ITgqZHJdQprUnffKWflcMbJ6YyyWBBABTi2eOX/VMHvtFWxT8ABDgmXbyqC3vTfRe6VlwN5ibDYH/UKDkULoJuX\u002Bw\u002BQB2e3sSP1OddN/ud8pWe5\u002BuCo3\u002BkQ9OG6x2GLMXJHWgah&quot;}--&gt;
  &lt;/head&gt;
  &lt;body class=&quot;x-background&quot;&gt;
  
  &lt;!--Blazor:{&quot;sequence&quot;:0,&quot;type&quot;:&quot;server&quot;,&quot;descriptor&quot;:&quot;CfDJ8MBhIHG65FtKlX56pWtbUtTdcyRUeUr\u002BhT344Mo3B4Gc0Gg3YwX1FY0c9owxv7oR1MDnLFR1BTJFjhuwYAjnROc3JT8UhSCkRbOdLVMuG0iwpNvwHNc47\u002BrguaHCTkDZKvZ9GKc0Jp\u002BCX0hcssqhCnp6eka\u002BG9Q7XF2B4ARhWnuJDKvUT\u002BbuWra063kFqG0Ixs4eWc4KrPRNS1KnTVu3QZrmx8r9dx6iyQXHjN/YgTqJhcv9LoQqWTfncbhBLwGm9l0BCTBLn3fGdJsOB6ES0lRwvVygmY7DA/2OGzhY7jGppr6UNaUXhdgo4xZDi3FkZgY3OL5xGS1p0bkc14UU9TM=&quot;}--&gt;
      &lt;script async src=&quot;/ui/app.js?v=3hNtGnhO8Vl6rh70OirKX4BnS6mxiiS5k9p3XAvofZA&quot;&gt;&lt;/script&gt;
      &lt;script src=&quot;_framework/blazor.server.js&quot; autostart=&quot;false&quot;&gt;&lt;/script&gt;
      &lt;script src=&quot;_content/Blazor-Analytics/blazor-analytics.js&quot;&gt;&lt;/script&gt;
      &lt;script&gt;
      async function connectionDown() {
          console.log(&quot;Blazor disconnected&quot;);
          location.reload();
      }
  
      function connectionUp() {
          console.log(&quot;Blazor connected&quot;);
      }
  
      window.Blazor.start({
          reconnectionOptions: {
              maxRetries: 10,
              retryIntervalMilliseconds: 500,
          },
          reconnectionHandler: {
              onConnectionDown: e =&gt; connectionDown(e),
              onConnectionUp: e =&gt; connectionUp(e)
          }
      });
  &lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  <p>When I open the web page from a browser, the website starts <code>SignalR</code>-stream and load the needed schedule HTML.</p>
  <p>How I can get a similar result from .NET?</p>
  <hr />
  <h1>SOME SUCCESSES</h1>
  <p>I found that the site uses the <code>blazor.server.js</code> script for manipulations with the <code>SignalR</code>-stream. There is its source code: <a href="https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Boot.Server.ts" rel="nofollow noreferrer">click</a>.</p>
  <h2>CONNECTION</h2>
  <p>I tried to rewrite the JS connection code to C# and I had success. JS:</p>
  <pre><code>...
  async function initializeConnection(options: CircuitStartOptions, logger: Logger, circuit: CircuitDescriptor): Promise&lt;HubConnection&gt; {
    const hubProtocol = new MessagePackHubProtocol();
    (hubProtocol as unknown as { name: string }).name = 'blazorpack';
  
    const connectionBuilder = new HubConnectionBuilder()
      .withUrl('_blazor')
      .withHubProtocol(hubProtocol);
  
    options.configureSignalR(connectionBuilder);
  
    const connection = connectionBuilder.build();
  ...
  </code></pre>
  <p>.NET - <strong>BlazorPackHubProtocol.cs</strong> (I couldn't find any other way to change the protocol name, so I created a shell for <code>MessagePackHubProtocol</code>):</p>
  <pre><code>using System;
  using System.Buffers;
  using Microsoft.AspNetCore.Connections;
  using Microsoft.AspNetCore.SignalR;
  using Microsoft.AspNetCore.SignalR.Protocol;
  using Microsoft.Extensions.Options;
  
  namespace KuzCode.SignalR.Protocols.BlazorPack
  {
      public class BlazorPackHubProtocol : IHubProtocol
      {
          private MessagePackHubProtocol _protocol;
  
          public string Name =&gt; &quot;blazorpack&quot;; // if the protocol has another name, connection fails
          public int Version =&gt; _protocol.Version;
          public TransferFormat TransferFormat =&gt; _protocol.TransferFormat;
  
          public BlazorPackHubProtocol(IOptions&lt;MessagePackHubProtocolOptions&gt; options)
          {
              _protocol = new(options);
          }
  
          public BlazorPackHubProtocol() : this(Options.Create(new MessagePackHubProtocolOptions())) { }
  
          public bool IsVersionSupported(int version) =&gt; _protocol.IsVersionSupported(version);
  
          public bool TryParseMessage(ref ReadOnlySequence&lt;byte&gt; input, IInvocationBinder binder, out HubMessage message)
              =&gt; _protocol.TryParseMessage(ref input, binder, out message);
  
          public void WriteMessage(HubMessage message, IBufferWriter&lt;byte&gt; output)
              =&gt; _protocol.WriteMessage(message, output);
  
          public ReadOnlyMemory&lt;byte&gt; GetMessageBytes(HubMessage message) =&gt; _protocol.GetMessageBytes(message);
      }
  }
  </code></pre>
  <p>.NET - <strong>BlazorPackProtocolDependencyInjectionExtensions.cs</strong> (for easy use in connection builder):</p>
  <pre><code>using KuzCode.SignalR.Protocols.BlazorPack;
  using Microsoft.AspNetCore.SignalR;
  using Microsoft.AspNetCore.SignalR.Protocol;
  using Microsoft.Extensions.DependencyInjection.Extensions;
  using System;
  
  namespace Microsoft.Extensions.DependencyInjection
  {
      public static class BlazorPackProtocolDependencyInjectionExtensions
      {
          public static TBuilder AddBlazorPackProtocol&lt;TBuilder&gt;(this TBuilder builder) where TBuilder : ISignalRBuilder
              =&gt; builder.AddBlazorPackProtocol(_ =&gt; { });
  
          public static TBuilder AddBlazorPackProtocol&lt;TBuilder&gt;(this TBuilder builder, Action&lt;MessagePackHubProtocolOptions&gt; configure)
              where TBuilder : ISignalRBuilder
          {
              builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton&lt;IHubProtocol, BlazorPackHubProtocol&gt;());
              builder.Services.Configure(configure);
  
              return builder;
          }
      }
  }
  </code></pre>
  <p>.NET - <strong>KhaiClient.cs</strong> (main class with connection):</p>
  <pre><code>using Microsoft.AspNetCore.Http.Connections;
  using Microsoft.AspNetCore.SignalR.Client;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Logging;
  using System;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  
  namespace KuzCode.KhaiApiClient
  {
      public class KhaiClient
      {
          private HubConnection _hubConnection;
  
          public KhaiClient()
          {
              _hubConnection = new HubConnectionBuilder()
                  .WithUrl(&quot;wss://education.khai.edu/_blazor&quot;, configuration =&gt;
                  {
                      configuration.SkipNegotiation = false;
                      configuration.Transports = HttpTransportType.WebSockets;
                  })
                  .AddBlazorPackProtocol()
                  .ConfigureLogging(logging =&gt;
                  {
                      logging.AddConsole();
                      logging.SetMinimumLevel(LogLevel.Debug);
                  })
                  .Build();
          }
  
          public async Task ConnectAsync() =&gt; await _hubConnection.StartAsync();
  
          public async Task DisconnectAsync() =&gt; await _hubConnection.StopAsync();
      }
  }
  </code></pre>
  <p>.NET - <strong>Program.cs</strong> (for testing):</p>
  <pre><code>var khaiClient = new KhaiClient();
  khaiClient.ConnectAsync().Wait();
  
  while (true) {}
  </code></pre>
  <p><strong>It's working! I had the next log:</strong></p>
  <pre><code>...
  info: Microsoft.AspNetCore.Http.Connections.Client.Internal.WebSocketsTransport[1]
        Starting transport. Transfer mode: Binary. Url: 'wss://education.khai.edu/_blazor?id=_f5aporohBDfopQF-CExsA'.
  
  ...
  
  info: Microsoft.AspNetCore.SignalR.Client.HubConnection[24]
        Using HubProtocol 'blazorpack v1'.
  dbug: Microsoft.AspNetCore.SignalR.Client.HubConnection[28]
        Sending Hub Handshake.
  
  ...
  
  dbug: Microsoft.AspNetCore.SignalR.Client.HubConnection[47]
        Receive loop starting.
  info: Microsoft.AspNetCore.SignalR.Client.HubConnection[44]
        HubConnection started.
  </code></pre>
  <h1>WHAT NEXT?</h1>
  <p>Now I tried to recreate some requests but had no success.</p>
  <p>Select another group request (I selected group <code>613п</code>):
  <a href="https://i.stack.imgur.com/vxDPR.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vxDPR.png" alt="enter image description here" /></a></p>
  <p>First response:
  <a href="https://i.stack.imgur.com/IvgSU.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/IvgSU.png" alt="enter image description here" /></a></p>
  <hr />
  <p>Source code: <a href="https://github.com/iiKuzmychov/KhaiApiClient" rel="nofollow noreferrer">https://github.com/iiKuzmychov/KhaiApiClient</a>.</p>
  <h1>Update:</h1>
  <p>I found a <a href="https://github.com/dotnet/aspnetcore/blob/4e0d4c44f866c6e37b44f3bc002bd30d0591b23c/src/Components/Server/src/ComponentHub.cs" rel="nofollow noreferrer">hub implementation</a> I want, I can copy-paste code, but I don`t know how to initialize it.</p>