让 HTML for HTML Agility Pack 进行处理的最佳方法是什么?

What is the best way to get the HTML for HTML Agiligy Pack to process?

我似乎无法从一些网站获得 HTML,但可以从许多其他网站获得。以下是我遇到问题的 2 个网站:

https://www.rei.com

https://www.homedepot.com

我正在构建一个应用程序,它将从用户输入的 URL 中获取元标记信息。一旦我得到 HTML 代码,我就使用 HTML Agility pack 处理它并且它完美地工作。问题在于从各种网站获取 HTML。

我尝试了各种方法来获得 HTML(HtmlWebHttpWebRequest 和其他),所有方法都设置了 user-agent(与 [=46 相同的代理标签) =]), headers, cookies 和自动重定向, gzip-ing 似乎每一个组合。全部通过 Fiddler 进行验证,但我似乎无法弄清楚为什么我无法从某些站点获取 HTML,它们只是超时,当我可以在我的浏览器就好了。我发送的 headers 看起来和 Fiddler 一样。 有谁知道是什么导致 URL 而不是 return 而 HTML/data?或者有没有人有一个 NuGet 包或框架来处理获取 HTML page/document 的所有细微差别,网站是否是 SSL、gzip、需要 cookie、重定向等?

进入这个项目我认为最困难的部分是处理 HTML 没有得到它所以任何帮助将不胜感激。

更新 1:

我试过了,但我似乎无法让它工作...我一定是遗漏了一些简单的东西...这是一个包含一些建议更改的更新示例。

https://dotnetfiddle.net/tQyav7

我不得不注释掉 dotnetfiddle 上的 ServerCertificateValidationCallback,因为它在那里抛出错误,但它不在我的开发箱中。我还必须将超时设置为仅 5 秒......我的开发箱上有 20 秒。任何帮助将不胜感激。

这是您的 助手 class,经过重构以支持 HttpWebResponse 可以处理的大多数 Web 响应。

注意:如果您没有 Option ExplicitOption Strict[=65=,请不要进行此类设置] 设置为 True:你永远做不对。自动推理不是你的朋友(好吧,实际上从来不是;你真的需要知道你正在处理的是什么对象)。

修改了什么重要句柄:

  • TLS 处理:将支持扩展到仅 TLS 1.1 和 TLS 1.2

  • WebRequest.ServicePoint.Expect100Continue = False:你永远不会想要这种回应,除非你准备好遵守。但从来没有必要。

  • [AutomaticDecompression][1] 是必需的,除非您想 手动 处理 GZip 或 Deflate 流。它几乎从不需要(只有当你想在解压之前分析原始流时)。

  • 每次都会重建CookieContainer。这尚未修改,但您可以存储一个静态对象并在每个请求中重复使用 Cookie:某些站点可能会在执行 Tls 握手时设置 cookie 并重定向到登录页面。 WebRequest 可用于 POST 身份验证参数(验证码除外),但您需要保留 Cookies,否则任何进一步的请求将无法通过身份验证。

  • Response Stream ReadToEnd() 方法也保持原样,但您应该修改它以读取缓冲区。例如,它将允许显示下载进度,并在需要时取消操作。

  • 重要提示:不能将 UserAgent 设置为任何现有浏览器的最新版本。一些网站,当检测到用户代理支持 HSTS protocol 时,将激活它并等待交互。 WebRequest 对 HSTS 一无所知,并且会超时。我将 UserAgent 设置为 Internet Explorer 11。它适用于所有网站。

  • Http 重定向设置为自动,但有时需要手动进行。这可以提高此过程的可靠性。例如,您可以禁止重定向到范围外的目的地。或者您不支持的 HTTP 协议更改。

建议:此 class 将受益于 HttpWebRequest 方法的 async 版本:您将能够发出多个并发请求,而不是等待每个请求和所有请求同步完成。
只需进行少量修改即可将此 class 转换为 async 版本。

此 class 现在应该支持大多数 Html 不使用脚本异步构建内容的页面。
如评论中所述,Lazy HttpClient 可以处理其中一些(不是全部)页面,但它需要完全不同的设置。


Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Security
Imports System.Security.Cryptography.X509Certificates
Imports System.Text

