爬虫养成记 - urllib2的HTTPCookieProcessor

xcwong

很多网站的资源需要用户登录之后才能获取。
我们一旦登录后再访问其他被保护的资源的时候,就不再需要再次输入账号、密码。那么网站是怎么办到的呢?
一般来说,用户在登录之后,服务器端会为该用户创建一个Session。Session相当于该用户的档案。该档案就代表着该用户。
那么某一次访问请求是属于该用户呢?登录的时候服务器要求浏览器储存了一个Session ID的Cookie值。每一个访问都带上了该Cookie。服务器将Cookie中的Session ID与服务器中的Session ID比对就知道该请求来自哪个用户了。

上述的文字对Session机制描述的比较简单也并不一定完全正确。更加详细的资料可以阅读:http://blog.csdn.net/fangaoxi...

opener

通过阅读源码我们可以知道,我们在调用urllib2.urlopen(url)的时候,其实urllib2open函数内部创建了一个默认的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的子类MozillaCookieJarLWPCookieJar实现了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里即可。

阅读 6.3K

helloword
happy coding

hello world

502 声望
20 粉丝
0 条评论

hello world

502 声望
20 粉丝
宣传栏