本節主要來介紹一下,在C#中制造爬蟲,最為常見、常用、實用的基礎類 ------ WebRequest、WebResponse。
先來看一個示例 [1.2.1]:
1 using System; 2 using System.IO; 3 using System.Net; 4 using System.Text; 5 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 var request = WebRequest.Create(@"https://www.cnblogs.com/mikecheers/p/12090487.html"); 11 request.Method = "GET"; 12 using (var response = request.GetResponse()) 13 { 14 using (var stream = response.GetResponseStream()) 15 { 16 using (var reader = new StreamReader(stream, new UTF8Encoding(false))) 17 { 18 var content = reader.ReadToEnd(); 19 Console.WriteLine(content); 20 } 21 } 22 response.Close(); 23 } 24 request.Abort(); 25 Console.ReadLine(); 26 } 27 }
運行以上代碼,我們能看到類似如下的輸出:
{省略若干html代碼}
<div id="cnblogs_post_body" class="blogpost-body "> <p> 在構建本章節內容的時候,筆者也在想一個問題,究竟什么樣的采集器框架,才能算得上是一個“全能”的呢?就我自己以往項目經歷而言,可以歸納以下幾個大的分類:</p> <ol> <li>根據通訊協議:HTTP的、HTTPS的、TCP的、UDP的;</li> <li>根據數據類型:純文本的、json的、壓縮包的、圖片的、視頻的;</li> <li>根據更新周期:不定期更新的、定期更新的、增量更新的;</li> <li>根據數據來源:單一數據源、多重數據源、多重數據源混合;</li> <li>根據采集點分布:單機的,集群的;</li> <li>根據反爬蟲策略:控制頻率的,使用代理的,使用特定UA的;</li> <li>根據配置:可配置的,不可配置的;</li> </ol> <p> 以上的分類,也有可能不夠全面,不過應該可以涵蓋主流數據采集的分類了。</p> <p> 為了方便闡述一個爬蟲的工作原理,我們從上面找到一條最簡單路徑,來進行闡述(偷奸耍滑?非也,大道化簡,萬變不離其宗:)</p> <p> OK,一個小目標,單機、單一數據源、定期更新、純文本、HTTP的爬蟲,來一只。</p> <p> </p> <p style="margin-left: 30px;">在第一境的后面各節中,我們就來逐步實現這個小目標,同時,也來探究一下其中的原理。只有掌握了這些,才能通過繼續擴展,衍生出強大的爬蟲:)</p> </div>
{省略若干html代碼}
一只爬蟲小幼崽兒,就如此輕松的誕生了~
示例 [1.2.1]中,最核心的部分,就是使用了本節的主題、兩個基礎類:WebRequest和WebResponse。
從名稱上就可以看出,這兩個類是針對Web資源的操作類。下面,將針對這兩個類來做針對性的細究。
WebRequest
[Code 1.2.2]
1 public abstract class WebRequest : MarshalByRefObject, ISerializable 2 { 3 protected WebRequest(); 4 protected WebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext); 5 /*************** 6 * 避免篇幅太長,此處省略部分屬性及方法。 7 * *************/ 8 public static IWebProxy DefaultWebProxy { get; set; } 9 public static RequestCachePolicy DefaultCachePolicy { get; set; } 10 public virtual IWebProxy Proxy { get; set; } 11 public static WebRequest Create(Uri requestUri); 12 public static WebRequest Create(string requestUriString); 13 public static WebRequest CreateDefault(Uri requestUri); 14 public static HttpWebRequest CreateHttp(Uri requestUri); 15 public static HttpWebRequest CreateHttp(string requestUriString); 16 public static IWebProxy GetSystemWebProxy(); 17 }
這里列出了我們通常最為關注的幾個屬性和方法,當然也包括類的定義。首先,它是一個抽象類,這也意味着,它會有派生類,在.Net Framework 4.6.1中,主要包括:
- System.IO.Packaging.PackWebRequest
- System.Net.FileWebRequest
- System.Net.FtpWebRequest
- System.Net.HttpWebRequest
[@https://docs.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.6.1]
可以看出,這些派生類都是挺實用的,微軟也做了貼心的封裝,大大減輕了碼工從socket做起的痛苦。
兩個構造函數,都是protected的,也就是說,對派生類的實例化是有要求的。
從帶參數的構造函數的兩個參數,可以看出,webrequest應該是可以被序列化的,而且提供了StreamingContext,這就為“接着”做某項采集工作提供了可能性。后面我看看有沒有合適的實例,再細說它們的作用。
在4.6.1中,比4.0多出了幾個靜態方法,比如:public static HttpWebRequest CreateHttp(xxx)。估計在更新的版本中,也會有所差異,所以大家在學習爬蟲的過程中,應盡量對自己所使用的framework版本及其對應的類有深刻的印象。
說完構造,我們看看[Code 1.2.2]中第11行到第15行中提及的關於創建實例的方法:
雖然有幾個重載,但看參數名稱就可以看得出,就是跟你要Uri(uniform resource identifier,統一資源標識符),比如:https://www.cnblogs.com/mikecheers/category/1609574.html,有了這個,它就可以出去“橫”了:P
返回類型有WebRequest和HttpWebRequest,可能是HttpWebRequest的需求太大了,所以在4.6.1中直接加入了創建這種實例的方法,我記得在4.0中還沒有,也沒有CreateDefault方法。
有了統一資源標識符,我們的爬蟲就可以定位到世界各地的資源了,而且不會有重復,但是,大家應該明白,定位歸定位,能不能拿,還不好說~
總之,通過這幾個常用的方法,我們就可以創建實例了。
說完創建實例,我們看看[Code 1.2.2]中第8行、第10行和第16行中提及的關於代理的屬性和方法:
- DefaultWebProxy 這個是一個靜態屬性,可以得知,無論我們創建了多少WebRequest的派生類的實例,DefaultWebProxy只有一個,當然,它是可變的,程序運行過程中,我們動態修改,但它是全局的,一旦設置,所有實例都會默認使用它,而無須未每個實例去設置;
- Proxy 這是一個虛屬性,為每一個WebRequest的派生類的實例提供定制化代理的可能;
- GetSystemWebProxy 是一個靜態方法,這個方法幫助我們獲取當前用戶IE配置的代理,在我國,大家使用的瀏覽器五花八門,所以很少使用到,不過做瀏覽器的話,還是很需要的;
說到代理,就再嘮叨兩句,代理是什么,有什么用處?代理是在訪問某個資源時,不能直接訪問到,或需要“繞道而行”,而提供的中轉站(這里注意措詞“不能直接訪問到”和“繞道而行”)。
- 不能直接訪問:比如說,目前在我天朝境內,我們想訪問google、facebook等,是不能直接訪問的,想訪問的話,代理,就是一條路,比如我們先繞到島國,由島國中轉再訪問google或facebook就可以了。當然,這里代理只是條條大路中的一條路而已,VPN也是一種選擇,不用太糾結;
- “繞道而行”:我們在做的是爬蟲,爬的資源人家是不是願意給,這個大家心里應該有點A<->C數,不願意給,人家就會做限制,俗稱“反爬策略”,其中一種常見的策略就是限制單客戶IP的並發數量及訪問頻率,我們作為爬蟲的開發者,有希望能夠盡快得到想要的資源,畢竟電費也挺貴的:)應對這種策略,常用的方式就是使用代理了,通過將請求分發到多個代理,來緩解單IP被限制的壓力。應用場景嘛,比如視頻直播間內無數的水軍帳號,動輒上萬,如果想從單IP做到這點,還是有難度的;
相信羅嗦這么多,大家也對代理有了個初步的理解了,那么從技術的角度,如何來看待代理呢。
首先,有了Uri,我們就知道最終的目的地是哪兒了,當然,代理收到我們的請求的時候,也能會意我們的最終目的是哪兒;
再次,它就以自己的立場再次向目的地發送請求,等待接收回復;
第三,當代理收到回復后,將數據交給我們的請求;
第四,我們收到回復,就好像從目的地收到的一樣;
其實,代理就是在做一個中轉站,為我們轉發請求。這里我們需要了解幾個問題點,並不是我們的每個請求都可以隨隨便便找個代理就可以轉發。代理分為很多種類:
- 比如根據轉發的請求不同,分為S5(Socket 5)、HTTP、HTTPS等;
- 根據隱匿程度,分為普通、高匿、透明等;還有其他一些分類;
- 根據公開程度,分為公有、私有等;
有需要深入了解的朋友,可以去找找專門講解代理的文章看看。
我們這里簡單說明一下匿名程度,代理是中轉站,也是我們的掩體,能夠多大程度隱藏我們的真實身份,標示了它的匿名程度,其中,普通匿名代理,標示目的地可以追蹤到我們的真實地址,透明代理,標示目的地只可追蹤到代理服務器的地址而無法追蹤到我們的真實地址。
前面提及代理會以自己的立場再次向目的地發送請求,這里並沒有明確指出是通過什么樣的手段(技術)來實現的,因為代理的實現,是一個不確定因素,市面上有很多廠家甚至是個人開發的代理軟件,還有一些專門用做代理服務器的硬件,它們的實現方式千差萬別,有的,只是將請求轉發,有的是通過克隆請求生成新的請求再重新發送,有的是在驅動級別修改網絡數據包等等。代理的世界也不小,我們姑且保留它們的神秘面紗吧,深究起來,就太耗費時間了:)
回到我們的主題上,兩個屬性(DefaultWebProxy和Proxy)一個方法(GetSystemWebProxy)依附的類型都是System.Net.IWebProxy接口。
1 public interface IWebProxy 2 { 3 ICredentials Credentials { get; set; } 4 Uri GetProxy(Uri destination); 5 bool IsBypassed(Uri host); 6 }
接口比較簡單,目前Framework(截止4.8)派生該接口的類,也只有System.Net.WebProxy一個。

