天天看點

技術| Python的從零開始系列連載(三十二)

綜述

最近山大軟體園校區QLSC_STU無線網掉線掉的厲害,連上之後平均十分鐘左右掉線一次,很是讓人心煩,還能不能愉快地上自習了?能忍嗎?反正我是不能忍了,嗯,自己動手,豐衣足食!寫個程式解決掉它!

假若你不能連這個無線,那就照照思路啦~

決戰前夕

首先我們看一下那個驗證頁面是咋樣滴,上個圖先

技術| Python的從零開始系列連載(三十二)

嘿,這界面還算可以把,需要我們輸入的東西就是倆,一個就是學号,另一個是身份證号後六位,然後就可以登入,享受免費的無線網啦。

不過不知道誰設定了個登入時長,一段時間後就會掉線了,于是,自動模拟登陸系統就要應運而生啦。

來,我們先點選一下連接配接,看一下浏覽器怎麼工作的。

按下F12,監聽網絡,我們點選第一個響應,也就是login.jsp,看一下。

技術| Python的從零開始系列連載(三十二)
我們具體看一下headers,裡面form送出了什麼東西,真的是茫茫多的資料啊。
技術| Python的從零開始系列連載(三十二)

嗯,一目了然POST的資料和送出的位址。 讓我們來分析幾個資料吧:

ClientIP:目前用戶端的IP位址,在山大軟體園校區這個位址是211.87開頭的

timeoutvalue:連接配接等待時間,也就是俗話說的timeout

StartTime:登入時間,也就是在你登入的那一刻的時間戳,這個時間戳是13位的,精确到了毫秒,不過一般是10位的,我們加3個0就好了

shkOvertime:登入持續時間,這個資料預設是720,也就是12分鐘之後,登入就失效了,自動掉線,我們可以手動更改

username:學号

password:密碼,也就是我們身份證号後六位

我們需要在登入的時候把form表單中的所有資訊都POST一下,然後就可以完成登入啦。 萬事俱備,隻欠東風,來來來,程式寫起來!

一觸即發

說走咱就走啊,天上的星星參北鬥啊!

登陸位址:Request URL:

http://192.168.8.10/portal/login.jsp?Flag=0 首先,我們需要驗證一下IP位址,先寫一個擷取IP位址的函數,首先判斷目前IP是不是211.87開頭的,如果是的話,證明連接配接的IP是有效的。 首先我們寫一個擷取本機IP的方法:

self.ip_pre = "211.87"    
#擷取本機無線IP
    def getIP(self):
        local_iP = socket.gethostbyname(socket.gethostname())
        if self.ip_pre in str(local_iP):
            return str(local_iP)
        ip_lists = socket.gethostbyname_ex(socket.gethostname())

        for ip_list in ip_lists:
            if isinstance(ip_list, list):
                for i in ip_list:
                    if self.ip_pre in str(i):
                        return str(i)
            elif type(ip_list) is types.StringType:
                if self.ip_pre in ip_list:
                    return ip_list
           

這個方法利用了gethostbyname和gethostbyname_ex方法,擷取了各個網卡的IP位址,周遊一下,找到那個211.87開頭的IP,傳回 接下來,擷取到IP之後,我們便可以建構form,然後進行模拟登陸了。

#模拟登入
    def login(self):
        print self.getCurrentTime(), u"正在嘗試認證QLSC_STU無線網絡"
        ip = self.getIP()
        data = {
            "username": self.username,
            "password": self.password,
            "serverType": "",
            "isSavePass": "on",
            "Submit1": "",
            "Language": "Chinese",
            "ClientIP": self.getIP(),
            "timeoutvalue": 45,
            "heartbeat": 240,
            "fastwebornot": False,
            "StartTime": self.getNowTime(),
            #持續時間,超過這個時間自動掉線,可進行設定
            "shkOvertime": self.overtime,
            "strOSName": "",
            "iAdptIndex": "",
            "strAdptName": "",
            "strAdptStdName": "",
            "strFileEncoding": "",
            "PhysAddr": "",
            "bDHCPEnabled": "",
            "strIPAddrArray": "",
            "strMaskArray": "",
            "strMask": "",
            "iDHCPDelayTime": "",
            "iDHCPTryTimes": "",
            "strOldPrivateIP": self.getIP(),
            "strOldPublicIP": self.getIP(),
            "strPrivateIP": self.getIP(),
            "PublicIP": self.getIP(),
            "iIPCONFIG":0,
            "sHttpPrefix": "http://192.168.8.10",
            "title": "CAMS Portal"
        }
        #消息頭
        headers = {
            'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',
            'Host': '192.168.8.10',
            'Origin': 'http://192.168.8.10',
            'Referer': 'http://192.168.8.10/portal/index_default.jsp?Language=Chinese'
        }
        post_data = urllib.urlencode(data)
        login_url = "http://192.168.8.10/portal/login.jsp?Flag=0"
        request = urllib2.Request(login_url, post_data, headers)
        response = urllib2.urlopen(request)
        result = response.read().decode('gbk')           

