使HTML for Agilegy Pack处理HTML的最佳方法是什么?

时间:2019-04-08 03:06:58

标签: .net vb.net html-agility-pack

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

  

https://www.rei.com

     

https://www.homedepot.com

我正在构建一个应用程序,该应用程序将从用户输入的URL获取元标记信息。一旦获得HTML代码,就可以使用HTML Agility Pack对其进行处理,并且可以完美地工作。问题在于从各个网站获取HTML。

我尝试了各种方法来获取HTML(HtmlWebHttpWebRequest等),这些方法都设置了用户代理(与chrome相同的代理标签),标头,cookie和自动重定向,gzip-似乎是每种组合。所有这些都以Fiddler的身份进行了验证,但我似乎无法弄清楚为什么我不能从某些站点获取HTML,它们只是超时,而当我可以在浏览器中提取相同的URL时。我发送的标题与Fiddler相同。 有谁知道是什么原因导致URL不返回HTML /数据?还是有人拥有NuGet软件包或框架来处理获取HTML页面/文档的所有细微差别,无论网站是否为SSL,采用gzip压缩,是否需要Cookie,重定向等?

进入这个项目,我认为最困难的部分将是处理HTML,而没有得到它,因此将不胜感激。

更新1:

我尝试过,但是似乎无法正常工作...我一定很容易错过一些东西...这是一个更新的示例,其中包含一些建议的更改。

https://dotnetfiddle.net/tQyav7

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

1 个答案:

答案 0 :(得分:0)

这是您的 helper 类,该类经过重构以支持HttpWebResponse可以处理的大多数Web响应。

注意:如果没有将 Option Explicit Option Strict 设置为True,则永远不要进行此类设置:您永远不会做对。自动推理不是您的朋友(嗯,实际上从来没有;您真的需要知道您要处理的对象)。

已修改的内容以及重要的句柄:

  • Tls处理:对Tls 1.1,Tls 1.2和当前框架可以处理的最大协议版本的扩展支持:

    System.Enum.GetValues(GetType(SecurityProtocolType)).OfType(Of SecurityProtocolType)().Max()
    
  • WebRequest.ServicePoint.Expect100Continue = False:除非您准备遵守,否则您永远都不会想要这种回应。但这从来没有必要。

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

  • CookieContainer每次都会重建。尚未修改,但是您可以存储静态对象,并在每个请求中重用Cookies:某些站点在执行Tls握手时可能会设置Cookies,并重定向到登录页面。可以使用WebRequest来发布身份验证参数(验证码除外),但是您需要保留Cookies,否则任何其他请求都不会被身份验证。

  • 响应流 ReadToEnd() 方法也保持不变,但是您应该对其进行修改以读取缓冲区。例如,它将允许显示下载进度,并且如果需要,还可以取消该操作。

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

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

一个建议:此类将受益于HttpWebRequest方法的async版本:您将能够发出多个并发请求,而不必等待每个请求都同步完成。
仅需进行少量修改即可将该类转换为异步版本。

该类现在应该支持大多数不使用脚本异步构建内容的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
        Dim maxFWValue As SecurityProtocolType = System.Enum.GetValues(GetType(SecurityProtocolType)).OfType(Of SecurityProtocolType)().Max()
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12 Or maxFWValue
        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")
            .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 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