Python实现网站模拟登陆
一、实验简介
1.1 基本介绍
本实验中我们将通过分析登陆流程并使用 Python 实现模拟登陆到一个实验提供的网站,在实验过程中将学习并实践 Python 的网络编程,Python 实现模拟登陆的方法,使用 Firefox 抓包分析插件分析网络数据包等知识。
模拟登录可以帮助用户自动化完成很多操作,在不同场合下有不同的用处,无论是自动化一些日常的繁琐操作还是用于爬虫都是一项很实用的技能。本课程通过 Firefox 和 Python 来实现,环境要求如下:
- Python 库:urllib, urllib2, cookielib, Django
- Firefox 要求:装有 live http header插件 (已提供)
1.2 知识点
本项目中我们将学习并实践以下知识点:
- 网站登录流程分析
- Python 网络编程基础
- Firefox 抓包分析插件 Live http header
- Python 模拟登陆实现流程
1.3 实验材料
为了节省时间,实验用到的材料已经提前制作完成,可以按照材料清单中给出的链接下载。
实验网站源码:
http://labfile.oss.aliyuncs.com/courses/640/mysite.zip
FireFox抓包插件:
http://labfile.oss.aliyuncs.com/courses/640/live_http_headers.xpi
1.4 实验准备
1) 安装抓包插件Live Http Headers
打开 Linux Xfce 终端并通过 wget 命令下载插件文件。下载成功后首选右键单击xpi文件-->使用FireFox打开,之后按照界面提示安装Live Http Header插件:


按照提示重启之后,通过 打开菜单-->附加组件-->扩展 找到安装好的插件,点击 首选项,勾选 Config 选项卡中的Open LiveHTTPHeaders in a new tab 选项以方便使用。

2)启动web应用
由于实验楼会员环境中启动的 WebIDE 会占用 8000 端口,所以如果是实验楼会员,请先停止 codebox 进程后再部署下面的 Web 应用。
使用 ps -aux | grep codebox 查询获得 codebox 的进程号,然后使用 kill -9 进程号 停止 codebox 进程。执行过程见下图:

首先安装demo依赖的web框架django,并测试是否安装成功:
$ sudo pip install django
$ python
>>> import django
>>> django.VERSION
(1, 10, 0, u'final', 1)
再通过unzip命令解压网站文件,并启动网站服务。
$ wget http://labfile.oss.aliyuncs.com/courses/640/mysite.zip
$ unzip mysite.zip
$ cd mysite
$ ./manage.py runserver
启动成功后在浏览器中输入 http://localhost:8000/polls 看到登录页面表示启动成功。

