request 请求 数据不全 python_两天时间挑战python实现广州大学抢课?(登陆篇)

本文讲述了作者利用Python的requests库和Charles抓包工具,挑战在两天内实现广州大学抢课系统的登录过程。通过分析登录过程中所需的form数据,包括验证码、lt、execution等,成功发送登录请求并完成登录。代码中包含验证码识别部分,若无法安装tesseract-ocr,可手动输入验证码。
摘要由CSDN通过智能技术生成

dbaa82e974759f9f1f1a8a36982fdab2.png

学python也有一段时间,学校7月10日就抢课了,剩下两天时间,挑战一下自己,看能不能实现。第一次写知乎,文章讲得不够详细,需要一定的python基础,废话不多说直接开始。

python配置:python3.6.5+tesseract-ocr(验证码识别)(安装不了的话可手动输入代替)+requests库

抓包工具:Charles4.5.6(百度有配置教程)

广州大学登录url:https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp

先来一个完整的抓包:

0db7665c8ef535d718b7967b00ee29ba.png

这么多包数据我们不是每个发送的,首先看一下首页:

e1aaca6184d20cf1f11bc4f7784ab791.png

fa522121cb150dcc96cb0b014ec93795.png
param数据

8e5ce3c5a7952f3bab697a37067095c9.png
headers数据

没有param(QueryString)是固定的,没有cookie数据,并且Headers没有Referer指向,这里想到多人抢课网络延迟会比较大,先给requests弄个超时封装一下:

首页

url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header = {        #浏览器请求头
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/9.0.084.36 Safari/536.5',
}
session = requests.session()         #session()cookie自动管理器
def reqs(action, this_url, datas={}, Time=30, error_tip='网络不流畅,重试中。。。'):
    # 为后面的每个requests请求添加超时操作
    while True:
        try:
            if action == 'get' or action == 'GET':
                return session.get(this_url, params=datas, headers=header, timeout=Time)
            elif action == 'post' or action == 'POST':
                return session.post(this_url, data=datas, headers=header, timeout=Time)
            break
        except Exception as e:
            if str(e).find('timed out') > -1:
                print(error_tip)
            else:
                print(e)
                input()
            continue
request = reqs('get', url)      # 进入首页
print(request.text)

看到输出的数据和抓包的response一样说明请求成功了。

接下来就要分析登录提交的form表单数据:

794359ff1614f6de3356fe63dd9c194b.png
登录form表单

多登陆几次抓包就发现有些固定值不用变,帐号密码固定值的就不说了,我们要完成的数据是captcha,lt,execution。

先看captcha验证码:

fe72fca97a1f679f5627e9cd2a4fe62e.png

1812ce735f974b16331ab8eed977963c.png

验证码其实就是个图片请求,我们就get请求,本地打开验证码图片,手动输入验证码就可以了。

分析验证码请求:

dbbdaa20235e35449a8b69a2caf9d55c.png

有cookie信息,记住开头结尾B9........E1

f75bba372b40753955f5d108f2175dbb.png

d93b319103dd151582f444b6c3628b12.png

这不是我们一开始请求过的首页吗?其实一开始我们请求首页的时候就使用了requests库里的session()来自动保存cookie。所以只要请求过的页面就会自动保存cookie。

再看一下验证码请求captcha的headers:

73c0a46c83602e2c4c35220148673ddb.png

有个Referer指向,对比一下就是我们进入首页的url,在header里添加一个Referer就好了:

url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header['Referer']=url

02f7be9b971f4eba17882fea95dde8f8.png

看一下验证码的请求url,ok,发送:

def captcha():  # 验证码处理
    this_url = 'https://cas.gzhu.edu.cn/cas_server/captcha.jsp'
    request = reqs('get', this_url)
    with open('code.png', 'wb')as f:
        f.write(request.content)
    open('code.png').show()
    code = input('Please input the code: ')
    return code

f0b191b2523962d4a65273376361a3a5.png

完美下一个表单数据 lt:

直接复制该值,ctrl+F查找

1f8d6ac1bf7f31855fc928f62cf41ef4.png

只看response响应

6a49599940f561b70748b46581735a2f.png

双击打开:

fa7f513bd8be5bb1c3c0411287ffd1cd.png

