1. 程式人生 > >HttpClient 獲取 Cookie 的一次踩坑實錄

HttpClient 獲取 Cookie 的一次踩坑實錄

在使用HttpClient進行抓取一些網頁的時候,經常會保留從伺服器端發回的Cookie資訊,以便發起其他需要這些Cookie的請求。大多數情況下,我們使用內建的cookie策略,便能夠方便直接地獲取這些cookie。

1234567891011121314151617181920212223242526 @TestpublicvoidgetCookie(){CloseableHttpClient httpClient=HttpClients.createDefault();HttpGet get=newHttpGet("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(IOExceptione){e.printStackTrace();}finally{try{httpClient.close();}catch(IOExceptione){e.printStackTrace();}}}

列印結果

123456789101112 >>>>>>headers:Server:bfe/1.0.8.18Date:Tue,12Sep201706:19:06GMTContent-Type:text/htmlLast-Modified:Mon,23Jan201713:28:24GMTTransfer-Encoding:chunkedConnection:Keep-AliveCache-Control:private,no-cache,no-store,proxy-revalidate,no-transformPragma:no-cacheSet-Cookie:BDORZ=27315;max-age=86400;domain=.baidu.com;path=/>>>>>>cookies:[version:0][name:BDORZ][value:27315][domain:baidu.com][path:/][expiry:null]

但是也有一些網站返回的cookie並不一定完全符合規範,例如下面這個例子,從打印出的header中可以看到,這個cookie中的Expires屬性是時間戳形式,並不符合標準的時間格式,因此,httpclient對於cookie的處理失效,最終無法獲取到cookie,並且發出了一條警告資訊:“Invalid ‘expires’ attribute: 1505204523”

123456789 警告:Invalid cookie header:"Set-Cookie: yd_cookie=90236a64-8650-494b332a285dbd886e5981965fc4a93f023d; Expires=1505204523; Path=/; HttpOnly".Invalid'expires'attribute:1505204523>>>>>>headers:Date:Tue,12Sep201706:22:03GMTContent-Type:text/htmlConnection:keep-aliveSet-Cookie:yd_cookie=90236a64-8650-494b332a285dbd886e5981965fc4a93f023d;Expires=1505204523;Path=/;HttpOnlyCache-Control:no-cache,no-storeServer:WAF/2.4-12.1>>>>>>cookies:

雖然我們可以利用header的資料,重新構造一個cookie出來,也有很多人確實也是這麼做的,但這種方法不夠優雅,那麼如何解決這個問題?網上相關的資料又很少,所以就只能先從官方文件入手。在官方文件3.4小節custom cookie policy中講到允許自定義的cookie策略,自定義的方法是實現CookieSpec介面,並通過CookieSpecProvider來完成在httpclient中的初始化和註冊策略例項的工作。好了,關鍵的線索在於CookieSpec介面,我們來看一下它的原始碼:

123456789101112131415161718 publicinterfaceCookieSpec{……/**      * Parse the {@ code "Set-Cookie"} Header into an array of Cookies.      *      * <p> This method will not perform the validation of the resultant<code>      * {@link Cookie}s</p>      *      * @ 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;……}

在原始碼中我們發現了一個parse方法,看註釋就知道正是這個方法,將Set-Cookie的header資訊解析為Cookie物件,自然地再瞭解一下在httplcient中的預設實現DefaultCookieSpec,限於篇幅,原始碼就不貼了。在預設的實現中,DefaultCookieSpec主要的工作是判斷header中Cookie規範的型別,然後再呼叫具體的某一個實現。像上述這種Cookie,最終是交由NetscapeDraftSpec的例項來做解析,而在NetscapeDraftSpec的原始碼中,定義了預設的expires時間格式為“EEE, dd-MMM-yy HH:mm:ss z”

1234567891011121314151617181920212223 publicclassNetscapeDraftSpecextendsCookieSpecBase{protectedstaticfinalStringEXPIRES_PATTERN="EEE, dd-MMM-yy HH:mm:ss z";/** Default constructor */publicNetscapeDraftSpec(finalString[]datepatterns){super(newBasicPathHandler(),newNetscapeDomainHandler(),newBasicSecureHandler(),newBasicCommentHandler(),newBasicExpiresHandler(datepatterns!=null?datepatterns.clone():newString[]{EXPIRES_PATTERN}));}NetscapeDraftSpec(finalCommonCookieAttributeHandler...handlers){super(handlers);}publicNetscapeDraftSpec(){this((String[])null);}……}

到這裡已經比較清楚了,我們只需要將Cookie中expires的時間轉換為正確的格式,然後再送入預設的解析器就可以了。

解決方法:

  1. 自定義一個CookieSpec類,繼承DefaultCookieSpec
  2. 重寫parser方法
  3. 將Cookie中的expires轉換為正確的時間格式
  4. 呼叫預設的解析方法

實現如下(URL就不公開了,已經隱去)

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 publicclassTestHttpClient{Stringurl=sth;classMyCookieSpecextendsDefaultCookieSpec{@OverridepublicList parse(Header header,CookieOrigin cookieOrigin)throwsMalformedCookieException{Stringvalue=header.getValue();Stringprefix="Expires=";if(value.contains(prefix)){Stringexpires=value.substring(value.indexOf(prefix)+prefix.length());expires=expires.substring(0,expires.indexOf(";"));Stringdate=DateUtils.formatDate(newDate(Long.parseLong(expires)*1000L),"EEE, dd-MMM-yy HH:mm:ss z");value=value.replaceAll(prefix+"\\d{10};",prefix+date+";");}header=newBasicHeader(header.getName(),value);returnsuper.parse(header,cookieOrigin);}}@TestpublicvoidgetCookie(){CloseableHttpClient httpClient=HttpClients.createDefault();Registry cookieSpecProviderRegistry=RegistryBuilder.create().register("myCookieSpec",context->newMyCookieSpec()).build();//註冊自定義CookieSpecHttpClientContext context=HttpClientContext.create();context.setCookieSpecRegistry(cookieSpecProviderRegistry);HttpGet get=newHttpGet(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);

相關推薦

HttpClient 獲取 Cookie實錄

在使用HttpClient進行抓取一些網頁的時候,經常會保留從伺服器端發回的Cookie資訊,以便發起其他需要這些Cookie的請求。大多數情況下,我們使用內建的cookie策略,便能夠方便直接地獲取這些cookie。

:使用Navicat連線Mysql8.0.11

  MySQL8.0正式版8.0.11已釋出,官方表示MySQL8要比MySQL5.7塊兩倍,同事還帶來了大量的改進和更快的效能!   從MySQL5.7升級到MySQL8.0僅支援通過使用in-place方式進行升級,並且不支援從MySQL8.0降級到MySQ

mac sed 使用實錄

其中 core 擴展 rep gre pan pre user 後來 [轉自別處] 比如我sed想做文件原地的替換,但是怎麽寫都出錯,錯誤提示還莫名其妙,後來多方搜索才知道Mac上的sed如果參數有-i就必須加上備份指令,即-i後添加任意字符,那些字符就作為備份文件的後綴

Spring Boot 開發系列 開發

strong contex configure post crud操作 如何 result png exce 這是學習spring boot 的第二周,公司號稱這玩意是啥都不會的新手就可以填空開發,於是決定上手一把,怎麽說我也是搞了快七八年的.NET和.NETcore,沒想

JAVA實用案例之文件導出(JasperReport實錄

十分 bytearray message remove 除了 只需要 老婆 不同 內存泄露問題 寫在最前面 想想來新公司也快五個月了,恍惚一瞬間。 翻了翻博客,因為太忙,也有將近五個多月沒認真總結過了。 正好趁著今天老婆出門團建的機會,記錄下最近這段時間遇到的大坑-J

ffmpeg 實錄(二)

時間 如果 .com http 一個 要求 test 兩個 效率問題 一、背景介紹 最近領導要求做一個視頻錄制的相關項目。其中,需要對視頻文件進行添加 實時時間水印。於是,我想到了使用之前的ffmpeg來做。 二、ffmpeg實際操作 首先把需要添

android 人臉識別實錄

隨著AI技術的發展,人臉識別的應用場景越來越多,提供技術支援的API也有好多可以選擇,但是大部分都是需要收費的,或者免費試用。由此可見人臉識別演算法確實是核心技術,不是隨便就可以獲取到的。經過多次嘗試,記錄一下自己在實現人臉識別遇到的坑吧。 也有免費的Api打著人臉識別的旗號,其實都是實現人臉檢

mysql獲取最後插入的id

mysql獲取最後一次插入的id: python程式碼示例: conn = getmysqlconn(connstr, use_pool) try: cursorclass = conn.cursor(cursor=pymysql.cursors.Cursor) with c

Angular2,Springboot,Zuul,Shiro跨域CORS請求實錄

前言:前後端分離,業務分離,閘道器路由等已經成為當下web application開發的流行趨勢。前端以單頁面路由為核心的框架為主體,可以單獨部署在nodejs或nginx上。後端以springboot為代表的分散式微服務框架為主體,可以獨立執行在任何埠上。相互通過符合restful規範的介面訪問或資料交換。

微信小程式 實錄

最近幾個月工作飽和度較高,寫了一些小程式相關資料都在公司內網wiki中。 正好有朋友想做小程式相關,想看些資料。趁著 這個契機,把文章發出來給大家分享一下。 文章簡陋,勿噴。 開發前 需要與產品商榷的頁面細節 序號

,mongodb無法啟動報錯 reason: errno:111 Connection refused

localhost.localdomain polkitd[686]: Unregistered Authentication Agent for unix-process:7798:588850 (system bus name :1.165, object path /org/freedesktop/P

Win10環境下安裝pytorch實錄

由於學習需要,筆者開始在win10下使用pip安裝pytorch,以學習此框架(為何不用linux?懶) 廢話不說,直接上乾貨 pip install pytorch torchvision 這個是安裝命令,不用多說 下面看看遇到的問題: pip指令不對 這個

,在雲伺服器上安裝jupyter並遠端使用

先說我經歷了什麼吧,首先是py3的坑 ## py3安裝的坑 首先我們拿到雲主機,連線上去,過程略 通過python看到預設版本為py2.7,不支援最新的jupyter 之前安裝的python3在過一陣之後會莫名其妙的崩潰,首先要安裝python的依賴 yum -y

MySql驅動8.0.12版本實錄

剛換了一家新公司,在公司電腦上搭建了新的開發環境。由於貪新鮮,在本地裝了mysql 8.0.12版本,然後匯入專案啟動的時候出現報錯資訊如下: Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionExcept

Docker實錄

1.docker啟動mysql容器 docker run --name mysql -p 3306:3306 -v /root/mysql/config/mysql.cnf:/etc/mysql/mysql.cnf -v /root/mysql/data/temp:/var

如何在30秒內建構Spark環境--使用docker-compose 實錄

如何在30秒內建構PySpark+Jupyter環境--使用docker-compose 踩坑實錄 前言 使用步驟 1. 下載這個專案 2. 進入專案的根目錄 3. 創造並執行PySpark+Jupyter的容器 4. 檢視

CentOS下Nvidia docker 2.0之安裝教程&實錄

CentOS下Nvidia docker 2.0之安裝教程&踩坑實錄 前言 要求 1. GNU/Linux x86_64 with kernel version > 3.10 2. Docker >= 1.12

CentOS 7下最新版Docker CE之無腦安裝教程&實錄

CentOS 7下最新版Docker CE之無腦安裝教程&踩坑實錄 前言 Docker CE版本概述 系統要求 安裝方式 安裝步驟 解除安裝舊版的Docker及Docker Engine 設定倉庫

weex+vue2.x 實錄(不定期更新)

sta 為什麽 第一個 new 新手 runtime 顯示空白 rom mod 執行 npm start 顯示空白頁面 這個是開始使用weex就出現的一個大坑,說實話對新手真的很不友好。 1、打開控制臺顯示:Cannot assign to read only pr

python xgboost實錄

python xgboost踩坑實錄 前言 載入模型 載入資料 參考連結 前言 在python下執行xgboost有許多要注意的地方。 筆者在載入模型及載入資料的時候都踩了坑,為了避免再度踩坑,所以將解法記錄於此。 載入模型