代理认证是爬虫的利器,现在稍微有一点规模的网站,反爬虫程序多少都会有一个点,只要程序请求的速度稍微快了一点,本地ip 就会被干掉,httpclient 作为下载网页资源的中干力量,多数爬虫都是使用httpclient 来下载,本章我们就来看一下httpclient的代理机制。
HTTP 认证
任何用户认证的过程,都需要一系列的凭证来确定用户的身份。最简单的用户凭证可以是用户名和密码这种形式。UsernamePasswordCredentials这个类可以用来表示这种情况,这种凭据包含明文的用户名和密码。这个类对于HTTP标准规范中定义的认证模式来说已经足够了。
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("username " ,"password")
使用上面的语句来设置用户名密码。
Authentication schemes 身份认证方案
AutoScheme接口表示一个抽象的面向挑战/响应的认证方案。一个认证方案要支持下面的功能:
- 客户端请求服务器受保护的资源,服务器会发送过来一个chanllenge(挑战),认证方案(Authentication scheme)需要解析、处理这个挑战
- 为processed challenge提供一些属性值:认证方案的类型,和此方案需要的一些参数,这种方案适用的范围
- 使用给定的授权信息生成授权字符串;生成http请求,用来响应服务器发送来过的授权challenge
HttpClient的船舶有几个AuthScheme 实现:
- Basic: 在RFC 2617中该验证方案定义基本身份验证机制是不安全的,因为凭据以明文形式传输。尽管它不安全,如果与TLS / SSL加密结合使用基本身份验证方案是完全足够了。
- Digest。 摘要式身份验证方案在RFC 2617摘要式身份验证方案定义比基本显著更安全,更可以为那些不想通过TLS / SSL加密全程运输安全的开销这些应用的理想选择。
凭证 provider
凭证providers旨在维护一套用户的凭证,当需要某种特定的凭证时,providers就应该能产生这种凭证。认证的具体内容包括主机名、端口号、realm name和认证方案名。当使用凭据provider的时候,我们可以很模糊的指定主机名、端口号、realm和认证方案,不用写的很精确。因为,凭据provider会根据我们指定的内容,筛选出一个最匹配的方案。只要我们自定义的凭据provider实现了CredentialsProvider这个接口,就可以在HttpClient中使用。默认的凭据provider叫做BasicCredentialsProvider,它使用java.util.HashMap对CredentialsProvider进行了简单的实现。
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
HTTP认证和执行上下文
HttpClient依赖AuthState类去跟踪认证过程中的状态的详细信息。在Http请求过程中,HttpClient创建两个AuthState实例:一个用于目标服务器认证,一个用于代理服务器认证。如果服务器或者代理服务器需要用户的授权信息,AuthScope、AutoScheme和认证信息就会被填充到两个AuthScope实例中。通过对AutoState的检测,我们可以确定请求的授权类型,确定是否有匹配的AuthScheme,确定凭据provider根据指定的授权类型是否成功生成了用户的授权信息。
在Http请求执行过程中,HttpClient会向执行上下文中添加下面的授权对象:
- Lookup对象,表示使用的认证方案。这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
- CredentialsProvider对象,表示认证方案provider,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
- AuthState对象,表示目标服务器的认证状态,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
- AuthState对象,表示代理服务器的认证状态,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
- AuthCache对象,表示认证数据的缓存,这个对象的值可以在本地上下文中进行设置,来覆盖默认值。
从版本4.1开始,HttpClient就会自动缓存验证通过的认证信息。但是为了使用这个缓存的认证信息,我们必须在同一个上下文中执行逻辑相关的请求。一旦超出该上下文的作用范围,缓存的认证信息就会失效。
抢先认证
HttpClient默认不支持抢先认证,因为一旦抢先认证被误用或者错用,会导致一系列的安全问题,比如会把用户的认证信息以明文的方式发送给未授权的第三方服务器。因此,需要用户自己根据自己应用的具体环境来评估抢先认证带来的好处和带来的风险。
即使如此,HttpClient还是允许我们通过配置来启用抢先认证,方法是提前填充认证信息缓存到上下文中,这样,以这个上下文执行的方法,就会使用抢先认证。
HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
//代理主机的ip和端口
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
//代理主机的用户名密码
new UsernamePasswordCredentials("username", "password"));
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth); //将目标的验证方案放入缓存中
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider); //上下文中添加认证凭证
context.setAuthCache(authCache); //上下文中加入认证缓存
在爬虫应用中,当使用代理的时候,我是喜欢将代理加载路由中,HttpClient 能够直接建立连接到目标主机,或者通过路由,但这会涉及多个中间连接–也被称为“一跳” 。HttpRoutePlanner是一个接口,它代表计算到基于执行上下文到给定目标完整路由策略。
如果采用的是静态代理,也就是只有ip地址,和端口号的代理,上面的代码需要在改动一下,上面的内容为云代理,需要用户名和密码验证才能登陆代理服务器,有代理服务器做地址转发
//创建代理地址实例
HttpHost proxy = new HttpHost("127.0.0.1", 8087);
//创建路由 使用DefaultProxyRoutePlanner
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
//路由添加到httpclient 实例创建中
CloseableHttpClient httpClient =HttpClients.custom().setRoutePlanner(routePlanner).build();
上面为静态代理,无用户名密码验证的代理使用方式
如果代理需要用户名和密码验证,则使用下面的方式:
HttpRoutePlanner httpRoute = new HttpRoutePlanner() {
@Override
public HttpRoute determineRoute(HttpHost target, HttpReques httpRequest, HttpContext httpContext) throws HttpException {
//可以在此处添加验证的信息 。。。
return new HttpRoute(target , httpHost);
}
};