这不是首页里的response数据吗,我们只要将上面请求过首页的request请求拿来用就行了。

def lt(request):  # 登录form['lt']
    result = re.search(r'lt" value="(.*?)"', request.text).groups()[0]
    return result

个人喜欢用re来查找,lt" value="对应的是我们要的lt值的前部,而"是lt值后部,中间的(.*?)就是我们想要的内容,而.groups()[0]可以理解为提取中间(.*?)部分的固定格式。想要具体学习正则的话可以点击:python正则表达式_python正则表达式教程_python正则表达式视频教程-慕课网

同理,我们看到上面

d2c14e816938f2b086de3f5fe64b0b56.png

第二条数据也出来了:

def execution(request):  # 登录form['execution']
    result = re.search(r'execution" value="(.*?)"', request.text).groups()[0]
    return result

到这时登录表单都ok了,这里参数request是首页的请求。

form_data = {
        'username': username,
        'password': password,
        'captcha': captcha(),
        'warn': 'true',
        'lt': lt(request),
        'execution': execution(request),
        '_eventId': 'submit',
        'submit': '登录'
    }

看一下提交的url:

6b148df6b59151788cea4147ea993084.png

这里ctrl+F是查不到的,对比一下发现和首页也就差了中间部分jsessionid=B9D54AE28BEE906B61A1201D87ECE6E1,我们就搜这个试试?

be8a23e6e2d0e2e2fd26ee5dc01aa531.png

cc727eed33d92a64b714ceae8cf299e8.png

很nice这就在我们的首页里,直接安排:

def login_url(request):  # 登录按钮链接
    result = re.search(r'fm1" action="(.*?)"', request.text).groups()[0]
    return 'https://cas.gzhu.edu.cn' + result

然后对比一下cookie

e83cf60ed146c456bebc8b0b7e488185.png

还是那个,问题不大。

最后看一下headers里的请求指向:

f87e1982efcad57dc81ef0f1c0634e2b.png

还是首页的url,完美,发送请求:

def login():
    request = reqs('get', url)  # 进入首页
    this_url = login_url(request)
    header['Referer'] = url
    form_data = {
        'username': username,
        'password': password,
        'captcha': captcha(),
        'warn': 'true',
        'lt': lt(request),
        'execution': execution(request),
        '_eventId': 'submit',
        'submit': '登录'
    }
    request = reqs('post', this_url, form_data)
    print(request.text)

b597a172083c942c241b2eb65479193e.png

记得这个页面没,看到这个就是登录成功了。

接下来进入系统:

浏览器中右击检查,记住右边的url

26ddea8ce5d9f5f0e0282e1c6e52574d.png

92b7699125605ca2844ace4ba7f45417.png

在charle找到这个url,发送请求:

request = reqs('get', 'http://jwxt.gzhu.edu.cn/sso/lyiotlogin')
print(request.text)

出现编码错误:

747dbbd5ed71b65dc60eec3241d85d5d.png

我们需要解码一下:

print(request.text.encode('gbk', 'ignore').decode('gbk'))

9a580d0c29c81961d0f2f08816ac846e.png

看到这页面就已经成功进入首页了,不过这堆数据看起来有点繁杂,我改用更简洁的方式提示我们已经进入首页,还记得我们进入官网时图片旁边的名字吗直接在抓包中搜索就行了:

ca8466c27552847aea97a36504a527de.png

90a7895b7813ce7b6717167c00d9fe36.png

9040f5ef46e6b084a24f5b8f642b0896.png

因为这里param数据不是固定不变的,所以这里需要在请求是带上param数据,里面的_是13位时间戳可以通过int(time.time() * 1000)获得,并且请求头headers中Referer指向上一次,发起请求:

def get_name(req):
    this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xtgl/index_cxYhxxIndex.html'
    param_data = {
        'xt': 'jw',
        'localeKey': 'zh_CN',
        '_': str(int(time.time() * 1000)),
        'gnmkdm': 'index',
        'su': username
    }
    header['Referer'] = req.url
    request = reqs('get', this_url, param_data)
    name = re.search(r'media-heading">(.*?)</', request.text).groups()[0]
    return name

最后附上全部代码(带验证码识别,没有成功安装tesserocr的请注释掉yzm_deal.py文件中的import tesserocr,使用手动输入):

GZschool.py

