我似乎无法从一些站点获得HTML,但是可以从许多其他站点获得HTML。这是我遇到问题的2个网站:
我正在构建一个应用程序,该应用程序将从用户输入的URL获取元标记信息。一旦获得HTML代码,就可以使用HTML Agility Pack对其进行处理,并且可以完美地工作。问题在于从各个网站获取HTML。
我尝试了各种方法来获取HTML(HtmlWeb
,HttpWebRequest
等),这些方法都设置了用户代理(与chrome相同的代理标签),标头,cookie和自动重定向,gzip-似乎是每种组合。所有这些都以Fiddler的身份进行了验证,但我似乎无法弄清楚为什么我不能从某些站点获取HTML,它们只是超时,而当我可以在浏览器中提取相同的URL时。我发送的标题与Fiddler相同。
有谁知道是什么原因导致URL不返回HTML /数据?还是有人拥有NuGet软件包或框架来处理获取HTML页面/文档的所有细微差别,无论网站是否为SSL,采用gzip压缩,是否需要Cookie,重定向等?
进入这个项目,我认为最困难的部分将是处理HTML,而没有得到它,因此将不胜感激。
更新1:
我尝试过,但是似乎无法正常工作...我一定很容易错过一些东西...这是一个更新的示例,其中包含一些建议的更改。
https://dotnetfiddle.net/tQyav7
我不得不在dotnetfiddle上注释掉ServerCertificateValidationCallback,因为它在那里抛出了错误,但是它不在我的开发框中。我还必须将超时设置为仅5秒...在我的开发箱中将其设置为20。任何帮助将不胜感激。
答案 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。它适用于所有站点。
一个建议:此类将受益于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