Python爬虫系列:(二)模拟登陆CSDN

很多网站都必须要登陆权限,爬虫如果想获取有用的信息就必须带有登陆后的cookie。所以就有了模拟登陆的需求。一般我们写爬虫的顺序就是 模拟登陆->页面抓取->页面解析和数据清理->数据入库,有的页面信息并不需要登陆权限就可以抓取数据,模拟登陆这一步就可以省略了。另外,如果涉及到数据分析,那么我们还需要将数据用可视化的方法展示出来。这些东西在以后都会讲到。

原计划先写慕课网的模拟登陆,因为比较简单。但刚上慕课网看了一下,它的登陆流程已经改了,和目前主流网站的登陆流程是类似的,那个属于比较复杂的登陆了,留在后面再讲吧。我在这里选择csdn作为初级模拟登陆的范例。下面讲它的流程。


通过抓包分析登陆流程

使用正常用户手动登陆目标网站,这里就是csdn。在打开目标网站之前需要打开抓包工具,我使用httpanalyzer的时候不知道是不是它抽风了,始终抓不到包,于是临时换为fiddler进行演示。注意要在打开目标网站登陆界面之前就要打开抓包工具,因为一般加密登陆用户名和密码的js代码在用户登陆之前就被请求了。如果在手动登陆的过程再打开,那么可能就找不到它的JS加密文件了。这种情况一般用于加密提交用户名和密码的时候,具体我会在分析微博模拟登陆的时候讲到。而csdn的登陆没那么复杂,用户名和密码都是用的明文传输。在手动登陆的时候有个小技巧,那就是故意把密码填错,这样可以很容易看到用户名和密码正确的提交路径和提交方式。下面是我手动登陆csdn填错用户名和密码抓包截图:

我们从Fillder的webforms一项中可以看到提交的具体参数。其中usernamepassword_enventId一看就很明了,表示用户名、密码和提交的动作,而且它的密码还是明文传输的(目前大多数的做法是采用js加密再进行传输)。对于enventId一项,我们只是猜测它是'submit'这个不变的值,还需要验证。而"lt"和"execution"看起来比较没有规律,尤其是"lt".于是我们再次进行手动登陆,发现ltexecution在发生变化,lt毫无规律可言,而execution却是每次我们错误登陆的时候,就会在s后面的数字加1。说明这个是记录我们登陆次数信息的,那么我们就取照片中的第一次的值即可。现在的问题就是分析lt是怎么来的。

lt不可能是平白无故自己生成的撒,只有三种情况,1是藏在页面中的 2是通过服务器返回的 3是通过运行js生成的 。第一种情况需要我们查看网页源代码,进行分析。第二种情况需要我们抓包,看是否返回相应信息,第三种情况也需要我们抓包,获取js代码。然后通过在js代码中寻找关键字,这里是'lt',然后进行分析得到结果。我们先来验证第一种情况。在登陆页面,点击鼠标右键,查看网页源代码(注意我们查看的是登陆表单的源代码,所以需要光标定位在登陆表单范围内,表单外可能是别的一个页面)。然后点击CTRL+F进行全文搜索,可以发现以下内容:

看来不光是lt的值可以得到,就连我们多次抓包得到的_enventId和execution的值都可以得到。所以可见查看登陆网页源代码的重要性

在分析出我们需要提交的值过后,就是对参数的提取。考虑到有的朋友可能没有爬虫编写经验,我这里用最通用的正则表达式进行提取(以后我会讲更加简单的提取方式,那一块会放在页面解析部分讲解)。

分析页面以获取登陆需要参数

我们这里使用requests库进行http请求,requests库的API对用户非常友好,因此我们可以把精力专注放在业务逻辑分析上。要使用requests库,那么我们首先需要引入:

import requests

通过上一步的抓包分析我们可以知道登陆页面是通过get进行获取的,并且请求的URL地址为

login_url = 'https://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn'

那么请求代码如下

header = {
    'Host': 'passport.csdn.net',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8',
    'Connection': 'keep-alive',
    'Referer': 'http://www.csdn.net/'
}
session = requests.session()
r = session.get(login_url, verify=False, headers=header)
login_page = r.text