1 // 2 // Summary: 3 // Contains HTTP proxy settings for the System.Net.WebRequest class. 4 public class WebProxy : IAutoWebProxy, IWebProxy, ISerializable 5 { 6 // 7 // Summary: 8 // Initializes an empty instance of the System.Net.WebProxy class. 9 public WebProxy(); 10 // 11 // Summary: 12 // Initializes a new instance of the System.Net.WebProxy class from the specified 13 // System.Uri instance. 14 // 15 // Parameters: 16 // Address: 17 // A System.Uri instance that contains the address of the proxy server. 18 public WebProxy(Uri Address); 19 // 20 // Summary: 21 // Initializes a new instance of the System.Net.WebProxy class with the specified 22 // URI. 23 // 24 // Parameters: 25 // Address: 26 // The URI of the proxy server. 27 // 28 // Exceptions: 29 // T:System.UriFormatException: 30 // Address is an invalid URI. 31 public WebProxy(string Address); 32 // 33 // Summary: 34 // Initializes a new instance of the System.Net.WebProxy class with the System.Uri 35 // instance and bypass setting. 36 // 37 // Parameters: 38 // Address: 39 // A System.Uri instance that contains the address of the proxy server. 40 // 41 // BypassOnLocal: 42 // true to bypass the proxy for local addresses; otherwise, false. 43 public WebProxy(Uri Address, bool BypassOnLocal); 44 // 45 // Summary: 46 // Initializes a new instance of the System.Net.WebProxy class with the specified 47 // host and port number. 48 // 49 // Parameters: 50 // Host: 51 // The name of the proxy host. 52 // 53 // Port: 54 // The port number on Host to use. 55 // 56 // Exceptions: 57 // T:System.UriFormatException: 58 // The URI formed by combining Host and Port is not a valid URI. 59 public WebProxy(string Host, int Port); 60 // 61 // Summary: 62 // Initializes a new instance of the System.Net.WebProxy class with the specified 63 // URI and bypass setting. 64 // 65 // Parameters: 66 // Address: 67 // The URI of the proxy server. 68 // 69 // BypassOnLocal: 70 // true to bypass the proxy for local addresses; otherwise, false. 71 // 72 // Exceptions: 73 // T:System.UriFormatException: 74 // Address is an invalid URI. 75 public WebProxy(string Address, bool BypassOnLocal); 76 // 77 // Summary: 78 // Initializes a new instance of the System.Net.WebProxy class with the specified 79 // System.Uri instance, bypass setting, and list of URIs to bypass. 80 // 81 // Parameters: 82 // Address: 83 // A System.Uri instance that contains the address of the proxy server. 84 // 85 // BypassOnLocal: 86 // true to bypass the proxy for local addresses; otherwise, false. 87 // 88 // BypassList: 89 // An array of regular expression strings that contains the URIs of the servers 90 // to bypass. 91 public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList); 92 // 93 // Summary: 94 // Initializes a new instance of the System.Net.WebProxy class with the specified 95 // URI, bypass setting, and list of URIs to bypass. 96 // 97 // Parameters: 98 // Address: 99 // The URI of the proxy server. 100 // 101 // BypassOnLocal: 102 // true to bypass the proxy for local addresses; otherwise, false. 103 // 104 // BypassList: 105 // An array of regular expression strings that contain the URIs of the servers to 106 // bypass. 107 // 108 // Exceptions: 109 // T:System.UriFormatException: 110 // Address is an invalid URI. 111 public WebProxy(string Address, bool BypassOnLocal, string[] BypassList); 112 // 113 // Summary: 114 // Initializes a new instance of the System.Net.WebProxy class with the specified 115 // System.Uri instance, bypass setting, list of URIs to bypass, and credentials. 116 // 117 // Parameters: 118 // Address: 119 // A System.Uri instance that contains the address of the proxy server. 120 // 121 // BypassOnLocal: 122 // true to bypass the proxy for local addresses; otherwise, false. 123 // 124 // BypassList: 125 // An array of regular expression strings that contains the URIs of the servers 126 // to bypass. 127 // 128 // Credentials: 129 // An System.Net.ICredentials instance to submit to the proxy server for authentication. 130 public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials); 131 // 132 // Summary: 133 // Initializes a new instance of the System.Net.WebProxy class with the specified 134 // URI, bypass setting, list of URIs to bypass, and credentials. 135 // 136 // Parameters: 137 // Address: 138 // The URI of the proxy server. 139 // 140 // BypassOnLocal: 141 // true to bypass the proxy for local addresses; otherwise, false. 142 // 143 // BypassList: 144 // An array of regular expression strings that contains the URIs of the servers 145 // to bypass. 146 // 147 // Credentials: 148 // An System.Net.ICredentials instance to submit to the proxy server for authentication. 149 // 150 // Exceptions: 151 // T:System.UriFormatException: 152 // Address is an invalid URI. 153 public WebProxy(string Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials); 154 // 155 // Summary: 156 // Initializes an instance of the System.Net.WebProxy class using previously serialized 157 // content. 158 // 159 // Parameters: 160 // serializationInfo: 161 // The serialization data. 162 // 163 // streamingContext: 164 // The context for the serialized data. 165 protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext); 166 167 // 168 // Summary: 169 // Gets or sets the credentials to submit to the proxy server for authentication. 170 // 171 // Returns: 172 // An System.Net.ICredentials instance that contains the credentials to submit to 173 // the proxy server for authentication. 174 // 175 // Exceptions: 176 // T:System.InvalidOperationException: 177 // You attempted to set this property when the System.Net.WebProxy.UseDefaultCredentials 178 // property was set to true. 179 public ICredentials Credentials { get; set; } 180 // 181 // Summary: 182 // Gets or sets an array of addresses that do not use the proxy server. 183 // 184 // Returns: 185 // An array that contains a list of regular expressions that describe URIs that 186 // do not use the proxy server when accessed. 187 public string[] BypassList { get; set; } 188 // 189 // Summary: 190 // Gets or sets a value that indicates whether to bypass the proxy server for local 191 // addresses. 192 // 193 // Returns: 194 // true to bypass the proxy server for local addresses; otherwise, false. The default 195 // value is false. 196 public bool BypassProxyOnLocal { get; set; } 197 // 198 // Summary: 199 // Gets or sets the address of the proxy server. 200 // 201 // Returns: 202 // A System.Uri instance that contains the address of the proxy server. 203 public Uri Address { get; set; } 204 // 205 // Summary: 206 // Gets a list of addresses that do not use the proxy server. 207 // 208 // Returns: 209 // An System.Collections.ArrayList that contains a list of System.Net.WebProxy.BypassList 210 // arrays that represents URIs that do not use the proxy server when accessed. 211 public ArrayList BypassArrayList { get; } 212 // 213 // Summary: 214 // Gets or sets a System.Boolean value that controls whether the System.Net.CredentialCache.DefaultCredentials 215 // are sent with requests. 216 // 217 // Returns: 218 // true if the default credentials are used; otherwise, false. The default value 219 // is false. 220 // 221 // Exceptions: 222 // T:System.InvalidOperationException: 223 // You attempted to set this property when the System.Net.WebProxy.Credentials property 224 // contains credentials other than the default credentials. For more information, 225 // see the Remarks section. 226 public bool UseDefaultCredentials { get; set; } 227 228 // 229 // Summary: 230 // Reads the Internet Explorer nondynamic proxy settings. 231 // 232 // Returns: 233 // A System.Net.WebProxy instance that contains the nondynamic proxy settings from 234 // Internet Explorer 5.5 and later. 235 [Obsolete("This method has been deprecated. Please use the proxy selected for you by default. http://go.microsoft.com/fwlink/?linkid=14202")] 236 public static WebProxy GetDefaultProxy(); 237 // 238 // Summary: 239 // Returns the proxied URI for a request. 240 // 241 // Parameters: 242 // destination: 243 // The System.Uri instance of the requested Internet resource. 244 // 245 // Returns: 246 // The System.Uri instance of the Internet resource, if the resource is on the bypass 247 // list; otherwise, the System.Uri instance of the proxy. 248 // 249 // Exceptions: 250 // T:System.ArgumentNullException: 251 // The destination parameter is null. 252 public Uri GetProxy(Uri destination); 253 // 254 // Summary: 255 // Indicates whether to use the proxy server for the specified host. 256 // 257 // Parameters: 258 // host: 259 // The System.Uri instance of the host to check for proxy use. 260 // 261 // Returns: 262 // true if the proxy server should not be used for host; otherwise, false. 263 // 264 // Exceptions: 265 // T:System.ArgumentNullException: 266 // The host parameter is null. 267 public bool IsBypassed(Uri host); 268 // 269 // Summary: 270 // Populates a System.Runtime.Serialization.SerializationInfo with the data that 271 // is needed to serialize the target object. 272 // 273 // Parameters: 274 // serializationInfo: 275 // The System.Runtime.Serialization.SerializationInfo to populate with data. 276 // 277 // streamingContext: 278 // A System.Runtime.Serialization.StreamingContext that specifies the destination 279 // for this serialization. 280 protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext); 281 }
這里,我就不細講WebProxy了,不是不重要,做爬蟲,代理很重要,只是我們在這重點關注WebRequest。Proxy后面會大量使用,用到時再細說,就舉一個官方的例子,簡單說明使用方法:
[Code 1.2.3]
1 WebProxy proxyObject = new WebProxy("http://proxyserver:80/",true); 2 WebRequest req = WebRequest.Create("http://www.contoso.com"); 3 req.Proxy = proxyObject;
說完代理,繼續看看WebRequest還有什么東東
[Code 1.2.4]

