前言

最近闲着无聊准备写写Python爬虫,爬一下我们学校的oj用来横向比对做题量。但由于我校OJ对没有登录的账号有浏览限制,因此想要获取数据,就必须要登录账号。以前我一直手动生成Cookie,然后到浏览器里用该Cookie登录,这样的话没过一会儿又要重来比较麻烦。于是我就学习了一下用PythonPOST数据的方式实现登录功能。

说明

  1. 感谢hiuseues大佬对我的指导!
  2. 此处以我校OJ为例
  3. 请不要使用此测试账号(账号来自某外校团队)来浏览我校题目或交题,否则我将把密码改掉
  4. 我习惯于使用Python2写爬虫,主要因为我校电脑和我的VPS预装的都是这个版本。可能会和hiuseues大佬产生代沟,不要在意。当然,这个脚本的核心部分在Python3中完全可以运行,希望这样的话代沟可以小一点。

分析

登录页面

我们进入loginpage.php,可以看到一个登陆的界面。

使用“审查元素”功能查看一下这个登录框的源代码。

其中的核心代码如下(其他内容并没有什么卵用):

1
2
3
4
5
<form action="login.php" method="post" role="form">
<input name="user_id">
<input name="password">
<button>登录</button>
</form>

即使你把代码删到只剩这么点,你照样可以登录:只不过界面难看点罢了。

分析数据

那么我们来讲讲这段“最简代码”讲了什么:

  1. 最外层的form中定义了需要post到的页面 <=> action="login.php"
  2. 同时也定义了数据的传输方式 <=> method="post"
  3. 两个input提供了两个输入框,也是需要post到对应页面的数据 <=> <input name="user_id"><input name="password">
  4. 最后你会发现还有一个button,你点一下数据就会被发送过去啦!

该系统使用了明文密码传输的方式,因此比较容易分析。现在为了安全,越来越多的网站都在避免明文密码传输,但原理基本都是有互通之处的。不过在中国这个网络大环境下,完全有可能上级一个领导电话过来向你要xxx用户的密码,你程序员要是不存明文密码那行啊(ε=(´ο`*)))唉)

那么你需要做的,就是给”/login.php”这个页面发送两个分别叫user_idpassword的数据即可!

如何post数据

此处我们使用urlliburllib2来操作,前者用于编码post的数据,后者用于发送请求。这两个库的名称和用法在python3略有改变,请参照下面的代码(下面的代码采用try的方式确保了在py2和py3中的兼容性,我以后的代码可能只会在py2中生效,但是你完全可以用同样的修改方式让它在py3中运行)。
当然,你也可以使用requests等第三方库。值得一提的是,requests库在处理post文件的操作时非常方便。

我们用urllib库中的urlencode()方法来编码我们需要post的数据,在py2中的代码如下

1
data = urllib.urlencode({"user_id": username, "password": password})

接着你需要使用urllib2库中的urlopen()方法来打开网页,同时post数据(data

1
urllib2.urlopen("http://www.h2zbmh.com:8080/html/JudgeOnline/index.php", data)

构建opener

也许你在想,这就没了?结果你在尝试打开下一个页面时会发现,我还是没法获取那些需要登陆的页面啊!

那么你可能需要一个opener来维持你打开页面的状态。接着使用opener.open(url)来代替urllib2.urlopen(url)并保留登陆状态。

在构建opener的同时,我们也会把当前自身的cookie保存到文件中,方便进一步的调试。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# encoding=utf8
try:
# python2的引入
import urllib
import urllib2
import cookielib
except ImportError:
# python3的引入
import urllib.parse as urllib
import urllib.request as urllib2
import http.cookiejar as cookielib
# 利用try实现同时兼容py2和py3的效果

username = 'LJB001590'
password = '123' # 测试账号,账号密码来自某团队
rooturl = 'http://www.h2zbmh.com:8080/html/JudgeOnline/'

def geturl(url):
return rooturl + url;

def Build():
global opener # 引入全局变量
filename = 'cookie.txt' # 保存cookie的文件
cookie = cookielib.MozillaCookieJar(filename) # 创建一个实例
handler = urllib2.HTTPCookieProcessor(cookie) # 创建urllib2的cookie处理器
opener = urllib2.build_opener(handler) # 通过之前的cookie处理器构造opener
opener.open(geturl('index.php')) # “随机”打开一个页面来获得cookie
cookie.save(ignore_discard=True, ignore_expires=True) # 保存cookie到文件


def Login():
global username, password, rooturl, opener # 引入全局变量
url = geturl('login.php') # 对应的网址
data = urllib.urlencode({"user_id": username, "password": password, }) # 需要post的数据
try:
response = opener.open(url, data=data) # post数据 即 发送登录请求
except TypeError:
response = opener.open(url, data=data.encode('utf-8')) # python3中的解决方式

Build()
Login()

open("ans.html", "wb+").write(opener.open(geturl('problemset.php')).read()) # 利用构建好的opener打开网页并输出到ans.html文件中(如果不显示“请登陆后继续操作!”说明成功)

# 再次感谢hiuseues大佬