c httpclient抓取网页(一下-Cookie的header信息解析信息的制作 )

优采云 发布时间: 2021-10-21 04:02

  c httpclient抓取网页(一下-Cookie的header信息解析信息的制作

)

  使用HttpClient抓取某些网页时,往往会保留服务器发回的cookie信息,以便发起其他需要这些cookie的请求。在大多数情况下,我们使用内置的 cookie 策略来轻松、直接地获取这些 cookie。

  下面一小段代码就是访问和获取对应的cookie:

  @Test

public void getCookie(){

CloseableHttpClient httpClient = HttpClients.createDefault();

HttpGet get=new HttpGet("http://www.baidu.com");

HttpClientContext context = HttpClientContext.create();

try {

CloseableHttpResponse response = httpClient.execute(get, context);

try{

System.out.println(">>>>>>headers:");

Arrays.stream(response.getAllHeaders()).forEach(System.out::println);

System.out.println(">>>>>>cookies:");

context.getCookieStore().getCookies().forEach(System.out::println);

}

finally {

response.close();

}

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

httpClient.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

  打印结果

  >>>>>>headers:

Server: bfe/1.0.8.18

Date: Tue, 12 Sep 2017 06:19:06 GMT

Content-Type: text/html

Last-Modified: Mon, 23 Jan 2017 13:28:24 GMT

Transfer-Encoding: chunked

Connection: Keep-Alive

Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform

Pragma: no-cache

Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/

>>>>>>cookies:

[version: 0][name: BDORZ][value: 27315][domain: baidu.com][path: /][expiry: null]

  但是,有一些网站返回的cookies不一定符合规范。比如下面这个例子,从打印的header可以看出,这个cookie中的Expires属性是时间戳的形式,不符合标准时间。因此httpclient处理cookie失败,最终无法获取cookie,并发出警告信息:“Invalid'expires'属性:1505204523”

  警告: Invalid cookie header: "Set-Cookie: yd_cookie=90236a64-8650-494b332a285dbd886e5981965fc4a93f023d; Expires=1505204523; Path=/; HttpOnly". Invalid 'expires' attribute: 1505204523

>>>>>>headers:

Date: Tue, 12 Sep 2017 06:22:03 GMT

Content-Type: text/html

Connection: keep-alive

Set-Cookie: yd_cookie=90236a64-8650-494b332a285dbd886e5981965fc4a93f023d; Expires=1505204523; Path=/; HttpOnly

Cache-Control: no-cache, no-store

Server: WAF/2.4-12.1

>>>>>>cookies:

  虽然我们可以使用header数据来重构cookie,其实很多人都是这么做的,但是这种方法不够优雅,那么如何解决这个问题呢?网上相关资料很少,只能从官方文档入手。在官方文档3.4节自定义cookie策略中,我们谈到了允许自定义的cookie策略。自定义的方法是实现CookieSpec接口,使用CookieSpecProvider在httpclient中完成策略实例的初始化和注册。好吧,关键线索就在于CookieSpec接口,我们来看看它的源码:

  public interface CookieSpec {

……

/**

* Parse the {@code "Set-Cookie"} Header into an array of Cookies.

*

* <p>This method will not perform the validation of the resultant

* {@link Cookie}s

*

* @see #validate

*

* @param header the {@code Set-Cookie} received from the server

* @param origin details of the cookie origin

* @return an array of {@code Cookie}s parsed from the header

* @throws MalformedCookieException if an exception occurs during parsing

*/

List parse(Header header, CookieOrigin origin) throws MalformedCookieException;

……

}</p>

  在源码中,我们找到了一个解析方法。看评论,我们知道就是这个方法。Set-Cookie 的头部信息被解析为 Cookie 对象。自然而然,我们就会了解 httplcient 中 DefaultCookieSpec 的默认实现。限于篇幅,源码就不贴出来了。在默认实现中,DefaultCookieSpec 的主要工作是确定头部中 Cookie 规范的类型,然后调用具体的实现。像上面这样的 cookie 最终交给 NetscapeDraftSpec 的实例进行分析。在 NetscapeDraftSpec 的源代码中,默认的过期时间格式定义为“EEE,dd-MMM-yy HH:mm:ss z”

  public class NetscapeDraftSpec extends CookieSpecBase {

protected static final String EXPIRES_PATTERN = "EEE, dd-MMM-yy HH:mm:ss z";

/** Default constructor */

public NetscapeDraftSpec(final String[] datepatterns) {

super(new BasicPathHandler(),

new NetscapeDomainHandler(),

new BasicSecureHandler(),

new BasicCommentHandler(),

new BasicExpiresHandler(

datepatterns != null ? datepatterns.clone() : new String[]{EXPIRES_PATTERN}));

}

NetscapeDraftSpec(final CommonCookieAttributeHandler... handlers) {

super(handlers);

}

public NetscapeDraftSpec() {

this((String[]) null);

}

……

}

  至此,就清楚了,我们只需要将Cookie中的expires time转换成正确的格式,然后发送给默认的解析器即可。

  解决方案:

  自定义一个 CookieSpec 类,继承 DefaultCookieSpec,重写解析器方法,将 Cookie 中的 expires 转换成正确的时间格式,调用默认的解析方法

  实现如下(网址不公开,已隐藏)

  public class TestHttpClient {

String url = sth;

class MyCookieSpec extends DefaultCookieSpec {

@Override

public List parse(Header header, CookieOrigin cookieOrigin) throws MalformedCookieException {

String value = header.getValue();

String prefix = "Expires=";

if (value.contains(prefix)) {

String expires = value.substring(value.indexOf(prefix) + prefix.length());

expires = expires.substring(0, expires.indexOf(";"));

String date = DateUtils.formatDate(new Date(Long.parseLong(expires) * 1000L),"EEE, dd-MMM-yy HH:mm:ss z");

value = value.replaceAll(prefix + "\\d{10};", prefix + date + ";");

}

header = new BasicHeader(header.getName(), value);

return super.parse(header, cookieOrigin);

}

}

@Test

public void getCookie() {

CloseableHttpClient httpClient = HttpClients.createDefault();

Registry cookieSpecProviderRegistry = RegistryBuilder.create()

.register("myCookieSpec", context -> new MyCookieSpec()).build();//注册自定义CookieSpec

HttpClientContext context = HttpClientContext.create();

context.setCookieSpecRegistry(cookieSpecProviderRegistry);

HttpGet get = new HttpGet(url);

get.setConfig(RequestConfig.custom().setCookieSpec("myCookieSpec").build());

try {

CloseableHttpResponse response = httpClient.execute(get, context);

try{

System.out.println(">>>>>>headers:");

Arrays.stream(response.getAllHeaders()).forEach(System.out::println);

System.out.println(">>>>>>cookies:");

context.getCookieStore().getCookies().forEach(System.out::println);

}

finally {

response.close();

}

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

httpClient.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

  再次运行,顺利打印出正确的结果,完美!

  >>>>>>headers:

Date: Tue, 12 Sep 2017 07:24:10 GMT

Content-Type: text/html

Connection: keep-alive

Set-Cookie: yd_cookie=9f521fc5-0248-4ab3ee650ca50b1c7abb1cd2526b830e620f; Expires=1505208250; Path=/; HttpOnly

Cache-Control: no-cache, no-store

Server: WAF/2.4-12.1

>>>>>>cookies:

[version: 0][name: yd_cookie][value: 9f521fc5-0248-4ab3ee650ca50b1c7abb1cd2526b830e620f][domain: www.sth.com][path: /][expiry: Tue Sep 12 17:24:10 CST 2017]

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线