很多网站的资源需要用户登录之后才能获取。
我们一旦登录后再访问其他被保护的资源的时候,就不再需要再次输入账号、密码。那么网站是怎么办到的呢?
一般来说,用户在登录之后,服务器端会为该用户创建一个Session。Session相当于该用户的档案。该档案就代表着该用户。
那么某一次访问请求是属于该用户呢?登录的时候服务器要求浏览器储存了一个Session ID的Cookie值。每一个访问都带上了该Cookie。服务器将Cookie中的Session ID与服务器中的Session ID比对就知道该请求来自哪个用户了。
上述的文字对Session机制描述的比较简单也并不一定完全正确。更加详细的资料可以阅读:http://blog.csdn.net/fangaoxi...
opener
通过阅读源码我们可以知道,我们在调用urllib2.urlopen(url)
的时候,其实urllib2
在open
函数内部创建了一个默认的opener对象。然后调用opener.open()
函数。
但是默认的opener并不支持cookie。
那么我们先新建一个支持cookie的opener。urllib2中供我们使用的是HTTPCookieProcessor
。
创建HTTPCookieProcessor需要闯入一个存放cookie的容器。
Python提供的存放cookie的容器位于cookielib,有以下几个。
CookieJar -> FileCookieJar -> MozillaCookieJar / LWPCookieJar
示例代码:
import cookielib
import urllib2
cookies = cookielib.CookieJar()
cookieHandler = urllib2.HTTPCookieProcessor(cookiejar=cookies)
opener = urllib2.build_opener(cookieHandler)
urllib2.install_opener(opener)
request = urllib2.Request("http://www.baidu.com")
urllib2.urlopen(request)
for cookie in cookies:
print cookie.name, cookie.value
上面的代码显示,urllib2的确帮我们把cookie从response中提取出来。但是如何保存在一个文件中呢?
FileCookieJar
FileCookieJar 实现了save()
、load()
、revert()
三个函数。
但是通过查看FileCookieJar的源码我们可以发现,FileCookeJar并没有实现save()
的具体功能,而是直接抛出了NotImplementedError
。
def save(self, filename=None, ignore_discard=False, ignore_expires=False):
"""Save cookies to a file."""
raise NotImplementedError()
而FileCookieJar
的子类MozillaCookieJar
和LWPCookieJar
实现了save()
方法。
示例代码:
# coding=utf-8
import cookielib
import urllib2
cookies = cookielib.MozillaCookieJar()
cookieHandler = urllib2.HTTPCookieProcessor(cookiejar=cookies)
opener = urllib2.build_opener(cookieHandler)
urllib2.install_opener(opener)
request = urllib2.Request("http://www.baidu.com")
urllib2.urlopen(request)
# 将cookie存为一个文件
cookies.save(filename="cookie.txt")
# 新建一个cookie对象
cookies2 = cookielib.MozillaCookieJar()
# 从文件中读取cookie
cookies2.load('cookie.txt')
for cookie in cookies2:
print cookie.name, cookie.value
save()函数带有两个参数,ignore_discard和ignore_expires。
ignore_discard: save even cookies set to be discarded. 即也保存需要被丢弃的cookie。
ignore_expires: save even cookies that have expired. 即过期的cookie也保存。
上面提到了save()、load(),还有一个函数未提到即revert()。revert()函数的作用是Clear all cookies and reload cookies from a saved file.
模拟登录实际操作
我们来试一下模拟登录SegmentFault。
示例代码如下:
import urllib
import urllib2
import cookielib
cookies = cookielib.MozillaCookieJar()
cookieHandler = urllib2.HTTPCookieProcessor(cookiejar=cookies)
opener = urllib2.build_opener(cookieHandler)
urllib2.install_opener(opener)
postData = {
"remember": 1,
"username": "YOUREMAIL",
"password": "YOURPASSWORD"
}
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4",
"Connection": "keep-alive",
"Content-Length": "54",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "_gat=1; PHPSESSID=YOUR_PHPSESSID; _ga=GA1.2.741059584.1485746441; Hm_lvt_e23800c454aa573c0ccb16b52665ac26=1485746441; Hm_lpvt_e23800c454aa573c0ccb16b52665ac26=1485746618",
"DNT": "1",
"Host": "segmentfault.com",
"Origin": "https://segmentfault.com",
"Referer": "https://segmentfault.com/",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
}
request = urllib2.Request("https://segmentfault.com/api/user/login?_=57f36e7f98914cc9a7971aebc264e113", headers=headers)
request.add_data(urllib.urlencode(postData))
response = urllib2.urlopen(request)
print response.getcode()
for cookie in cookies:
print cookie.name, cookie.value
response = urllib2.urlopen("https://segmentfault.com/u/charliecharlie")
print response.read()
但是目前这个模拟登录并不完美。
实际上浏览器在访问第一个页面的时候,服务器就在Response中返回了一个cookie,设置了一个PHPSESSID的Cookie。
目前博主只知道login链接后带的_
参数需要与PHPSESSID
相匹配。但是并不知道两者具体的关系。
且上述代码中其实并不需要HTTPCookieProcessor
而是直接写在headers
里即可。