import re
import requests
import time
import yzm_deal
username = '' # 验证码
password = '' # 密码
seting = 160 # 验证码识别设置的阀值
isAutoYzm = input('是否自动识别验证码(N/n取消)?')
url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header = { # 浏览器请求头
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/9.0.084.36 Safari/536.5',
}
session = requests.session() # cookie管理器
 

def reqs(action, this_url, datas={}, Time=30, error_tip='网络不流畅,重试中。。。'):
    # 请求
    while True:
        try:
            if action == 'get' or action == 'GET':
                return session.get(this_url, params=datas, headers=header, timeout=Time)
            elif action == 'post' or action == 'POST':
                return session.post(this_url, data=datas, headers=header, timeout=Time)
            break
        except Exception as e:
            if str(e).find('timed out') > -1:
                print(error_tip)
            else:
                print(e)
                input()
            continue


def lt(request):  # 登录form['lt']
    result = re.search(r'lt" value="(.*?)"', request.text).groups()[0]
    return result


def execution(request):  # 登录form['execution']
    result = re.search(r'execution" value="(.*?)"', request.text).groups()[0]
    return result


def login_url(request):  # 登录按钮链接
    result = re.search(r'fm1" action="(.*?)"', request.text).groups()[0]
    return 'https://cas.gzhu.edu.cn' + result


def captcha():  # 验证码处理
    this_url = 'https://cas.gzhu.edu.cn/cas_server/captcha.jsp'
    request = reqs('get', this_url)
    with open('code.png', 'wb')as f:
        f.write(request.content)
    yzm_deal.yzm_print(seting)
    if isAutoYzm != 'N' or isAutoYzm != 'n':
        code = yzm_deal.yzm_deal(seting)
        print('code OCR result: ' + code)
        return code
    else:
        code = input('Please input the code: ')
        return code


def login(): # 登录表单提交
    request = reqs('get', url)  # 进入首页
    this_url = login_url(request)
    header['Referer'] = url
    form_data = {
        'username': username,
        'password': password,
        'captcha': captcha(),
        'warn': 'true',
        'lt': lt(request),
        'execution': execution(request),
        '_eventId': 'submit',
        'submit': '登录'
    }
    reqs('post', this_url, form_data)
    request = reqs('get', 'http://jwxt.gzhu.edu.cn/sso/lyiotlogin')
    time.sleep(0.5)
    return request


def get_name(req): # 获取学生姓名
    this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xtgl/index_cxYhxxIndex.html'
    param_data = {
        'xt': 'jw',
        'localeKey': 'zh_CN',
        '_': str(int(time.time() * 1000)), # 13位的时间戳
        'gnmkdm': 'index',
        'su': username # 帐号
    }
    header['Referer'] = req.url
    request = reqs('get', this_url, param_data)
    name = re.search(r'media-heading">(.*?)</', request.text).groups()[0]
    return name


def main():
    req = login()
    name = get_name(req)
    print('欢迎   ' + name + '   同学')


if __name__ == '__main__':
    main()

yzm_deal.py文件

from PIL import Image
import tesserocr  # 没有安装tesserocr的请注释掉


def binarizing(img, seting): # 验证码二值化处理
    # 二值化,seting为控制阀值
    img = img.convert("L")  # 转灰度
    pixdata = img.load()
    w, h = img.size
    for x in range(w):
        for y in range(h):
            if pixdata[x, y] < seting:
                pixdata[x, y] = 0
            else:
                pixdata[x, y] = 255
    return img


def yzm_deal(seting): # 验证码识别
    img = binarizing(Image.open('code.png'), seting)
    img.show()
    img.save('code.png', quality=100)
    result = tesserocr.image_to_text(img, lang='eng')
    exclude_char_list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
    text = ''.join([x for x in result if x in exclude_char_list])
    return text


def yzm_print(seting): # 打印验证码
    # 手动输入打印验证码
    img = binarizing(Image.open('code.png'), seting)
    pixdata = img.load()
    w, h = img.size
    for y in range(h - 7):
        for x in range(w):
            if pixdata[x, y] == 0:
                print('8', end='')
            else:
                print(' ', end='')
        print()

4f95e9c5455af22a54ed626d6ed013b9.png

第一篇先就到这为止吧,明天弄进去自主选课搜索课程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值