公衆號第一次發文,分享一個小案例,所有樣本在:
https://github.com/suchocolate/test/tree/master/autowifi
1.問題現象
最近把樹莓派留在了機房想遠程登錄,但似乎每到夜裏機房WIFI就會斷網,導致無法遠程樹莓派(如何遠程下回有機會再講)。然而早上到機房後看樹莓派的無線連接並沒有斷,只需要登陸認證網頁認證一下就可以再次上網。所以基本判斷不是無線網卡或WIFI信號的問題,可能是WIFI設置了認證租期,那麼只要讓樹莓派能夠在斷網時自動重新認證應該就可以保持上網了。
圖1.1 樹莓派4B
2.觀察認證頁面
爲了讓樹莓派能夠自動重認證,我們先看一下認證的頁面。首先用自己的電腦接入WIFI,然後用瀏覽器F12打開元素查看器-網絡,登陸認證頁面看看。
圖2.1
可以看到這個網頁加載了1個JavaScript和2個圖片。
網頁一般由3種元素HTML(構架)+CSS(外觀)+JavaScript(行爲)組成,我們先來看下基礎的HTML。
圖2.2
第60行表示需要加載外部JavaScript,就是上面圖中的loginscript.js。
<script language="javascript"src="/loginscript.js"></script>
第80行表示頁面中有一個表單,當提交表單時使用HTTP 的POST方法,並向https://192.0.2.100/login.html提交。
<FORM method="post"ACTION="/login.html">
其實登陸頁面中整個有文字的部分都是這個表單。
圖2.3
第132和138行是實際提交信息的輸入框username和password。
第144行是一個獲取按鈕的函數,這個函數定義在loginscript.js中,讓我們看下這個腳本。
圖2.4
圖2.5
這個函數的功能是向HTML主體寫入一個button,當點擊這個按鈕時調用函數onClickFn形參指向的實參,就是調用時輸入的submitAction()。
讓我們看一下這個函數:
圖2.6
雖然不知道函數具體執行的內容,但最後都會調用form的submit方法來提交數據,所以這個按鈕就是指導瀏覽器發送POST提交賬號密碼的方法,那我們就用瀏覽器點擊一下按鈕看看效果。
3.觀察登陸
填寫賬號密碼,點擊按鈕後,瀏覽器向https://192.0.2.100/login.htmlPOST了消息。
圖3.1
點擊響應看看效果,LoginSuccessful。
圖3.2
點擊預覽的倒三角收起預覽,可以看到網頁的代碼:
圖3.3
點擊參數查看提交的信息,原來實際提交的表單就在這裏。
圖3.4
十分不推薦這種明文發送賬號密碼的認證機制。
4.用python登陸吧
現在知道了要提交的數據,那麼就可以準備腳本讓樹莓派認證了。
樹莓派默認安裝了python,並且默認有requests模塊,就用requests模塊實現認證。
import requests
在準備post消息之前還需要準備http的頭部消息。
頭部數據從圖3.1中複製獲得,表單數據從圖3.4中複製獲得,把他們轉成字典,供requests使用。
headers={
"Host": "192.0.2.100",
"User-Agent": "Mozilla/5.0",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type":"application/x-www-form-urlencoded",
"Content-Length": "156",
"Origin": "https://192.0.2.100",
"Connection": "keep-alive",
"Referer":"https://192.0.2.100/login.html?redirect=192.0.2.100/",
"Upgrade-Insecure-Requests": "1"
}
data={
"buttonClicked":"4",
"err_flag":"0",
"err_msg":"",
"info_flag":"0",
"info_msg":"",
"redirect_url":"http%3A%2F%2F192.0.2.100%2F",
"network_name":"Guest+Network",
"username":"VIP12",
"password":"123456"
}
#最後用requests登陸即可
requests.post('https://192.0.2.100/login.html',data=data,headers=headers,verify=False)
# 'https://192.0.2.100/login.html 就是認證的網頁。
# data是提交的表單。
# headers是http頭部。
# verify=False是關閉SSL檢查,因爲這個網站的證書沒有在公網註冊。
現在解決了重認證的問題,那麼接下來解決如何檢測斷網:
# 引入os用以使用系統命令,引入time用以延時和打印時間
import os
import time
# 定義子函數,循環檢測是否斷網
def autowifi():
while (True):
# ping外網地址3次,每次等5秒
result =os.system(u"ping 1.2.4.8 -c 3 -W 5")
# 如果ping失敗
if result !=0:
try:
# iw是無線網卡的操控命令
os.system(u"iw wlan0 disconnect")
# sleep讓程序暫停,目的是留給前一個命令充分的時間執行
time.sleep(5)
# wifi名稱需要根據實際情況替換
os.system(u"iw wlan0 connect wifiname")
time.sleep(5)
# 重新獲取IP
os.system(u"dhclient wlan0")
time.sleep(10)
r=requests.post('https://192.0.2.100/login.html',data=data,headers=headers,verify=False)
time.sleep(5)
# 打印post的時間和結果,status_code是requests對象自帶屬性
print(time.asctime(time.localtime(time.time())),r.status_code)
exceptException as e:
# 如果有報錯,打印報錯,不退出程序
print(e)
else:
# 如果ping 正常說明沒有斷網,打印時間
print(time.asctime(time.localtime(time.time())),"normal")
print("="*50)
# 睡10分鐘再測
time.sleep(600)
# 主語句調用子函數
if __name__=='__main__':
autowifi()
5.腳本上線
爲了持續監控需要python腳本保持在後臺,那麼用nohup運行:
nohup python -u autowifi.py > nohup.out 2>&1 &
# -u:讓系統命令直接輸出到標準輸出
# >nohut.out:輸出到log文件
# 2>&1:報錯也輸出到log文件
# &:後臺運行
運行幾天後的效果。