本节主要来介绍一下,在C#中制造爬虫,最为常见、常用、实用的基础类 ------ WebRequest、WebResponse。

先来看一个示例 [1.2.1]:

     using System;
using System.IO;
using System.Net;
using System.Text; class Program
{
static void Main(string[] args)
{
var request = WebRequest.Create(@"https://www.cnblogs.com/mikecheers/p/12090487.html");
request.Method = "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
var content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
response.Close();
}
request.Abort();
Console.ReadLine();
}
}

运行以上代码,我们能看到类似如下的输出:

{省略若干html代码}
<div id="cnblogs_post_body" class="blogpost-body ">
<p>  在构建本章节内容的时候,笔者也在想一个问题,究竟什么样的采集器框架,才能算得上是一个&ldquo;全能&rdquo;的呢?就我自己以往项目经历而言,可以归纳以下几个大的分类:</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>&nbsp;</p>
<p style="margin-left: 30px;">在第一境的后面各节中,我们就来逐步实现这个小目标,同时,也来探究一下其中的原理。只有掌握了这些,才能通过继续扩展,衍生出强大的爬虫:)</p>
</div>
{省略若干html代码}

一只爬虫小幼崽儿,就如此轻松的诞生了~

示例 [1.2.1]中,最核心的部分,就是使用了本节的主题、两个基础类:WebRequest和WebResponse。

从名称上就可以看出,这两个类是针对Web资源的操作类。下面,将针对这两个类来做针对性的细究。

WebRequest

[Code 1.2.2]

     public abstract class WebRequest : MarshalByRefObject, ISerializable
{
protected WebRequest();
protected WebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext);
/***************
* 避免篇幅太长,此处省略部分属性及方法。
* *************/
public static IWebProxy DefaultWebProxy { get; set; }
public static RequestCachePolicy DefaultCachePolicy { get; set; }
public virtual IWebProxy Proxy { get; set; }
public static WebRequest Create(Uri requestUri);
public static WebRequest Create(string requestUriString);
public static WebRequest CreateDefault(Uri requestUri);
public static HttpWebRequest CreateHttp(Uri requestUri);
public static HttpWebRequest CreateHttp(string requestUriString);
public static IWebProxy GetSystemWebProxy();
}

这里列出了我们通常最为关注的几个属性和方法,当然也包括类的定义。首先,它是一个抽象类,这也意味着,它会有派生类,在.Net Framework 4.6.1中,主要包括:

[@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接口。

     public interface IWebProxy
{
ICredentials Credentials { get; set; }
Uri GetProxy(Uri destination);
bool IsBypassed(Uri host);
}

接口比较简单,目前Framework(截止4.8)派生该接口的类,也只有System.Net.WebProxy一个。

     //
// Summary:
// Contains HTTP proxy settings for the System.Net.WebRequest class.
public class WebProxy : IAutoWebProxy, IWebProxy, ISerializable
{
//
// Summary:
// Initializes an empty instance of the System.Net.WebProxy class.
public WebProxy();
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class from the specified
// System.Uri instance.
//
// Parameters:
// Address:
// A System.Uri instance that contains the address of the proxy server.
public WebProxy(Uri Address);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// URI.
//
// Parameters:
// Address:
// The URI of the proxy server.
//
// Exceptions:
// T:System.UriFormatException:
// Address is an invalid URI.
public WebProxy(string Address);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the System.Uri
// instance and bypass setting.
//
// Parameters:
// Address:
// A System.Uri instance that contains the address of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
public WebProxy(Uri Address, bool BypassOnLocal);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// host and port number.
//
// Parameters:
// Host:
// The name of the proxy host.
//
// Port:
// The port number on Host to use.
//
// Exceptions:
// T:System.UriFormatException:
// The URI formed by combining Host and Port is not a valid URI.
public WebProxy(string Host, int Port);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// URI and bypass setting.
//
// Parameters:
// Address:
// The URI of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
//
// Exceptions:
// T:System.UriFormatException:
// Address is an invalid URI.
public WebProxy(string Address, bool BypassOnLocal);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// System.Uri instance, bypass setting, and list of URIs to bypass.
//
// Parameters:
// Address:
// A System.Uri instance that contains the address of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
//
// BypassList:
// An array of regular expression strings that contains the URIs of the servers
// to bypass.
public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// URI, bypass setting, and list of URIs to bypass.
//
// Parameters:
// Address:
// The URI of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
//
// BypassList:
// An array of regular expression strings that contain the URIs of the servers to
// bypass.
//
// Exceptions:
// T:System.UriFormatException:
// Address is an invalid URI.
public WebProxy(string Address, bool BypassOnLocal, string[] BypassList);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// System.Uri instance, bypass setting, list of URIs to bypass, and credentials.
//
// Parameters:
// Address:
// A System.Uri instance that contains the address of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
//
// BypassList:
// An array of regular expression strings that contains the URIs of the servers
// to bypass.
//
// Credentials:
// An System.Net.ICredentials instance to submit to the proxy server for authentication.
public WebProxy(Uri Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials);
//
// Summary:
// Initializes a new instance of the System.Net.WebProxy class with the specified
// URI, bypass setting, list of URIs to bypass, and credentials.
//
// Parameters:
// Address:
// The URI of the proxy server.
//
// BypassOnLocal:
// true to bypass the proxy for local addresses; otherwise, false.
//
// BypassList:
// An array of regular expression strings that contains the URIs of the servers
// to bypass.
//
// Credentials:
// An System.Net.ICredentials instance to submit to the proxy server for authentication.
//
// Exceptions:
// T:System.UriFormatException:
// Address is an invalid URI.
public WebProxy(string Address, bool BypassOnLocal, string[] BypassList, ICredentials Credentials);
//
// Summary:
// Initializes an instance of the System.Net.WebProxy class using previously serialized
// content.
//
// Parameters:
// serializationInfo:
// The serialization data.
//
// streamingContext:
// The context for the serialized data.
protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext); //
// Summary:
// Gets or sets the credentials to submit to the proxy server for authentication.
//
// Returns:
// An System.Net.ICredentials instance that contains the credentials to submit to
// the proxy server for authentication.
//
// Exceptions:
// T:System.InvalidOperationException:
// You attempted to set this property when the System.Net.WebProxy.UseDefaultCredentials
// property was set to true.
public ICredentials Credentials { get; set; }
//
// Summary:
// Gets or sets an array of addresses that do not use the proxy server.
//
// Returns:
// An array that contains a list of regular expressions that describe URIs that
// do not use the proxy server when accessed.
public string[] BypassList { get; set; }
//
// Summary:
// Gets or sets a value that indicates whether to bypass the proxy server for local
// addresses.
//
// Returns:
// true to bypass the proxy server for local addresses; otherwise, false. The default
// value is false.
public bool BypassProxyOnLocal { get; set; }
//
// Summary:
// Gets or sets the address of the proxy server.
//
// Returns:
// A System.Uri instance that contains the address of the proxy server.
public Uri Address { get; set; }
//
// Summary:
// Gets a list of addresses that do not use the proxy server.
//
// Returns:
// An System.Collections.ArrayList that contains a list of System.Net.WebProxy.BypassList
// arrays that represents URIs that do not use the proxy server when accessed.
public ArrayList BypassArrayList { get; }
//
// Summary:
// Gets or sets a System.Boolean value that controls whether the System.Net.CredentialCache.DefaultCredentials
// are sent with requests.
//
// Returns:
// true if the default credentials are used; otherwise, false. The default value
// is false.
//
// Exceptions:
// T:System.InvalidOperationException:
// You attempted to set this property when the System.Net.WebProxy.Credentials property
// contains credentials other than the default credentials. For more information,
// see the Remarks section.
public bool UseDefaultCredentials { get; set; } //
// Summary:
// Reads the Internet Explorer nondynamic proxy settings.
//
// Returns:
// A System.Net.WebProxy instance that contains the nondynamic proxy settings from
// Internet Explorer 5.5 and later.
[Obsolete("This method has been deprecated. Please use the proxy selected for you by default. http://go.microsoft.com/fwlink/?linkid=14202")]
public static WebProxy GetDefaultProxy();
//
// Summary:
// Returns the proxied URI for a request.
//
// Parameters:
// destination:
// The System.Uri instance of the requested Internet resource.
//
// Returns:
// The System.Uri instance of the Internet resource, if the resource is on the bypass
// list; otherwise, the System.Uri instance of the proxy.
//
// Exceptions:
// T:System.ArgumentNullException:
// The destination parameter is null.
public Uri GetProxy(Uri destination);
//
// Summary:
// Indicates whether to use the proxy server for the specified host.
//
// Parameters:
// host:
// The System.Uri instance of the host to check for proxy use.
//
// Returns:
// true if the proxy server should not be used for host; otherwise, false.
//
// Exceptions:
// T:System.ArgumentNullException:
// The host parameter is null.
public bool IsBypassed(Uri host);
//
// Summary:
// Populates a System.Runtime.Serialization.SerializationInfo with the data that
// is needed to serialize the target object.
//
// Parameters:
// serializationInfo:
// The System.Runtime.Serialization.SerializationInfo to populate with data.
//
// streamingContext:
// A System.Runtime.Serialization.StreamingContext that specifies the destination
// for this serialization.
protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext);
}

