2018年11月11日 星期日

利用樹莓派的 Python 程式定時回報外網 IP 的方法 (Heart beat)

今天下午弄完滷冬瓜後, 花了一整個下午研究樹莓派上執行的 reportip2.py 為何近兩周以來都沒有回報外網 IP 變更訊息, 原先以為問題可能是 myip.com.tw 已經從 http 改成 https 協定, 導致呼叫 visit() 函數時無法取得 IP 資料所致. 但後來發覺 reportip2.py 的檔案模式竟然不是可執行模式, 奇怪, 難道有駭客入侵改了嗎? 總之, 兩個都改了之後就 OK 了 :

pi@raspberrypi:~ $ ls reportip2.py -al
-rw-r--r-- 1 pi pi 2960 Dec 10  2017 reportip2.py
pi@raspberrypi:~ $ sudo chmod +x /home/pi/reportip2.py 
pi@raspberrypi:~ $ ls reportip2.py -al 
-rwxr-xr-x 1 pi pi 2960 Dec 10  2017 reportip2.py 
pi@raspberrypi:~ $ python2 reportip2.py 
Network is Ready!
IP information from http://myip.com.tw
IP not change. 111.254.54.xx 192.168.2.192

reportip2.py 會將目前最新外網 IP 紀錄於文字檔中, 每次比對時會讀取現在的外網 IP 與檔案所記錄之 IP 比較, 若不一樣才以郵件通報. 這次我一直到兩周後連線失敗才發現這問題似乎太晚, 應該改為 HEART BEAT 模式, 例如每小時通報一次, 不需要去比對有無變異, 這樣只要沒有收到 email 就表示樹莓派有問題了, 而且從郵件中找尋最新 IP 也很快.

關於回報 IP 以便能從遠端存取樹莓派的方法參考之前的測試紀錄 :

從外網以 SSH 存取樹莓派的方法
Python 學習筆記 : 網頁擷取 (一) 使用 urllib 與 HTMLParser
Python 學習筆記 : 以 Gmail 寄送郵件的方法 (一)
樹莓派自動偵測網路斷線時重開機的方法

目前的 reportip2.py 是用 Python 2 改寫的 (利用我的 Hinet 信箱寄送郵件), 當時也嚐試改成 Python 3 版但沒成功, reportip3.py 就晾在那邊, 前陣子測試了用 Gmail 傳送郵件的方法, 因此這次就改為用 Gmail 來寄送 IP 訊息, 且改用 Python 3 來寫, 原始碼 reportip3.py 如下 :


#reportip3.py : send mail by Gmail with TLS
import smtplib
from urllib import request
import re
import socket
import time

class Getmyip:
    def getip(self):
        try:
            myip=self.visit("https://myip.com.tw")
        except:
            try:
                myip=self.visit("http://cmp.nkuht.edu.tw/info/ip.asp")
            except:
                try:
                    myip=self.visit("http://dir.twseo.org/ip-check.php")
                except:
                    print("Fail to get the Network ip.")
                    print("Get the LAN ip.")
                    myip=get_lan_ip()
        return myip
    def visit(self, url):
        opener=request.urlopen(url,timeout=20)
        #print(opener.headers.get_content_charset())
        if url==opener.geturl():
            str=opener.read().decode(opener.headers.get_content_charset())
            print("IP information from", url)
        return re.search('\d+\.\d+\.\d+\.\d+',str).group(0)

def get_network_ip():
    getmyip=Getmyip()
    localip=getmyip.getip()
    return localip

def get_lan_ip():
    s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("1.1.1.1",80))
    ipaddr=s.getsockname()[0]
    s.close()
    return ipaddr
 
#check_network()
ipaddr=get_network_ip()
lanip=get_lan_ip()
emailip=str(ipaddr)+" "+str(lanip)
print("current ip: {}".format(emailip))

smtp=smtplib.SMTP("smtp.gmail.com", 587)
smtp.ehlo()
smtp.starttls()
smtp.login("mygmail@gmail.com", "yguxhsurqwpseksw")
from_addr="mygmail@gmail.com"
to_addr=["myhinetmail@msa.hinet.net"]
msg="Subject:My IP\n\
From:Raspberry Pi 3\n\
To:My IP\n\
Cc:mycompanymail@mycompany.com.tw\n\
{}".format(emailip)
status=smtp.sendmail(from_addr, to_addr, msg)
if status=={}:
    print("郵件傳送成功!")
else:
    print("郵件傳送失敗!")
smtp.quit()

檔案存成 reportip3.py, 然後用 chmod 指令變更為可執行 : 