比較多的内容就在于form表單的資料内容以及請求頭,後來利用urllib2的urlopen方法實作模拟登陸。

如果大家對此不熟悉,可以參見

Urllib的基本使用:

http://cuiqingcai.com/947.html

這樣,登入後的結果就會儲存在result變量中,我們隻需要從result中提取出我們需要的資料就可以了。

乘勝追擊

接下來,我們就分析一下資料啦,結果有這麼幾種:

1.登入成功 2.已經登入 3.使用者不存在 4.密碼錯誤 5.未知錯誤

好,利用result分析一下結果

#列印登入結果
    def getLoginResult(self, result):
        if u"使用者上線成功" in result:
            print self.getCurrentTime(),u"使用者上線成功,線上時長為",self.overtime/60,"分鐘"
        elif u"您已經建立了連接配接" in result:
            print self.getCurrentTime(),u"您已經建立了連接配接,無需重複登陸"
        elif u"使用者不存在" in result:
            print self.getCurrentTime(),u"使用者不存在,請檢查學号是否正确"
        elif u"使用者密碼錯誤" in result:
            pattern = re.compile('<td class="tWhite">.*?2553:(.*?)</b>.*?</td>', re.S)
            res = re.search(pattern, result)
            if res:
                print self.getCurrentTime(),res.group(1),u"請重新修改密碼"
        else:
            print self.getCurrentTime(),u"未知錯誤,請檢查學号密碼是否正确"           

通過字元串比對和正規表達式,我們分辨并提取出了上述五種情況。

增加循環檢測 既然是檢測網絡是否斷開,那麼我們隻需要每隔一段時間檢測一下就好了,那就10秒吧。

因為這個10秒是可配置的,為了友善配置,統一配置到__init__方法裡面。

#檢測間隔時間,機關為秒
self.every = 10           

然後,我們寫一個循環來檢測一下

while True:
            nowIP = self.getIP()
            if not nowIP:
                print self.getCurrentTime(), u"請檢查是否正常連接配接QLSC_STU無線網絡"
            else:
                print self.getCurrentTime(),u"成功連接配接了QLSC_STU網絡,本機IP為",nowIP
                self.login()
                while True:
                    can_connect = self.canConnect()
                    if not can_connect:
                        nowIP = self.getIP()
                        if not nowIP:
                            print self.getCurrentTime(), u"目前已經斷線,請確定連接配接上了QLSC_STU網絡"
                        else:
                            print self.getCurrentTime(), u"目前已經斷線,正在嘗試重新連接配接"
                            self.login()
                    else:
                        print self.getCurrentTime(), u"目前網絡連接配接正常"
                    time.sleep(self.every)
            time.sleep(self.every)           

其中我們用到了canConnect方法,這個就是檢測網絡是否已經斷開的方法,我們可以利用ping百度的方法來檢測一下。

方法實作如下

#判斷目前是否可以聯網
    def canConnect(self):
        fnull = open(os.devnull, 'w')
        result = subprocess.call('ping www.baidu.com', shell = True, stdout = fnull, stderr = fnull)
        fnull.close()
        if result:
            return False
        else:
            return True           

好啦,所有的要點我們已經逐一擊破,等着凱旋吧

收拾戰場

好了,所有的代碼要點已經被我們攻破了,接下來就整理一下,讓他們組合起來,變成一個應用程式吧。

__author__ = 'CQC'
#-*- coding:utf-8 -*-

import urllib
import urllib2
import socket
import types
import time
import re
import os
import subprocess