二、分析登录过程
要通过编程实现登录,首先需要理解一般Web应用的登陆过程。
不同的网站和应用登录的安全性和复杂性都不同,因此它们的登录实现过程自然会存在差异,尽管如此,最基础,核心的过程依然是相同的。对于复杂的登录过程(例如淘宝),对请求包和响应包的分析能力是很重要的。
浏览器有自带的分析工具,但是界面比较窄,考虑到实验环境界面较小,本课程选择了 Live Http Header,同学们可以在本地选用任何自己喜欢的工具进行分析。我们先通过示例页面分析一下简单的登陆过程熟悉下分析过程。
2.1 抓取请求
- 输入
http://localhost:8000/polls打开登录页。 - 打开live http header(
F10->工具->Live Http Header)。 - 输入用户名和密码(都是
shiyanlou)并提交表单,登入系统。 - 切换到Live Http Header页面查看http请求和响应信息
2.2原理分析
Live http Headers插件的Headers选项卡中会列出抓取到的所有的http请求和响应头,一次请求的url、请求和响应之间通过空行隔开,不同的请求之间通过虚线隔开。在按照2.1中的步骤登入系统后,我们在列表中看到了2个请求。第一个请求核心内容如下(还记得HTTP协议的内容吗?):
POST /polls/login HTTP/1.1 //请求行
******************** //此处省略其他请求头
Content-Type: application/x-www-form-urlencoded //请求实体类型
Content-Length: 41 //实体信息长度
name=shiyanlou&pwd=shiyanlou&commit=Login //实体内容
HTTP/1.0 302 Found //响应行,302重定向
Location: . //重定向的路径
******************** //此处省略其他响应头
Set-Cookie: sessionid=aped4pzerxjgd3db0ixw0dowkgrdpsxb;expires=Thu, 15-Sep-2016 15:04:28 GMT; httponly; Max-Age=1209600; Path=/ //响应头,服务器回写cookie
为什么是2个请求而不是1个呢?通过分析登录请求发现,登陆成功之后服务器发送了302重定向响应,服务器要求浏览器重新请求首页,这就产生了第二个请求。再来分析第二个请求,可以看到它相比登录请求多了一个请求头:
Cookie: sessionid=aped4pzerxjgd3db0ixw0dowkgrdpsxb
这个 Cookie 中的 sessionid 是从上一个响应头中的 Set-Cookie 行中获取,所以值相同。session的意思是会话,服务器通过session存储和维护与单个用户之间的会话信息。不同的用户在服务器端有不同的session,并且为了确保正常通信,每个用户的session都是唯一的。登录过程其实就是在验证用户登录信息后在session中存储用户登录标识的过程。在开发web应用时,“登陆成功”与“在session中存储用户登录标识”是等价的。整个登录流程如下图所示:

那么问题来了,服务器到底是如何区别不同用户的session的?为什么登陆成功会后要回写cookie呢?答案就是sessionid!每个用户的session都有独立的、唯一的编号sessionid用来标识用户身份。用户登录后,服务器通过从用户的session中读取登录标识来进行身份认证,因此必须要知道sessionid来访问用户的session,而使用cookie正是满足这个需求的方法。服务器将需要浏览器存储的信息通过Set-Cookie响应头发送给浏览器,浏览器会将cookie存储在本地,并在每次访问该网站时附带发送指定的cookie以满足服务器的需求,而通过cookie存储sessionid就是其中的一种应用。
2.3小结
对于服务器来说,登录=验证+写session。对于浏览器来说,登录=发送登录信息+获取带sessionid的cookie。可以说,只要获得了sessionid,就算实现了模拟登录。有了它我们便可以游离于系统之中。
三、使用Python实现登录(简单实例)
理解了登录过程的原理和细节之后,开始用Python来编写模拟登陆程序吧。在任意目录下新建login_base.py文件,使用你喜欢的编辑器打开。实现流程如下:
3.1导入模块
不要忘记编写文件头、导入必要的依赖模块哦
#!/usr/bin/python
#-*- coding:utf-8 -*-
import urllib
import urllib2
import cookielib
3.2构造登录请求
一个http请求由3部分组成:请求url、请求头以及请求实体(附带数据)。在Python的urllib2库中,由urllib2.Request对象描述一个request。其中url是一个字符串、请求头是一个dict,key是请求头名称,value是请求头的内容。至于请求数据就要分具体情况了,在表单提交这种场景下,请求数据类型为application/x-www-form-urlencoded,意思就是经过url编码的表单数据,数据的组织形式是key=value,多组键值对之间用&分隔。登录请求的实体部分如下:
Content-Type: application/x-www-form-urlencoded //请求实体类型
Content-Length: 41 //实体信息长度
name=shiyanlou&pwd=shiyanlou&commit=Login //实体内容
此时,我们只需要使用dict来存储键值对,再用urllib.urlencode()方法进行编码就可以了。最后创建urllib2.Request对象,全部代码如下:
url = 'http://localhost:8000/polls/login'
values = {
'name':'shiyanlou',
'pwd':'shiyanlou',
'commit':'Login'
}
headers = {'Referer':'http://localhost:8000/polls/show_login'}
request = urllib2.Request(url,data=urllib.urlencode(values),headers=headers)
附加参数
需要注意的是,在浏览器页面操作时,用户只需要输入用户名和密码,看起来好像只需要提交2个参数到服务器。但实际上前端页面一般都会提交其他元参数到服务器,大多数都是与服务端的访问api设计有关,还有一些控制参数。一个登录请求包含5个以上的参数是再正常不过的事情,但并不是每一个参数都是登录所必要的,有些参数即使没有也不会影响正常登录。在实验demo中,commit这个参数就是必须提交的,否则会登录失败。
防盗链
Web 应用的资源都是有url的,只要获得了url就能够在任何地方引用。听起来很方便,但这可能会导致你的资源被别人盗用。为了访问量,把自己辛辛苦苦PS的一张美照放到个人站点上,却被别人的站点给引用走了,岂不是很气人?而HTTP的请求头Referer就是为了解决这类问题而生,Referer描述了当前请求的发出者,也就是引用者,服务器可以通过Referer来判断当前的引用者是否是合法的引用者从而决定是否返回请求的资源。考虑到一定的安全性,一般的网站登录都会限制Referer域来防止非法登录。因此,为了成功登录,我们需要在request头中填写Referer头,内容从抓取的请求头中照搬来即可。
3.3发送请求并保存cookie
一般情况下,打开url默认使用urllib2的urlopen()函数,但是它不能处理cookie。这里我们需要自己创建能够存储cookie的opener。python内置有cookielib库来处理cookie,其中的MozillaCookieJar可以将cookie存储到文件中,并可以从文件中读取cookie。先创建MozillaCookieJar对象,再使用urllib2.HTTPCookieProcessor创建cookie处理器,最后使用urllib2.build_opener创建opener。接下来就可以用opener发送请求并存储cookie了。代码如下:
# 创建opener
cookies = cookielib.MozillaCookieJar('my_cookies.txt') # 指定cookie的存储文件
cookie_handler = urllib2.HTTPCookieProcessor(cookies)
opener = urllib2.build_opener(cookie_handler)
# 发送请求 & 保存cookie
response = opener.open(request)
cookies.save()
print response.code
print response.read()
如果登陆成功,就可以在指定的文件my_cookies.txt中看到sessionid了。
localhost.local FALSE / FALSE 1474019663 sessionid vejomjbliggkwblzfagobv3u8foik6am
3.4 使用cookie访问系统服务
登录系统只不过是一个开始,使用sessionid访问系统服务才是最终目的。举一个简单的例子,我们的网站demo的首页中由一个查询系统当前时间的接口(在现实生活中可能是更有用的,比如信息查询),我们在登录后点击它,可以看到它的url是http://localhost:8000/polls/date,这是只为登录用户提供的服务。如果我们现在返回首页,退出登录之后直接在地址栏中输入这个url,页面将会302重定向至登录页,无法看到当前时间。