pi@raspberrypi:~ $ ls reportip3.py -ls    
4 -rw-r--r-- 1 pi pi 1800 Nov 11 17:03 reportip3.py    
pi@raspberrypi:~ $ sudo chmod +x /home/pi/reportip3.py    
pi@raspberrypi:~ $ ls reportip3.py -ls
4 -rwxr-xr-x 1 pi pi 1800 Nov 11 17:03 reportip3.py     

用 python3 指令執行成功 : 

pi@raspberrypi:~ $ python3 reportip3.py
IP information from https://myip.com.tw
current ip: 111.254.54.xx 192.168.2.192
Send mail successfully!

最後修改 crontab, 加入於整點執行 reportip3.py 的定時器 : 

pi@raspberrypi:~ $ crontab -e    
pi@raspberrypi:~ $ crontab -l 
*/10 * * * * /usr/bin/python2 /home/pi/reportip2.py    
0 * * * * sudo /usr/local/checkwifi.sh
0 * * * * /usr/bin/python3/home/pi/reportip3.py   

這是整點執行的 crontab. 

如果是想每個小時執行一次, 但不是整點, 而是 crontab 更新的那時開始每隔一小時的話要這麼寫 :

pi@raspberrypi:~ $ crontab -l 
*/10 * * * * /usr/bin/python2 /home/pi/reportip2.py
0 * * * * sudo /usr/local/checkwifi.sh
* */1 * * * /usr/bin/python3 /home/pi/reportip3.py  

查看 crontab 日誌 :

pi@raspberrypi:~ $ sudo cat /var/log/cron.log 

參考 :

定時任務之crontab命令
第十五章、例行性工作排程(crontab)
crontab 表达式每小时执行一次(整点整分)


2018-11-12 補充 :

今早收信確認每小時 (30 分) 都會收到樹莓派發出的外網 IP 通知信 :






只要漏掉一封信就表示有問題, 要嘛是網路斷線, 要嘛是樹莓派系統出問題.

2019-07-27 補充:

今天發現手機收到的 email 中, 鄉下 Pi 3 傳回來的 IP 為 192.168.2.192, 這表示查詢外網 IP 有問題, 檢查 reportip3.py 應該是每一個 try 都失敗, 原因可能出在 visit(), 它原先是用 urllib.request, 乾脆改成 requests 好了 :

#reportip3.1.py
#Send mail by Gmail with TLS
import smtplib
import request3
import re
import socket
import time

class Getmyip:
    def getip(self):
        try:
            myip=self.visit("https://myip.com.tw") #utf-8
        except:
            try:
                myip=self.visit("http://cmp.nkuht.edu.tw/info/ip.asp") #big-5
            except:
                try:
                    myip=self.visit("http://dir.twseo.org/ip-check.php")
                except:
                    print("Fail to get the Network ip.")
                    print("Get the LAN ip.")
                    myip=get_lan_ip()
        return myip
    def visit(self, url):
        r=requests.get(url)
        #print(r.encoding)
        return re.search('\d+\.\d+\.\d+\.\d+',r.text).group(0)

def get_network_ip():
    getmyip=Getmyip()
    localip=getmyip.getip()
    return localip

def get_lan_ip():
    s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("1.1.1.1",80))
    ipaddr=s.getsockname()[0]
    s.close()
    return ipaddr

#check_network()
ipaddr=get_network_ip()
lanip=get_lan_ip()
emailip=str(ipaddr)+" "+str(lanip)
print("current ip: {}".format(emailip))

smtp=smtplib.SMTP("smtp.gmail.com", 587)
smtp.ehlo()
smtp.starttls()
smtp.login("yhhuang1966@gmail.com", "yguxhsurqwpseksw")
from_addr="yhhuang1966@gmail.com"
to_addr=["tony1966@ms5.hinet.net"]
msg="Subject:Meinung IP\n\
From:Raspberry Pi 3\n\
To:MyIP\n\
Cc:yhhuang@cht.com.tw\n\
{}".format(emailip)
status=smtp.sendmail(from_addr, to_addr, msg)
if status=={}:
    print("Send mail successfully!")
else:
    print("Send mail in failure!")
smtp.quit()


2 則留言 :

匿名 提到...

您好 最近遇到一個問題
要如何使樹莓派開機時 必定可以連上iPhone的基地台

小狐狸事務所 提到...

要編寫 /etc/network/interfaces 這個檔案, 設定 wpa-ssid 與 wpa-psk 為您的 iphone 基地台即可, 參考 :
http://yhhuang1966.blogspot.com/2016/03/wifi-fw150us.html