接上一篇,有些网站是需要先登录,然后才能看到数据的。这种情况下,就要用到requests.session了。

接下来,我们分步骤来看每一步怎么做。

一、找到登录请求

登录或者说鉴权的原理是,在你访问真正的数据前,把你的用户名密码等信息,喂给服务器上特定的接口,从而让服务器能够验证你是否是合法的用户。所以这一步,我们就要尝试找到登录过程中真正接收用户名密码的网址是什么。

为了找到这个网址,我们常常要用到浏览器的开发人员工具选项来追踪和分析我们登录的过程。(注:如果你对分析web请求了如指掌,那么你可以直接跳到第3部分)

以Chorme浏览器为例,按F12就可以进入开发人员模式,接着我们切换到名为Network的Tab页,然后正常访问你要抓取的网站的登录入口页面,并输入用户名和密码进行登录。

示例:切换到Network页,准备登录

一旦你点击了登录,你会发现整个过程中的网络请求,都被浏览器记录了下来。

示例:右侧记录了登录过程中的若干次网络请求。

接下来,我们就要从这一堆请求中,找到真正的登录请求。这个请求往往符合以下几个条件:

  1. 往往是若干次请求中的第一个请求。
  2. Request Method往往是POST(参考下图1)。
  3. 往往带Form Data(参考下图2)。
图1:Request Method是POST
图2:这次请求中带Form Data

像上图中的例子,我们基本可以判断,第一个请求就是真正的登录请求了。我们可以把这个请求对应的网址拷贝出来,以作后用。

二、分析登录参数

接下来这一步,就要开始分析上面提到的Form Data了。

从上面图中,我们可以看到登录时我们总共给服务器传了四个值,分别是:

  1. csrfmiddlewaretoken
  2. username
  3. password
  4. next

这四个值中,username和password都好理解,就是我们要输入的用户名和密码;next也好理解,能猜出来它是指登录成功后要转向哪个页面。那么,第一个csrfmiddlewaretoken是什么鬼?

不管它是什么,我们都可以猜出,如果这个值不能正确传递的话,服务器是不会允许我们登录的。那接下来,我们来找找它从何而来。

这时,我们在登录入口页面的”登录”按钮上点右键,然后选择“检查”,就会自动跳出来登录页面的源代码。

附图:登陆页面的源代码

幸运的是,我们很快在这份源代码中,找到了csrfmiddlewaretoken的值(见上图中红框)。

到目前为止,用于登录的URL有了,要传递给服务器的各个参数也都有着落了。下一步,我们要开始用python代码模拟登录了。

三、开始行动

真正下代码的时间到了。我们把整个过程分为三个关键步骤。

  1. 抓取登录入口页面,并从中取得 csrfmiddlewaretoken的值;
  2. 模拟登录,获取后续访问真正的数据页面的权限。
  3. 从数据页面抓取数据。

要真正完成第3步,需要让服务器知道前2步和第3步的请求来自同一会话,这时候就需要用到requests.session了。

首先,创建一个会话:

import requests   # 导入requests模块

session = requests.session() # 创建一个会话,之后的所有网络都基于这个会话来做

接下来,从登录入口页面中爬取csrfmiddlewaretoken。这里还是要强调一下,之所以要爬取这个token,是因为我举的例子中恰好需要。在你的实际应用中,你有可能不需要爬取token,也有可能需要爬取别的token。

# 请求登录入口页面,目的是获取csrfmiddlewaretoken
portal = session.get("http://www.xxx.com/xxx/login/") 

# 这里忽略了解析csrfmiddlewaretoken的代码(这不是本文的重点),
# 也就是说parse_token函数需要你根据实际情况去编写
token = parse_token(portal.text)

然后开始模拟登录。注意,下面这几句代码是本文的重点。

# 把token、用户名、密码组合成你的服务器需要的样子(具体样式参考前面form data的截图)
form_data = "csrfmiddlewaretoken=%s&username=%s&password=%s&next=%%2Fadmin%%2F"%(token, your_username, your_password)

# 把Content-Type设置成application/x-www-form-urlencoded,这个不一定必须,但我用的例子中必须得这么设定
session.headers.update({'Content-Type': 'application/x-www-form-urlencoded'})

# 发送POST请求,模拟登录
login = session.post("http://www.xxx.com/xxx/login/?next=/", data=form_data)

# 打印服务器响应,看看是否已经登录成功
print(login.text)

如果上一步成功了,那么接下来就可以请求真正的数据了。例如:

# 请求真正的数据网页
data = session.get("http://www.xxx.com/data.html")

# 解析数据
# ...