在我们模拟登录成功后,就可以直接通过opener打开这个url来使用这项系统服务。代码实现如下:
url2 = 'http://localhost:8000/polls/date'
response2 = opener.open(url2)
print response2.code
print response2.read()
如果有是在另外一个py文件中使用这个cookie的话,再打开url之前需要先载入cookie:
# 载入cookie
cookie = cookielib.MozillaCookieJar()
cookie.load('my_cookies.txt')
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
当然,以上只是一个简单的示例,只是为了说明原理。实际应用中的请求中会有各种各样的请求参数、元数据参数甚至是多样的验证cookie,只有理解了原理才能够以不变应万变。
四、高级登陆练习
是不是原理听懂了,但是只实现demo感觉不够过瘾?我们来一起做一做下面这个稍微难一点的练习吧。
4.1题目
已知信息:
- 登录页面url : http://localhost:8000/admin/login/?next=/admin/
- 登录提交url : http://localhost:8000/admin/login/?next=/admin/
- 用户名 :admin
- 密码 : djangoadmin
要求:
- 模拟登陆成功并存储cookie
4.2 过程分析提示
管理员的登录请求的请求如下:
POST /admin/login/?next=/admin/ HTTP/1.1
Referer: http://localhost:8000/admin/login/?next=/admin/
Cookie: csrftoken=adxAiQKnRYgQoS4RmoooCesA7mBgJtwVnsd98nttE1arcBAnGwRbILLvWeS5xLfM
Content-Type: application/x-www-form-urlencoded
Content-Length: 137
csrfmiddlewaretoken=tsxs3KcOUt4ZdMg1gMBCKkNV8JTxZigaGHd1ThVUHwYA1vMxAU4pQR6QXBamNAZ1&username=admin&password=djangoadmin&next=%2Fadmin%2F
HTTP/1.0 302 Found
Vary: Cookie
Last-Modified: Fri, 02 Sep 2016 09:24:46 GMT
Location: /admin/
Content-Type: text/html; charset=utf-8
Set-Cookie: csrftoken=seowCkz5vprAPZbkqF7M4QuzYZPKgoUrsO5nE3WWxtYnc5NRKB34rmmrTAIDdCaK; expires=Fri, 01-Sep-2017 09:24:46 GMT; Max-Age=31449600; Path=/
Set-Cookie: sessionid=cb6z7xlu31uxt8kdupfh0td43ewhgvpx; expires=Fri, 16-Sep-2016 09:24:46 GMT; httponly; Max-Age=1209600; Path=/
不难观察到登录请求中附带了csrftoken这个cookie,表单数据中必须提交csrfmiddlewaretoken,username,password这三个参数,最后的next参数是url中附带的,不需要额外添加。必须得到前两个参数才能够成功登陆,这两个参数从名字上来看都用来作认证令牌,应该是由服务器生成的。其中的cookie肯定是来自上一个response的回写,而从csrfmiddlewaretoken的位置来看,应该是在登陆表单当中。注销登陆,重新进入登录页面,点击右键查看源代码发现了这个输入域(每次请求的值都是不同的):
<input type='hidden' name='csrfmiddlewaretoken' value='PSM5KbmRGdHraaVXK9UEzsWmLYlHxYjUPstWMUJIIheexgxu45QWWYOeGzeAuczd' />
这样一来我们的思路就很清晰了:

解析页面可以使用python的正则表达式re模块或者是比较火的beautifulsoup库。希望同学们先自己动手挑战一下,欢迎再课程回复中和我讨论,届时我将提供登录代码。
五、总结&扩展
模拟登陆的关键是弄清楚登录请求需要提交的参数,重点是要获取带有sessionid的cookie,最终目的是不受限制的访问系统提供的有价值的服务。
只有分析清楚登陆过程才能达到最终目的,这就需要具备强的HTTP包分析能力,理解HTTP协议,并配合包分析工具(FireBug,Live Http Header,Burp Suite等)解析出关键参数,有时需要编辑和重发包来删减掉不必要的多余参数。下面给出一些提高的参考资料,学有余力的同学可以深入研究:
Python实现网站模拟登陆的更多相关文章
- Python爬虫实例(四)网站模拟登陆
一.获取一个有登录信息的Cookie模拟登陆 下面以人人网为例,首先使用自己的账号和密码在浏览器登录,然后通过抓包拿到cookie,再将cookie放到请求之中发送请求即可,具体代码如下: # -*- ...
- python作业:模拟登陆(第一周)
模拟登陆作业需求: 1. 用户输入帐号密码进行登陆 2. 用户信息保存在文件内 3. 用户密码输入错误三次后锁定用户 额外实现功能: 1.提示输入错误次数 2.输入已锁定用户会提示 3.用户不存在会提 ...
- python之scrapy模拟登陆人人网
1.settings.py主要配置信息,包括USER_AGENT等 # -*- coding: utf-8 -*- # Scrapy settings for renren project # # F ...
- python 用cookie模拟登陆网站
import re import requests def get_info(url): headers = { "Cookie" :"***************** ...
- scrapy爬取某网站,模拟登陆过程中遇到的那些坑
本节内容 在访问网站的时候,我们经常遇到有些页面必须用户登录才能访问.这个时候我们之前写的傻傻的爬虫就被ban在门外了.所以本节,我们给爬虫配置cookie,使得爬虫能保持用户已登录的状态,达到获得那 ...
- Python Selenium + phantomJS 模拟登陆教务管理系统 “抢课”
# _*_coding:utf-8_*_ from selenium import webdriver from selenium.webdriver.common.action_chains imp ...
- python使用mechanize模拟登陆新浪邮箱
mechanize相关知识准备: mechanize.Browser()<br># 设置是否处理HTML http-equiv标头 set_handle_equiv(True)<br ...
- 【小白学爬虫连载(10)】–如何用Python实现模拟登陆网站
Python如何实现模拟登陆爬取Python实现模拟登陆的方式简单来说有三种:一.采用post请求提交表单的方式实现.二.利用浏览器登陆网站记录登陆成功后的cookies,采用get的请求方式,传入c ...
- Python模拟登陆TAPD
因为在wiki中未找到需要的数据,查询也很迷,打算用python登录tapd抓取所需项目下的wiki数据,方便查找. 2018-9-30 19:12:44 几步走 模拟登录tapd 抓取wiki页左侧 ...
随机推荐
- 利用popstate事件和window下的history对象处理浏览器跳转问题
引子 之前,偶尔在项目中用过history接口做返回上一页功能,当时是用的history.go(-1),前几天面试中遇到一个控制浏览器跳转的问题时有点懵,特意查了文档记录一下,并且列出一些目前能想到的 ...
- BZOJ 2683: 简单题(CDQ分治 + 树状数组)
BZOJ2683: 简单题(CDQ分治 + 树状数组) 题意: 你有一个\(N*N\)的棋盘,每个格子内有一个整数,初始时的时候全部为\(0\),现在需要维护两种操作: 命令 参数限制 内容 \(1\ ...
- Js - JQ事件委托( 适用于给动态生成的脚本元素添加事件)
最近一段时间打了一个大仗,现在总算消停点,才有时间来做个总结吧算是: 移动端遇到一个项目,是一个列表的侧滑栏,在我这里用jq写的交互事件.自测各方面都挺好的,美滋滋的给了研发.研发也美滋滋的开始开发. ...
- tp5 日志文件名称问题
原文:http://www.upwqy.com/details/17.html 我的项目在运行一段时间后 我发现在日志中生成了 1508467147-20.log 这种文件名的日志 开始还以为是bug ...
- 开发中使用mongoTemplate进行Aggregation聚合查询
笔记:使用mongo聚合查询(一开始根本没接触过mongo,一点一点慢慢的查资料完成了工作需求) 需求:在订单表中,根据buyerNick分组,统计每个buyerNick的电话.地址.支付总金额以及总 ...
- 12.C++-构造函数与析构函数调用顺序,const成员函数,const对象
单个对象创建时,构造函数的调用顺序 1.首先判断该对象的类是否拥有父类,若有则先调用父类的构造函数 2.判断该对象的成员是否是其它类的成员,若是则调用成员变量的构造函数(调用顺序和声明顺序相同) 3. ...
- 微信小程序快捷键
- 云计算之路-阿里云上:针对 docker swarm 故障的部署调整以及应急措施
针对这周 docker swarm 集群的频繁故障(详见故障一 .故障二.故障三),我们今天对 docker swarm 集群的部署进行了如下调整. 将 docker engine 由 “17.12 ...
- wifislax中的linset软件钓鱼教程
wifislax中很多破解wifi密码的工具,下面就来说说里面的linset软件的钓鱼过程,国内很多人知道这个方法,不过没有总结,youtube上视频一大把,我刚才测试了一把,还是打算记录一下攻击过程 ...
- 值得 .NET 开发者了解的15个特性
本文列举了 15 个值得了解的 C# 特性,旨在让 .NET 开发人员更好的使用 C# 语言进行开发工作. 1. ObsoleteAttribute ObsoleteAttribute 适用于除组件. ...