System.Net.WebProxy

这里,我就不细讲WebProxy了,不是不重要,做爬虫,代理很重要,只是我们在这重点关注WebRequest。Proxy后面会大量使用,用到时再细说,就举一个官方的例子,简单说明使用方法:

[Code 1.2.3]

 WebProxy proxyObject = new WebProxy("http://proxyserver:80/",true);
WebRequest req = WebRequest.Create("http://www.contoso.com");
req.Proxy = proxyObject;

点击查看来源

说完代理,继续看看WebRequest还有什么东东

[Code 1.2.4]

     public abstract class WebRequest : MarshalByRefObject, ISerializable
{
/// <summary>
/// 获取或设置此请求的默认缓存策略。
/// </summary>
public static RequestCachePolicy DefaultCachePolicy { get; set; }
/// <summary>
/// 获取或设置此请求的缓存策略。
/// </summary>
public virtual RequestCachePolicy CachePolicy { get; set; }
/// <summary>
/// 获取或设置当前请求的模拟级别。
/// </summary>
public TokenImpersonationLevel ImpersonationLevel { get; set; }
/// <summary>
/// 当在子类中重写时,获取或设置请求的连接组的名称。
/// </summary>
public virtual string ConnectionGroupName { get; set; }
//
// Summary:
// When overridden in a descendant class, gets or sets the collection of header
// name/value pairs associated with the request.
//
// Returns:
// A System.Net.WebHeaderCollection containing the header name/value pairs associated
// with this request.
//
// Exceptions:
// T:System.NotImplementedException:
// Any attempt is made to get or set the property, when the property is not overridden
// in a descendant class.
public virtual WebHeaderCollection Headers { get; set; }
/// <summary>
/// 当在子类中被重写时,获取或设置所发送的请求数据的内容长度。
/// </summary>
public virtual long ContentLength { get; set; }
/// <summary>
/// 当在子类中被重写时,获取或设置所发送的请求数据的内容类型。
/// </summary>
public virtual string ContentType { get; set; }
/// <summary>
/// 当在子类中被重写时,获取或设置用于对 Internet 资源请求进行身份验证的网络凭据。
/// </summary>
public virtual ICredentials Credentials { get; set; }
/// <summary>
/// 当在子代类中重写时,获取或设置一个 Boolean 值,该值控制 DefaultCredentials 是否随请求一起发送。
/// </summary>
public virtual bool UseDefaultCredentials { get; set; }
/// <summary>
/// 当在子类中被重写时,指示是否对请求进行预身份验证。
/// </summary>
public virtual bool PreAuthenticate { get; set; }
/// <summary>
/// 获取或设置请求超时之前的时间长度(以毫秒为单位)。
/// </summary>
public virtual int Timeout { get; set; }
/// <summary>
/// 获取或设置用于此请求的身份验证和模拟的级别。
/// </summary>
public AuthenticationLevel AuthenticationLevel { get; set; }
/// <summary>
/// 当在子类中被重写时,获取或设置要在此请求中使用的协议方法。
/// </summary>
public virtual string Method { get; set; }
/// <summary>
/// 当在子类中被重写时,获取与请求关联的 Internet 资源的 URI。
/// </summary>
public virtual Uri RequestUri { get; } /***************
* 避免篇幅太长,此处省略部分属性及方法。
* *************/ /// <summary>
/// 为指定的 URI 注册 WebRequest 子代。
/// </summary>
public static bool RegisterPrefix(string prefix, IWebRequestCreate creator);
/// <summary>
/// 中止请求。
/// </summary>
public virtual void Abort();
/// <summary>
/// 当在子类中重写时,提供 GetRequestStream() 方法的异步版本。
/// </summary>
public virtual IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state);
/// <summary>
/// 当在子类中被重写时,开始对 Internet 资源的异步请求。
/// </summary>
public virtual IAsyncResult BeginGetResponse(AsyncCallback callback, object state);
/// <summary>
/// 当在子类中重写时,返回用于将数据写入 Internet 资源的 Stream。
/// </summary>
public virtual Stream EndGetRequestStream(IAsyncResult asyncResult);
/// <summary>
/// 当在子类中重写时,返回 WebResponse。
/// </summary>
public virtual WebResponse EndGetResponse(IAsyncResult asyncResult);
/// <summary>
/// 当在子类中重写时,返回用于将数据写入 Internet 资源的 Stream。
/// </summary>
public virtual Stream GetRequestStream();
/// <summary>
/// 当在子类中被重写时,将用于写入数据的 Stream 作为异步操作返回到 Internet 资源。
/// </summary>
public virtual Task<Stream> GetRequestStreamAsync();
/// <summary>
/// 当在子类中被重写时,返回对 Internet 请求的响应。
/// </summary>
public virtual WebResponse GetResponse();
/// <summary>
/// 当在子类中被重写时,将作为异步操作返回对 Internet 请求的响应。
/// </summary>
public virtual Task<WebResponse> GetResponseAsync();
/// <summary>
/// 使用将目标对象序列化所需的数据填充 SerializationInfo。
/// </summary>
protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext);
}

