之前封装过一个完整的微博爬虫工具包,但是在我查找python多进程爬虫的相关资料时发现,其实使用urllib2写网络爬虫已经是陈年往事,如今大家都用requests包,就像明明有.NET和Qt框架偏偏要用MFC框架,我是自找麻烦了。在使用requests包逐个重构网络爬虫部件的时候,对weibo.cn的模拟登录过程有了一些新的理解,于是来写篇文介绍一下。
我们再来重新分析一遍weibo.cn的登录过程。出发前先来检查一下装备:
- firefox浏览器
- 一个firefox浏览器下的HTTP报文记录分析插件——httpfox
- python2语言环境——python 2.7
- 一个python HTTP包——requests
- 一个python XML解析包——BueatifulSoup4
妥,先来用httpfox记录一下手工登录的过程:
图1 weibo.cn模拟登录过程总览
无视掉第二条失败的图片请求,剩下的就是weibo.cn登录的全过程。我们逐条分析:
图2 请求登录页面
上图中是第一条请求的报文头部分,第一条请求是在访问weibo.cn的登录页面。请求到的页面源码中,掐头去尾,比较关键的部分如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <form action="?backURL=http%3A%2F%2Fweibo.cn%2F&backTitle=%E5%BE%AE%E5%8D%9A&vt=1&revalid=2&ns=1" method="post"> <div> 手机号/电子邮箱/会员帐号:<br/> <input type="text" name="mobile" size="30" value="" /><br/> 密码:<br/> <input type="password" name="password_3523" size="30" /><br/> <input type="checkbox" name="remember" checked="checked" />记住登录状态,需支持并打开手机的cookie功能。<br/> <input type="hidden" name="backURL" value="http%3A%2F%2Fweibo.cn%2F" /> <input type="hidden" name="backTitle" value="微博" /> <input type="hidden" name="tryCount" value="" /> <input type="hidden" name="vk" value="3523_4ea5_1803939589" /> <input type="submit" name="submit" value="登录" /> </div> </form>
|
可以看到表单的action地址、密码输入框的name、隐藏变量vk的值都是变动的,这三样东西正是我们在模拟登录时需要从登录页面解析获得的。对应的python代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| headers = { 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:42.0) Gecko/20100101 Firefox/42.0', 'DNT': '1', 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0', }
def get_post_needs(): url = 'http://login.weibo.cn/login/?ns=1&revalid=2&backURL=http://weibo.cn/&backTitle=微博&vt=' text = requests.get(url, headers=headers).text soup = BeautifulSoup(text, 'lxml') action_url = 'http://login.weibo.cn/login/' + soup.form['action'] password_tag = soup.find('input', type = 'password') password_name = re.search('(?<=name=")\w+(?=")', str(password_tag)).group(0) vk_tag = soup.find('input', attrs = {'name': 'vk'}) vk = re.search('(?<=value=")\w+(?=")', str(vk_tag)).group(0) return action_url, password_name, vk
|
页面解析完毕还需要填充表单,对应的python代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| def get_post_data(username, password, password_name, vk): form_data = { 'mobile': username, password_name: password, 'remeber': 'on', 'backURL': 'http://weibo.cn/', 'backTitle': '微博', 'tryCount': '', 'vk': vk, 'submit': '登录' } return form_data
|
填充完登录表单,就该post出去了,由4个连续的状态码为302的请求不难看出,发送的post请求随后被进行了足足4次重定向。
可以看到,第一次重定向后,请求访问了newlogin.sina.cn,此时新浪服务器已经给我们的请求加上了两个cookie字段(httpfox可以以字典的形式查看每个请求的cookie字段,这里我就不上图了),一个叫“_T_WM”另一个叫“SUB”,虽然不明白这两个字段的含义,但是经过实验得知,其实这个“SUB”字段就可以用来做登录验证了。
第二次重定向请求访问了passport.weibo.com,这次新浪服务器给请求重新填充了许多cookie字段,但是仍然包含一个“SUB”字段,没错,这此的“SUB”也可以拿来做登录验证。
类似的,第三次访问weibo.cn的重定向也填充了新cookie字段,我们又获得了一个可用的“SUB”字段。
至于最后一次重定向,只是通过一个几乎纯JavaScript组成的页面让我们以已登录的状态跳转到前面表单参数中的backURL指向的页面。
在使用python记录cookie字段时需要注意,这种经过若干次重定向的”长会话“不能直接使用Request对象来发送最初的post请求,而是需要一个能处理”长会话“的Session对象的帮助,获得cookie字段的对应python代码如下:
1 2 3 4 5 6 7 8 9
| def wap_login(username, password): url, password_name, vk = get_post_needs() data = get_post_data(username, password, password_name, vk) session = requests.Session() response = session.post(url, headers=headers, data=data) return session.cookies.get('SUB', domain='.weibo.cn')
|
拿到了必要的cookie字段,模拟登录过程其实就已经完成了,接下来我们也可以用下面的代码测试一下cookie字段是否管用:
1 2 3 4 5 6
| url = 'http://weibo.cn/moegirlwiki' username = raw_input('请输入新浪通行证用户名:') password = getpass('请输入新浪通行证密码:') cookies = {'SUB': wap_login(username, password)} response = requests.get(url, cookies=cookies) print '登录成功!' if response.url == url else '登录失败!'
|
如果读者接触过weibo.com的模拟登录过程想必明白,weibo.cn的登录过程简单多了,更重要的是,必要cookie字段在weibo.cn和weibo.com是通用的,那我们又何必选择更麻烦的模拟登录途径呢?