这里我们先看header吧。header是http请求头部的信息,为了让目标服务器相信我们是正常用户而不是爬虫在进行请求,我们需要伪造请求头信息,header的信息由dict进行封装。我们如何知道该在header里面设置些什么呢?这个问题的答案在上一步(手动分析登陆流程)就给出了,以获取请求页面为例:我们在手动登陆的时候通过抓包工具(这里我使用的是fidder)可以查看到请求的头信息。通过浏览器的F12也可以看到。下图是我的请求header信息:

所以就有了上面代码中header的由来。 session=requests.session()是什么意思呢?在requests中,我们不必关心cookie的相关操作,requests库可以自动给我们维护,我们只需要通过调用它的session()方法,返回的变量(这里是session)就包括了所有通过该变量进行http请求的cookie。当然,如果有特殊需求,比如需要把cookie持久化(即存入数据库或者文本中),那么requests也是支持的,我在后面的新浪微博模拟登陆会讲到。

设置好之后就可以进行Http请求了,比如这里是用的get请求,verify=False这个参数是为了解决requests不信任目标网站https证书的问题。如果我们不设置verify=False,那么可能直接抛一个“ssl相关error”。

通过get请求会返回一个Http Response,就是这里的“r”,这个变量中封装了一些很有用的属性,常见的有text(网页源代码)、cookie(网页cookie)和status code(响应状态码)。这里我再说一下status_code,如果我们不设置header,那么我们在模拟登陆成功后访问自己的主页,是会出现403的,下面会讲到。所以说可见设置header的重要性

程序运行到这里,我们已经拿到了登陆的页面了,下一步就是通过正则表达式提取需要的参数。正则表达式作为解析页面最常用也是最通用的方法,是我们需要掌握的。后面我会讲更加简单的方法解析页面。关于正则表达式的教程,推荐大家查看廖雪峰老师的博客.廖老师的博客写得深入浅出,值得一看。以前我自己读廖老师的博客,为了离线阅读,做了个爬虫把他的Git、Python和Js教程全部抓取下来了,该项目的代码放在github上:廖雪峰的教程。下面是提取参数的正则表达式相关代码:

lt_pattern = r'name="lt" value="(.*)"'
m = re.search(lt_pattern, html)
lt = m.group(1)
exec_pattern = r'name="execution" value="(.*)"'
m2 = re.search(exec_pattern, html)
execution = m2.group(1)
submit_pattern = r'name="_eventId" value="(.*)"'
m3 = re.search(submit_pattern, html)
submit = m3.group(1)

这里我强调一点,就是关于search()和match()的区别,因为如果你点开看了廖雪峰老师的教程,那么他基本都用的match(),但是这里用match()是匹配不到任何结果的,原因就在于match()只从字符串的开始与正则表达式匹配,而search()将字符串的所有字串尝试与正则表达式匹配

提交登陆需要的参数

通过抓包手动分析登陆流程可以看到提交登陆参数是通过Post方式的,那么怎么通过post构造提交参数呢?requetst库采用的是dict对其进行封装,下面是登陆参数提交代码:

post_data = {
                'username': username,
                'password': password,
                'lt': lt,
                'execution': execution,
                '_eventId': submit
             }

session.post(login_url, data=post_data, headers=header)

通过前面的讲解,这一段代码应该不用细讲了吧,调用的是post方法,post中的data参数就是需要提交的内容。

然后我们再通过访问自己主页验证是否已经登陆:

home_page = 'http://my.csdn.net/my/mycsdn'

r = session.get(home_page, headers=header).text

经过查看,确实是已经得到我们想要的结果了,至此,整个CSDN的模拟登陆流程和编码工作就讲完了。


概括起来就以下几点

1.手动登陆,抓包分析登陆流程

2.请求登陆页面以获取登陆提交需要参数

3.提交登陆参数

4.登陆完成,验证是否正常

PS:整篇文章中需要大家注意以下几点: - 请求的时候注意构造header信息,有兴趣的同学可以不带header访问自己主页,看是否会出现403

  • 正则表达式中的match()和search()的区别

  • 对于需要通过cookie才能访问需要登陆权限的网站,requests通过requests.session()就可以自动维护cookie了,并不需要我们手动去维护

  • 别频繁登陆,因为CSDN登陆页面可能返回验证码。关于图片验证码的识别,则是比较难的部分了,从本质上说并不属于爬虫的核心技术。

这里是它的源代码:csdn模拟登陆,前段时间比较忙没有更新,不过我会一直保持更新的。