System.Net.WebRequest 重要的属性和方法,但这里不细究。

这里列出了一摞子属性和方法,它们都很重要,基本可说是WebRequest的核心部分,但是要各位看官干嚼,又索然无味,后面我们结合具体案例场景,到时再看,就比较美味了。这里只是加了些简单注释,先混个脸熟吧。

好,收工,WebRequest的解读,先告一段落。

喜欢本系列丛书的朋友,可以点击链接加入QQ交流群(994761602)【C# 破境之道】
方便各位在有疑问的时候可以及时给我个反馈。同时,也算是给各位志同道合的朋友提供一个交流的平台。
需要源码的童鞋,也可以在群文件中获取最新源代码。

《C# 爬虫 破境之道》:第一境 爬虫原理 — 第二节:WebRequest的更多相关文章

  1. 网络爬虫入门:你的第一个爬虫项目(requests库)

    0.采用requests库 虽然urllib库应用也很广泛,而且作为Python自带的库无需安装,但是大部分的现在python爬虫都应用requests库来处理复杂的http请求.requests库语 ...

  2. Python爬虫实践 -- 记录我的第一只爬虫

    一.环境配置 1. 下载安装 python3 .(或者安装 Anaconda) 2. 安装requests和lxml 进入到 pip 目录,CMD --> C:\Python\Scripts,输 ...

  3. 《C# 爬虫 破境之道》:第二境 爬虫应用 — 第一节:HTTP协议数据采集

    首先欢迎您来到本书的第二境,本境,我们将全力打造一个实际生产环境可用的爬虫应用了.虽然只是刚开始,虽然路漫漫其修远,不过还是有点小鸡冻:P 本境打算针对几大派生类做进一步深耕,包括与应用的结合.对比它 ...

  4. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第六节:第一境尾声

    在第一境中,我们主要了解了爬虫的一些基本原理,说原理也行,说基础知识也罢,结果就是已经知道一个小爬虫是如何诞生的了~那么现在,请默默回想一下,在第一境中,您都掌握了哪些内容?哪些还比较模糊?如果还有什 ...

  5. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第五节:数据流处理的那些事儿

    为什么说到数据流了呢,因为上一节中介绍了一下异步发送请求.同样,在数据流的处理上,C#也为我们提供几个有用的异步处理方法.而且,爬虫这生物,处理数据流是基础本能,比较重要.本着这个原则,就聊一聊吧. ...

  6. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第一节:整体思路

    在构建本章节内容的时候,笔者也在想一个问题,究竟什么样的采集器框架,才能算得上是一个“全能”的呢?就我自己以往项目经历而言,可以归纳以下几个大的分类: 根据通讯协议:HTTP的.HTTPS的.TCP的 ...

  7. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第四节:同步与异步请求方式

    前两节,我们对WebRequest和WebResponse这两个类做了介绍,但两者还相对独立.本节,我们来说说如何将两者结合起来,方式有哪些,有什么不同. 1.4.1 说结合,无非就是我们如何发送一个 ...

  8. 《C# 爬虫 破境之道》:第一境 爬虫原理 — 第三节:WebResponse

    第二节中,我们介绍了WebRequest,它可以帮助我们发送一个请求,不过正所谓“来而不往非礼也”,对方收到我们的请求,不给点回复,貌似不太合适(不过,还真有脸皮厚的:P). 接下来,就重点研究一下, ...

  9. 《C# 爬虫 破境之道》:概述

    第一节:写作本书的目的 关于笔者 张晓亭(Mike Cheers),1982年出生,内蒙古辽阔的大草原是我的故乡. 没有高学历,没有侃侃而谈的高谈阔论,拥有的就是那一份对技术的执著,对自我价值的追求. ...