1 public abstract class WebRequest : MarshalByRefObject, ISerializable 2 { 3 /// <summary> 4 /// 獲取或設置此請求的默認緩存策略。 5 /// </summary> 6 public static RequestCachePolicy DefaultCachePolicy { get; set; } 7 /// <summary> 8 /// 獲取或設置此請求的緩存策略。 9 /// </summary> 10 public virtual RequestCachePolicy CachePolicy { get; set; } 11 /// <summary> 12 /// 獲取或設置當前請求的模擬級別。 13 /// </summary> 14 public TokenImpersonationLevel ImpersonationLevel { get; set; } 15 /// <summary> 16 /// 當在子類中重寫時,獲取或設置請求的連接組的名稱。 17 /// </summary> 18 public virtual string ConnectionGroupName { get; set; } 19 // 20 // Summary: 21 // When overridden in a descendant class, gets or sets the collection of header 22 // name/value pairs associated with the request. 23 // 24 // Returns: 25 // A System.Net.WebHeaderCollection containing the header name/value pairs associated 26 // with this request. 27 // 28 // Exceptions: 29 // T:System.NotImplementedException: 30 // Any attempt is made to get or set the property, when the property is not overridden 31 // in a descendant class. 32 public virtual WebHeaderCollection Headers { get; set; } 33 /// <summary> 34 /// 當在子類中被重寫時,獲取或設置所發送的請求數據的內容長度。 35 /// </summary> 36 public virtual long ContentLength { get; set; } 37 /// <summary> 38 /// 當在子類中被重寫時,獲取或設置所發送的請求數據的內容類型。 39 /// </summary> 40 public virtual string ContentType { get; set; } 41 /// <summary> 42 /// 當在子類中被重寫時,獲取或設置用於對 Internet 資源請求進行身份驗證的網絡憑據。 43 /// </summary> 44 public virtual ICredentials Credentials { get; set; } 45 /// <summary> 46 /// 當在子代類中重寫時,獲取或設置一個 Boolean 值,該值控制 DefaultCredentials 是否隨請求一起發送。 47 /// </summary> 48 public virtual bool UseDefaultCredentials { get; set; } 49 /// <summary> 50 /// 當在子類中被重寫時,指示是否對請求進行預身份驗證。 51 /// </summary> 52 public virtual bool PreAuthenticate { get; set; } 53 /// <summary> 54 /// 獲取或設置請求超時之前的時間長度(以毫秒為單位)。 55 /// </summary> 56 public virtual int Timeout { get; set; } 57 /// <summary> 58 /// 獲取或設置用於此請求的身份驗證和模擬的級別。 59 /// </summary> 60 public AuthenticationLevel AuthenticationLevel { get; set; } 61 /// <summary> 62 /// 當在子類中被重寫時,獲取或設置要在此請求中使用的協議方法。 63 /// </summary> 64 public virtual string Method { get; set; } 65 /// <summary> 66 /// 當在子類中被重寫時,獲取與請求關聯的 Internet 資源的 URI。 67 /// </summary> 68 public virtual Uri RequestUri { get; } 69 70 /*************** 71 * 避免篇幅太長,此處省略部分屬性及方法。 72 * *************/ 73 74 /// <summary> 75 /// 為指定的 URI 注冊 WebRequest 子代。 76 /// </summary> 77 public static bool RegisterPrefix(string prefix, IWebRequestCreate creator); 78 /// <summary> 79 /// 中止請求。 80 /// </summary> 81 public virtual void Abort(); 82 /// <summary> 83 /// 當在子類中重寫時,提供 GetRequestStream() 方法的異步版本。 84 /// </summary> 85 public virtual IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state); 86 /// <summary> 87 /// 當在子類中被重寫時,開始對 Internet 資源的異步請求。 88 /// </summary> 89 public virtual IAsyncResult BeginGetResponse(AsyncCallback callback, object state); 90 /// <summary> 91 /// 當在子類中重寫時,返回用於將數據寫入 Internet 資源的 Stream。 92 /// </summary> 93 public virtual Stream EndGetRequestStream(IAsyncResult asyncResult); 94 /// <summary> 95 /// 當在子類中重寫時,返回 WebResponse。 96 /// </summary> 97 public virtual WebResponse EndGetResponse(IAsyncResult asyncResult); 98 /// <summary> 99 /// 當在子類中重寫時,返回用於將數據寫入 Internet 資源的 Stream。 100 /// </summary> 101 public virtual Stream GetRequestStream(); 102 /// <summary> 103 /// 當在子類中被重寫時,將用於寫入數據的 Stream 作為異步操作返回到 Internet 資源。 104 /// </summary> 105 public virtual Task<Stream> GetRequestStreamAsync(); 106 /// <summary> 107 /// 當在子類中被重寫時,返回對 Internet 請求的響應。 108 /// </summary> 109 public virtual WebResponse GetResponse(); 110 /// <summary> 111 /// 當在子類中被重寫時,將作為異步操作返回對 Internet 請求的響應。 112 /// </summary> 113 public virtual Task<WebResponse> GetResponseAsync(); 114 /// <summary> 115 /// 使用將目標對象序列化所需的數據填充 SerializationInfo。 116 /// </summary> 117 protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext); 118 }
這里列出了一摞子屬性和方法,它們都很重要,基本可說是WebRequest的核心部分,但是要各位看官干嚼,又索然無味,后面我們結合具體案例場景,到時再看,就比較美味了。這里只是加了些簡單注釋,先混個臉熟吧。
好,收工,WebRequest的解讀,先告一段落。
喜歡本系列叢書的朋友,可以點擊鏈接加入QQ交流群(994761602)【C# 破境之道】
方便各位在有疑問的時候可以及時給我個反饋。同時,也算是給各位志同道合的朋友提供一個交流的平台。
需要源碼的童鞋,也可以在群文件中獲取最新源代碼。