我在使用scrapy模擬登錄新浪微博時,想將登錄成功后的cookies保存到本地,下次加載它實現直接登錄,省去中間一系列的請求和POST等。關於如何從本次請求中獲取並在下次請求中附帶上cookies的方法,官方文檔已經有很好的說明,網上也有很多相關的資料,但是將cookies存儲到文件和從文件加載cookies卻未找到相關的說明,只好自己折騰了,經過一番嘗試,總算是實現了該功能,方法記錄如下。
簡單分析scrapy的cookies
查看scrapy與cookies有關的源碼,在http/cookies.py文件中,下面是部分代碼:
import time
from cookielib import CookieJar as _CookieJar, DefaultCookiePolicy, IPV4_RE
from scrapy.utils.httpobj import urlparse_cached
class CookieJar(object):
def __init__(self, policy=None, check_expired_frequency=10000):
self.policy = policy or DefaultCookiePolicy()
self.jar = _CookieJar(self.policy)
self.jar._cookies_lock = _DummyLock()
self.check_expired_frequency = check_expired_frequency
self.processed = 0
def extract_cookies(self, response, request):
wreq = WrappedRequest(request)
wrsp = WrappedResponse(response)
return self.jar.extract_cookies(wrsp, wreq)
@property
def _cookies(self):
return self.jar._cookies
def set_cookie(self, cookie):
self.jar.set_cookie(cookie)
可以看出scrapy的CookieJar
類基本上是通過cookielib的CookieJar
類來實現的相應功能。
獲取cookies
將meta
的cookie_jar
設置為CookieJar
對象,通過response.meta['cookie_jar']
即可獲得cookies的內容。
from scrapy.http.cookies import CookieJar
cookie_jar = CookieJar()
return [scrapy.FormRequest(url="http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)",
formdata=self.login_data,
meta = {'dont_merge_cookies': True, 'cookiejar': cookie_jar},#首次請求
callback=self.after_post)]
......
#之后的每次請求通過傳遞這個CookieJar對象
yield scrapy.Request(login_url, meta = {'dont_merge_cookies': True, 'cookiejar' : response.meta['cookiejar']}, callback=self.verify_login)
......
#登錄成功后,得到要保存的cookies,為CookieJar對象
cookie_jar = response.meta['cookiejar']
cookie_jar.extract_cookies(response, response.request)
將cookies存儲到文件
可以通過CookieJar._cookies
方法將cookies轉換為字典類型進行存儲,也可以通過遍歷的方式將各條cookies分別存儲,為了后續解析的簡單,我選擇了遍歷的方式:
with open(self.cookie_file, 'wb+') as f:
for cookie in cookie_jar:
f.write(str(cookie) + '\n')
這種方式相比直接存儲為字典格式的方式而言,遍歷后自動忽略了一部分cookies,數量相比后者有明顯減少,但分析后發現,關於最后一次請求也就是登錄成功時的請求的cookies都被保存了下來,沒有被忽略,如果不放心,完全可以直接解析完整的字典類型的cookies。保存下來的cookies格式如:
<Cookie SRF=1456586813 for .passport.weibo.com/>
......
<Cookie SSOLoginState=1456586813 for .weibo.com/>
從文件加載cookies
通過FireFox分析微博登錄成功時的請求返回的cookies以及這篇博客可知,用於登錄的cookies的domain都是.weibo.com
,那么可以通過正則表達式從文件中提取相關的cookies:
with open(self.cookie_file) as f:
cookiejar = f.read()
p = re.compile('\<Cookie (.*?) for .weibo.com\/\>')
cookies = re.findall(p, cookiejar)
cookies = (cookie.split('=') for cookie in cookies)
cookie_jar = dict(cookies)
這里之所以將讀取的cookies轉換為字典類型,是因為若要在scrapy的請求中手動添加cookies,需要使用scrapy.Request
方法中的cookies
參數。將從文件中加載的字典類型的cookie_jar
賦值給cookies
參數,可以實現直接登錄微博:
yield scrapy.Request(url='http://weibo.com', cookies=cookie_jar, callback=self.logined)