随机推荐

  1. 用Xshell连接谷歌云

    谷歌云服务器,默认用浏览器进行SSH链接,而且也不告知密码.以Centos为例,先使用浏览器连接 1,给root修改密码 1 sudo passwd root 2,编辑ssh配置文件 sudo nan ...

  2. centos虚拟机Ping不通网关

    centos虚拟机Ping不通网关 今天在VMware中安装了centos mini版本,安装完成后,用xshell连接一直连不上,本来以为是mini版本没有安装ssh server,于是就用命令: ...

  3. C# 匹配可空变量

    在 C# 7.0 的时候提供更好用的模式匹配方法,支持通过 is 直接转换对应的类,但是如果是尝试转换可空的对象,那么将会提示无法编译,或转换失败 在 C# 7.0 的 is 转换是十分好用的功能,例 ...

  4. CodeForces Goodbye 2017

    传送门 A - New Year and Counting Cards •题意 有n张牌,正面有字母,反面有数字 其中元音字母$a,e,o,i,u$的另一面必须对应$0,2,4,6,8$的偶数 其他字 ...

  5. CountDownLatch 部分加载和同时并发业务。

    按顺序部分加载: import java.util.concurrent.CountDownLatch; /** * @Title: ThreadCountDownTest.java * @Descr ...

  6. JQuery多个异步操作后执行(resolve,promise,when,done)

    代码分享: //3秒后完成 function asyncThing1() { var dfd = $.Deferred(); setTimeout(function () { alert('async ...

  7. 【Linux】centos查看防火墙是否关闭

    查看防火墙的状态的命令为: sudo systemctl status firewalld 打开防火墙的方式有两种,一种是打开后重启会恢复回原来的状态,命令为: sudo systemctl star ...

  8. 【Linux】grep笔记

    Linux grep命令用于查找文件里符合条件的字符串. 参数: -a 或 --text : 不要忽略二进制的数据. -A<显示行数> 或 --after-context=<显示行数 ...

  9. docker(整理中

    docker镜像默认的下载地址就是docker的官网,而他们的官网在国内没有节点,时不时就被国家防火墙隔绝,会出现DNS解析不到,或者找不到镜像等狗血提示. 解决的方法有三个: 第一,就是不断尝试,因 ...

  10. Visio数据视觉化处理

    形状数据的查看的两种方式 定义形状数据:右键单击数据窗口 打勾的代表可以显示 其他没有打勾的就必须要在开发模式才会显示出来 开发模式就是打开开发工具面板 注意其中类型的设置 类型与格式是一一对应的 不 ...