原理
有时候爬取的内容需要登录才能看到,这时就要求爬虫能够登录网站,并保持登录状态。比如我们登录了一个网站:豆瓣,打开新的豆瓣网页会发现还是处于登录状态。那么,网站是如何保持这种登录状态的呢?我们是否可以利用爬虫保持登录状态,实现对网站深层网页的爬虫呢?首先,我们先来分析一下网站是如何保持登录状态的,一般有如下两种机制。
1. Cookies 机制
Cookies 是浏览器访问一些网站后,这些网站存放在客户端的一组数据,用于网站跟踪用户,实现用户自定义功能。
一般使用 Cookies 保持用户登录的过程是这样的:用户登录验证后,网站会创建登录凭证(如用户ID+登录时间+过期时间),对登录凭证进行加密,将加密后的信息写到浏览器的 Cookies 中,以后每次浏览器请求都会发送 Cookies 给服务器,服务器根据对应的解密算法对其进行验证。
2. Session机制
Session 是存放在服务器端的类似于 HashTable 的结构,用来存放用户数据。当浏览器第一次发送请求时,服务器自动生成了一个 HashTable 和一个 Session ID 用来唯一标示出这个 HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求的时候,系统会将前一次服务器响应中的 Session ID 存放在请求中,一并发送到服务器上,服务器从请求中提取出 Session ID,并和保存的所有 Session ID进行对比,找到这个用户对应的 HashTable。一般来说,Session ID 在用户端使用 Cookies 来保存。
模拟登录
1. 复制 Cookies 登录
既然前面已经说过了模拟登录需要 Cookies ,那么一种简单的方式就是人为首先登录网站,将 Cookies 复制下来,这个 Cookies 是保持了网站的登录状态的。因此,现在我们就可以使用该 Cookies 访问豆瓣的首页,然后验证是否已经登录。
# -*- coding: utf-8 -*-
# # # @Author: lemon
# # # @Date: 2019-09-17 10:19
# # # @Last Modified by: lemon
# # # @Last Modified time: 2019-09-17 10:30
# # # @function: 模拟登录豆瓣(设置 Cookies)
import requests
# 将字符串形式的 Cookies 处理成字典形式
def coo_regular(cookie):
coo = {}
for k_v in cookie.split(';'):
k, v = k_v.split('=', 1)
coo[k.strip()] = v.replace('"', '')
print(coo)
return coo
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
cookie = 'll="118267"; bid=HVDmoMT9dyo; _pk_ses.100001.8cb4=*; __utmc=30149280; ap_v=0,6.0; __yadk_uid=d5N02s1R9ix13lCWloPf7EPBCr7ElShc; push_noty_num=0; push_doumail_num=0; __utmv=30149280.18279; douban-fav-remind=1; dbcl2="182793377:o/M7crr6b6E"; ck=SFlh; __utma=30149280.334936774.1568701993.1568701993.1568703664.2; __utmz=30149280.1568703664.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utmb=30149280.2.10.1568703664; _pk_id.100001.8cb4=ad54d9ac0f20220e.1568701992.1.1568703668.1568701992.'
def main():
url = 'https://www.douban.com'
res = requests.get(url, headers=headers, cookies=coo_regular(cookie))
print('柠檬不萌' in res.text)
if __name__ == '__main__':
main()
我们验证源代码中是否包含个人昵称,如果包含则说明登录成功。
2. Requests 会话对象
Request 会话对象能够跨请求保持某些参数如 Cookies ,即同一个 Session 实例发出的所有请求都保持同一个 Cookies,而 Requests 模块每次会自动处理 Cookies ,这样就可以很方便的处理登录时保存 Cookies 的问题。因此,要想在爬虫中保持登录状态,可以使用 Requests 会话对象。
下面仍以登录豆瓣网站为例,进行说明。首先分析一下流程:
- 构造需要的 POST 表单
- 登录豆瓣网站,并爬取首页内容
import requests
from lxml import etree
from PIL import Image
from io import BytesIO
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
url = 'https://www.douban.com/accounts/login'
data = {
'source': 'index_nav',
'form_email': '984595060@qq.com',
'form_password': '963852741',
}
s = requests.session() # 初始化会话
r = s.post(url, data=data, headers=headers)
print('柠檬不萌' in r.text)
3. 验证码的处理
如果有验证码的话,我们就不能像之前那样进行登录了,我们需要获取验证码的内容并将其作为表单内容的一项发送给服务器。按照如下步骤进行:
- 爬取登录页面,获取验证码数据
- 构造 POST 表单数据
- 登录并爬取首页
# -*- coding: utf-8 -*-
# # @Author: lemon
# # @Date: 2019-09-17 14:35
# # @Last Modified by: lemon
# # @Last Modified time: 2019-09-17 10:30
# # @function: 模拟登录豆瓣(提交表单-账号-密码)
import requests
from lxml import etree
from PIL import Image
from io import BytesIO
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
url = 'https://www.douban.com/accounts/login'
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
captcha = html.xpath('//*[@id="captcha_image"]/src')
if captcha: # 判断是否有验证码
captcha_url = captcha[0] # 获取验证码图片的 URL
captcha_image = requests.get(captcha_url) # 下载验证码图片
img = Image.open(BytesIO(captcha_image.content)) # 打开验证码图片
img.show() # 显示验证码图片
captcha_text = input(u'请输入验证码 : ') # 输入识别出的验证码
# 下面解析出 captcha_id 的值
captcha_id = captcha_url.split('=')[1].split('&')[0]
else:
captcha_id = None
captcha_text = None
data = {
'source': 'index_nav',
'form_email': '984595060@qq.com',
'form_password': '963852741',
'captcha-solution': captcha_text,
'captcha-id': captcha_id
}
s = requests.session() # 初始化会话
r = s.post(url, data=data, headers=headers)
print('日月光华' in r.text)