Public Class WebRequestHelper
    Private m_ResponseUri As Uri
    Private m_StatusCode As HttpStatusCode
    Private m_StatusDescription As String
    Private m_ContentSize As Long
    Private m_WebException As WebExceptionStatus
    Public Property SiteCookies As CookieContainer
    Public Property UserAgent As String = "Mozilla / 5.0(Windows NT 6.1; WOW32; Trident / 7.0; rv: 11.0) like Gecko"
    Public Property Timeout As Integer = 30000
    Public ReadOnly Property ContentSize As Long
        Get
            Return m_ContentSize
        End Get
    End Property

    Public ReadOnly Property ResponseUri As Uri
        Get
            Return m_ResponseUri
        End Get
    End Property

    Public ReadOnly Property StatusCode As Integer
        Get
            Return m_StatusCode
        End Get
    End Property

    Public ReadOnly Property StatusDescription As String
        Get
            Return m_StatusDescription
        End Get
    End Property

    Public ReadOnly Property WebException As Integer
        Get
            Return m_WebException
        End Get
    End Property


    Sub New()
        SiteCookies = New CookieContainer()
    End Sub

    Public Function GetSiteResponse(ByVal siteUri As Uri) As String
        Dim response As String = String.Empty

        ServicePointManager.DefaultConnectionLimit = 50
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
        ServicePointManager.ServerCertificateValidationCallback = AddressOf TlsValidationCallback

        Dim Http As HttpWebRequest = WebRequest.CreateHttp(siteUri.ToString)
        With Http
            .Accept = "ext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
            .AllowAutoRedirect = True
            .AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
            .CookieContainer = Me.SiteCookies
            .Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate")
            .Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US,en;q=0.7")
            .Headers.Add(HttpRequestHeader.CacheControl, "no-cache")
            ' Default
            .KeepAlive = True
            .MaximumAutomaticRedirections = 50
            .ServicePoint.Expect100Continue = False
            .ServicePoint.MaxIdleTime = Me.Timeout
            .Timeout = Me.Timeout
            .UserAgent = Me.UserAgent
        End With

        Try
            Using webResponse As HttpWebResponse = DirectCast(Http.GetResponse, HttpWebResponse)
                Me.m_ResponseUri = webResponse.ResponseUri
                Me.m_StatusCode = webResponse.StatusCode
                Me.m_StatusDescription = webResponse.StatusDescription
                Dim contentLength As String = webResponse.Headers.Get("Content-Length")
                Me.m_ContentSize = If(String.IsNullOrEmpty(contentLength), 0, Convert.ToInt64(contentLength))

                Using responseStream As Stream = webResponse.GetResponseStream()
                    If webResponse.StatusCode = HttpStatusCode.OK Then
                        Dim reader As StreamReader = New StreamReader(responseStream, Encoding.Default)
                        Me.m_ContentSize = webResponse.ContentLength
                        response = reader.ReadToEnd()
                        Me.m_ContentSize = If(Me.m_ContentSize = -1, response.Length, Me.m_ContentSize)
                    End If
                End Using
            End Using
        Catch exW As WebException
            If exW.Response IsNot Nothing Then
                Me.m_StatusCode = CType(exW.Response, HttpWebResponse).StatusCode
            End If
            Me.m_StatusDescription = "WebException: " & exW.Message
            Me.m_WebException = exW.Status
        End Try
        Return response
    End Function

    Private Function TlsValidationCallback(sender As Object, CACert As X509Certificate, CAChain As X509Chain, SslPolicyErrors As SslPolicyErrors) As Boolean
        ' If you trust the (known) Server, you could just return True
        If SslPolicyErrors = SslPolicyErrors.None Then Return True
        Dim Certificate As New X509Certificate2(CACert)

        CAChain.Build(Certificate)
        For Each CACStatus As X509ChainStatus In CAChain.ChainStatus
            If (CACStatus.Status <> X509ChainStatusFlags.NoError) And
                (CACStatus.Status <> X509ChainStatusFlags.UntrustedRoot) Then
                Return False
            End If
        Next
        Return True
    End Function
End Class