class Login:

    #初始化
    def __init__(self):
        #學号密碼
        self.username = '201200131012'
        self.password = 'XXXXXX'
        #山大無線STU的IP網段
        self.ip_pre = '211.87'
        #登入時長
        self.overtime = 720
        #檢測間隔時間,機關為秒
        self.every = 10

    #模拟登入
    def login(self):
        print self.getCurrentTime(), u"正在嘗試認證QLSC_STU無線網絡"
        ip = self.getIP()
        data = {
            "username": self.username,
            "password": self.password,
            "serverType": "",
            "isSavePass": "on",
            "Submit1": "",
            "Language": "Chinese",
            "ClientIP": self.getIP(),
            "timeoutvalue": 45,
            "heartbeat": 240,
            "fastwebornot": False,
            "StartTime": self.getNowTime(),
            #持續時間,超過這個時間自動掉線,可進行設定
            "shkOvertime": self.overtime,
            "strOSName": "",
            "iAdptIndex": "",
            "strAdptName": "",
            "strAdptStdName": "",
            "strFileEncoding": "",
            "PhysAddr": "",
            "bDHCPEnabled": "",
            "strIPAddrArray": "",
            "strMaskArray": "",
            "strMask": "",
            "iDHCPDelayTime": "",
            "iDHCPTryTimes": "",
            "strOldPrivateIP": self.getIP(),
            "strOldPublicIP": self.getIP(),
            "strPrivateIP": self.getIP(),
            "PublicIP": self.getIP(),
            "iIPCONFIG":0,
            "sHttpPrefix": "http://192.168.8.10",
            "title": "CAMS Portal"
        }
        #消息頭
        headers = {
            'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36',
            'Host': '192.168.8.10',
            'Origin': 'http://192.168.8.10',
            'Referer': 'http://192.168.8.10/portal/index_default.jsp?Language=Chinese'
        }
        post_data = urllib.urlencode(data)
        login_url = "http://192.168.8.10/portal/login.jsp?Flag=0"
        request = urllib2.Request(login_url, post_data, headers)
        response = urllib2.urlopen(request)
        result = response.read().decode('gbk')
        self.getLoginResult(result)


    #列印登入結果
    def getLoginResult(self, result):
        if u"使用者上線成功" in result:
            print self.getCurrentTime(),u"使用者上線成功,線上時長為",self.overtime/60,"分鐘"
        elif u"您已經建立了連接配接" in result:
            print self.getCurrentTime(),u"您已經建立了連接配接,無需重複登陸"
        elif u"使用者不存在" in result:
            print self.getCurrentTime(),u"使用者不存在,請檢查學号是否正确"
        elif u"使用者密碼錯誤" in result:
            pattern = re.compile('<td class="tWhite">.*?2553:(.*?)</b>.*?</td>', re.S)
            res = re.search(pattern, result)
            if res:
                print self.getCurrentTime(),res.group(1),u"請重新修改密碼"
        else:
            print self.getCurrentTime(),u"未知錯誤,請檢查學号密碼是否正确"

    #擷取目前時間戳,13位
    def getNowTime(self):
        return str(int(time.time()))+"000"

    #擷取本機無線IP
    def getIP(self):
        local_iP = socket.gethostbyname(socket.gethostname())
        if self.ip_pre in str(local_iP):
            return str(local_iP)
        ip_lists = socket.gethostbyname_ex(socket.gethostname())

        for ip_list in ip_lists:
            if isinstance(ip_list, list):
                for i in ip_list:
                    if self.ip_pre in str(i):
                        return str(i)
            elif type(ip_list) is types.StringType:
                if self.ip_pre in ip_list:
                    return ip_list

    #判斷目前是否可以聯網
    def canConnect(self):
        fnull = open(os.devnull, 'w')
        result = subprocess.call('ping www.baidu.com', shell = True, stdout = fnull, stderr = fnull)
        fnull.close()
        if result:
            return False
        else:
            return True

    #擷取目前時間
    def getCurrentTime(self):
        return time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime(time.time()))

    #主函數
    def main(self):
        print self.getCurrentTime(), u"您好,歡迎使用模拟登陸系統"
        while True:
            nowIP = self.getIP()
            if not nowIP:
                print self.getCurrentTime(), u"請檢查是否正常連接配接QLSC_STU無線網絡"
            else:
                print self.getCurrentTime(),u"成功連接配接了QLSC_STU網絡,本機IP為",nowIP
                self.login()
                while True:
                    can_connect = self.canConnect()
                    if not can_connect:
                        nowIP = self.getIP()
                        if not nowIP:
                            print self.getCurrentTime(), u"目前已經掉線,請確定連接配接上了QLSC_STU網絡"
                        else:
                            print self.getCurrentTime(), u"目前已經掉線,正在嘗試重新連接配接"
                            self.login()
                    else:
                        print self.getCurrentTime(), u"目前網絡連接配接正常"
                    time.sleep(self.every)
            time.sleep(self.every)

login = Login()
login.main()
           

來,我們來運作一下,看下效果吧! 執行

python login.py           

目前是可以聯網的,我分别在網頁上操作執行了斷開,操作,程式自動檢測到掉線,自動重新連接配接。

接下來我直接斷開了QLSC_STU網絡的連結,程式同樣檢測到QLSC_STU這個熱點沒有連接配接上,提示使用者連結。

接下來我重新連接配接上了這個熱點,由于剛才已經登入上線,且持續時間較短,網絡自動恢複正常。

下面是運作結果:

技術| Python的從零開始系列連載(三十二)

嗯,這樣我們就是實作了自動掉線的檢測和模拟登入。

凱旋而歸

咿呀伊爾喲,想約妹子上自習嗎?那就趕緊來試試吧!一網在手,天下我有!追男神女神都不再是夢想!

如果有問題,歡迎留言讨論,代碼肯定有不完善的地方,僅供參考。

原文釋出時間為:2018-12-7

本文作者:燈塔大資料

本文來自雲栖社群合作夥伴“

燈塔大資料

”,了解相關資訊可以關注“DTbigdata”微信公衆号