【python爬虫】校园网的自动登录脚本+python+request

利用python爬虫实现对校园网的自动登录


微信公众号内回复关键字:’‘校园网’’ 获得下载地址

摘要

​ 本文介绍了如何利用python爬虫实现对zzuli校园网的自动登录。主要利用requests包,通过模仿浏览器动作对局域网服务器发送请求,实现登录动作。用户不需要等待弹出的上网登录窗、选择运营商等一系列操作,只需要双击程序,即可完成自动登录、挤掉另一个登录账号等功能。本文思路适用于绝大部分高校校园网。

准备工作

  • 校园网环境(废话)

  • python3

  • 必要的包:

    import os
    import socket
    import time
    
    import requests
    from urllib.parse import parse_qs
    from urllib.parse import urlsplit
    
    
  • Chrome浏览器(用来抓包)

  • pycharm(可有可无)

思路

  1. 通过Chrome抓包分析浏览器登录动作。
  2. 通过python模拟每一个动作。完成一个简单的登录脚本。
  3. 通过设置配置文件实现下次双击自动登录。
  4. 通过OS包实现打开系统无线网功能。

实现步骤

1. 浏览器的动作分析

  1. 打开Chrome,并设置为Chrome为默认浏览器
  2. 按照惯例,链接zzuli-student无线网。等待跳出登录框。
  3. 登录框跳出后,F12

​ 先说一点题外话,经过我的测试,发现URL中的wlanuserip与wlanacip参数后的IP地址并不是一成不变的,随着时间,学校服务器会改变这个地址。

​ 那么岂不是说无法拿到这个地址喽?拿不到地址怎么能利用python进行后续模拟呢?完全不用担心,细心的话就会发现,Chrome弹开的一瞬间,会有一个地址跳转成上面的那个RequestURL地址。如果用fidder抓包可以抓到,Chrome抓的话手速并没有那么快。所以这里直接给出我抓到的静态地址:

http://www.msftconnecttest.com/redirect

​ 也就是说,上面的地址跳转到当前的这个地址,同时给它了两个重要的参数(这两个参数一直会用到最后)。

​ 总结下,通过对上面的url进行get请求,会重定向到一个带两个重要参数的链接(wlanuserip与wlanacip参数),之所以重要是因为会随着时间变化,如果写死在程序里,只能用一段时间,以后会失效的。【第一个分析结束】

  1. 下面来分析账户密码提交的动作

随便提交一个账户密码,然后按照箭头指示的地方清空右侧的请求。

点击登录,选择列表中第一个请求

解释:

  • 点击登录后会发现浏览器跳转到一个无关紧要的链接然后又跳到这里来了。经过我的分析发现那个链接没有什么新的信息,当我们提交账户密码信息的时候,直接对上图中的URL提交即可,当然请求头和请求数据包一定要构造正确。
  • 状态码不用管,我们只要将正确的数据提交到正确的地方就OK了。
  • 注意这个请求是:POST请求

下面来看这个动作的请求头和请求数据包

  • 请求头

两个参数:

Referer:当浏览器向web发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。(这个参数由第一个动作构造而成的)

User-Agent:告诉web我是一个浏览器而不是python

  • 请求数据包

这个就是我们的重重之重了。

DDDDD:用户名,0是一个很有魔性的参数,偶尔会变成1,技术太渣、无法深究。10086自然是账号了。unicom自然是联通的意思了。

upass:是密码

另外:我发现R6参数与DDDDD中的0是同步的。所以到时候只能勉强的动态处理下吧。

其他的都是空。

【第二个动作分析完成】

  1. 总结:

    通过第一个动作对静态链接的访问,拿到两个参数,并通过这两个参数可以构造各种链接、参数。

    通过第二个动作对数据的提交,完成登录。

2. 核心代码的实现

import requests
from urllib.parse import parse_qs
from urllib.parse import urlsplit

# 简单请求头
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
}
# 复杂请求头
login_headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    "Referer":"",
}


# url参数表
get_par = {
    "wlanuserip":"",#
    "wlanacip":"",#
    "wlanacname":"",
    "vlanid":"0",
    "ip":"",#
    "ssid":"null",
    "areaID":"null",
    "mac":"00-00-00-00-00-00"
}

# post url参数表
post_par = {
    "hostname":"10.168.6.10",
    "iTermType":"1",
    "wlanuserip":"10.66.67.119",
    "wlanacip":"10.168.6.9",
    "mac":"00-00-00-00-00-00",
    "ip":"10.66.67.119",
    "enAdvert":"0",
    "queryACIP":"0",
    "loginMethod":"1"
}
static_url = "http://www.msftconnecttest.com/redirect"

# 第一个动作
def get_url():

    # 获取静态链接的所有回复内容
    static_response = requests.get(static_url, headers=headers)


    # 从静态链接302中的跳转页面获取信,用于构造真实的登录页面地址
    static_response_302_dict = dict(parse_qs(urlsplit(static_response.url).query))

    # 写入请求参数
    get_par['wlanuserip'] = static_response_302_dict['wlanuserip'][0]
    get_par['wlanacip'] = static_response_302_dict['wlanacip'][0]
    get_par['ip'] = static_response_302_dict['wlanuserip'][0]


    real_url_head_str = "http://10.168.6.10/a70.htm?"
    real_url_par_str = 'wlanuserip=' + str(get_par['wlanuserip']) + \
                       '&wlanacip=' + str(get_par['wlanacip']) + \
                       '&wlanacname=' + str(get_par['wlanacname']) + \
                       '&vlanid=' + str(get_par['vlanid']) + \
                       '&ip=' + str(get_par['wlanuserip']) + \
                       '&ssid=' + str(get_par['ssid']) + \
                       '&areaID=' + str(get_par['areaID']) + \
                       '&mac=' + str(get_par['mac'])

    real_url = real_url_head_str + real_url_par_str

    # 构造Referer参数
    login_headers["Referer"] = real_url
    post_par["wlanuserip"] = get_par["wlanuserip"]
    post_par["wlanacip"] = get_par["wlanacip"]
    post_par["ip"] = get_par["ip"]

    # 最后的提交地址
    r_url_head = "http://10.168.6.10:801/eportal/?c=ACSetting&a=Login&protocol=http:"
    r_url_par = "&hostname=" + post_par["hostname"] + "&iTermType=" + post_par["iTermType"] + \
                "&wlanuserip=" + post_par["wlanuserip"] + "&wlanacip=" + post_par["wlanacip"] + \
                "&mac=" + post_par["mac"] + "&ip=" + post_par["ip"] + \
                "&enAdvert=" + post_par["enAdvert"] + "&queryACIP=" + post_par["queryACIP"] + \
                "&loginMethod=" + post_par["loginMethod"]
    r_url = r_url_head + r_url_par

    return r_url


if __name__ == '__main__':

    '''
        联通:unicom
        移动:cmcc
        单宽:other
    '''
    #用户数据:学号 密码 运营商
    login_data = ["541707090100","123456","unicom"]

    # 第一个动作
    URL = get_url()

    # post 请求包
    user_post = {
        "DDDDD": "",
        "upass": "",
        "R1": "0",
        "R2": "0",
        "R3": "0",
        "R6": "",
        "para": "00",
        "0MKKey": "123456",
        "buttonClicked": "",
        "redirect_url": "",
        "err_flag": "",
        "username": "",
        "password": "",
        "user": "",
        "cmd": "",
        "Login": ""
    }


    # 这里是由于不知道R6参数的含义,只能反复提交参数,有一个能命中就行了
    dynamic_R6_data = ['0', '1', '2']
    for i in range(len(dynamic_R6_data)):

        user_post["DDDDD"] = "," + dynamic_R6_data[i] + "," + login_data[0] + "@" + login_data[2]
        user_post["upass"] = login_data[1]
        user_post["R6"] = dynamic_R6_data[i]

        # 第二个动作
        requests.post(URL, data=user_post, headers=login_headers)

3.存储用户名密码便于下次直接使用

import os
file_path = os.getcwd() + "\login_data.ini"

def set_login_data():
    """
    配置登录信息
    :return:
    """
    ini_data = []
    for i in range(3):
        if i == 0:
            ini_data.append(input("请输入上网账号:"))
        if i == 1:
            ini_data.append(input("请输入上网密码:"))
        if i == 2:
            t = input("请输入运营商:\n联通【1】\n移动【2】\n单宽【3】\n")
            if t == "1":
                ini_data.append("unicom")
            if t == "2":
                ini_data.append("cmcc")
            if t == "3":
                ini_data.append("other")

    if len(ini_data) != 3:
        print("error:input data")
        return
    else:
        f = open(file_path, 'w')
        for line in ini_data:
            f.write(line + '\n')
        f.close()
    pass
def get_login_data():
    """
    读取配置信息
    :return:
    """
    temp = []
    try:
        f = open(file_path)
        for line in f.readlines():
            temp.append(line.strip('\n'))
        print("登录账号:"+str(temp[0]))
        f.close()
        return temp
    except IOError:
        print("login_data.ini 文件找不到")
        return []
    pass

if __name__ == '__main__':

    if os.access(file_path, os.F_OK):
        print("正在读取配置文件...")
    else:
        print("请按要求配置登录信息:\n")
        set_login_data()
        print("配置成功!正在进行登录操作...")

    # 这个login_data就可以代替上面代码中自己定义的了
    login_data = get_login_data()

pass

4.小细节

【实现打开计算机的wifi】

def open_wifi():
    """
    dos命令打开wifi并链接校园网
    :return:
    """
    os.system('netsh wlan connect name=zzuli-student')
    time.sleep(1)

【检查是否成功联网】

ps:作恶多端的百度唯一的用处就是检测网络是否联通

def is_net_ok():
    """
    判断网络是否链接
    :return:
    """
    s = socket.socket()
    s.settimeout(1)
    try:
        status = s.connect_ex(('www.baidu.com', 443))# 
        if status == 0:
            s.close()
            return True
        else:
            s.close()
            return False
    except Exception as e:
        return False

5.打包成exe

  • 直接把脚本给用户不知道会出现什么奇葩问题
  • 打包成exe会让性能削弱很多
  • 不过还是要打包

【打包】pyinstaller -F 文件名.py

采用pyinstaller打包成exe,相关的教程遍地都是(搜索:pyinstaller打包 exe)

6.关于更新

这个版本依旧是有很多bug的,所以我会持续更新的。

项目源码以及exe文件,关注微信公众号:并非一无所有

猜你喜欢

转载自blog.csdn.net/F_zmmfs/article/details/88682188