家里的宽带是电信的。电信的宽带比联通或是移动的是贵一些,但有一个优点是可以比较简单的申请到公网ip。有了公网ip之后,我们就可以用来做一些有趣的事情,比如远程登录到家里的台式机,访问家里的黑群晖。
但需要注意的是,这个ip是不固定的,即重新拨号之后,地址将会变掉。虽然略有不方便的地方,但搭配上路由器的DDNS功能和端口转发功能,就可以使用域名来访问到这个地址下开通的服务。
最近一段时间,不知道什么原因,路由器的ddns注册功能总是异常,表现为域名注册失败。这下,在外面再也不能简单的连到家里的网络了。虽然可以通过公网ip来进行连接,但这建立在我记住了最新的ip而路由器没有重新拨号的前提下。
这几天,由于沉迷看芙莉莲,发生了几次兴冲冲想要打开emby server但却连不上的尴尬局面。也许是时候想办法解决这个问题了。首先我对ddns注册不上的问题毫无思路,我只能=退而求其次,从同步获取最新的公网ip地址方向来解决。
我的整体思路是,使用python脚本的方式来进行实现:
- 脚本持续运行。
- 每隔1个小时执行一次获取ip的操作。
- 和上次获取的ip进行比较,如ip不同,则将新的ip发送到指令的邮箱。
- 部署方案采用docker镜像的方式,部署在nas中。
首先是如何获取到公网ip。从路由器的管理台倒是能够直接查看到,但想要程序获取的话,就需要处理登录、session等等相关的问题,略显麻烦,放弃。
在google的帮助下,找到了一个可以展示ip的web页面:https://2024.ip138.com。查看了之后,确认可以正确的展示公网ip。就选择你了。
获取ip的问题解决了之后,剩下的工作就十分简单了。在chatgpt的帮助下,十分钟不到就完成了脚本的编写。其中,main
函数是脚本主题逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| import os import smtplib import time from datetime import datetime
from email.mime.text import MIMEText from email.header import Header
import requests from bs4 import BeautifulSoup
def get_public_ip(): """ 获取公网ip地址 :return: """ headers = { 'User-Agent': 'PostmanRuntime/7.26.8', 'Accept': '*/*', 'Accept-Encoding': 'gzip,deflate,br', 'Connection': 'keep-alive' }
get_ip_url = 'https://2024.ip138.com' resp = requests.get(get_ip_url, headers=headers) html=resp.text bs = BeautifulSoup(html, 'html.parser') title = bs.find_all('title') if len(title) > 0: text = title[0].text return text.split(':')[-1]
def send_email(sender, password, receiver, content): """ 使用sender向receiver发送邮件 :param sender: 发送方 :param password: 发送方token :param receiver: 接收方 :param content: 内容 :return: """ if not content: print('no content to send') return subject = f'Today\'s ip is {content}, and now is {str(datetime.now())}' message = MIMEText(content, 'plain', 'utf-8') message['From'] = Header(sender, 'utf-8') message['To'] = Header(receiver, 'utf-8') message['Subject'] = Header(subject, 'utf-8')
try: smtp_obj = smtplib.SMTP_SSL('smtp.163.com', 465) smtp_obj.login(sender, password) smtp_obj.sendmail(sender, receiver, message.as_string()) print('send successfully.') except smtplib.SMTPException as e: print(e) finally: smtp_obj.close()
def main(): previous_ip = '' sender = os.getenv('sender') password = os.getenv('password') receiver = os.getenv('receiver') if not sender or not password or not receiver: print('no environment variables') return -1 while True: try: ip = get_public_ip() except Exception as e: print(e) else: if previous_ip != ip: previous_ip = ip try: send_email(sender, password, receiver, ip) except Exception as e: print(e) else: print('no difference since last fetched.') print('now, sleep for 1 hours') time.sleep(60 * 60)
if __name__ == '__main__': main()
|
前面的环节一切顺利,到了制作docker镜像的时候遇到了点麻烦。我需要选择一个包含python执行环境的docker镜像,但pull镜像的时候才发现,包含python运行环境的基础镜像都有7、8百兆之大。为了我这么一个小小的需求,这未免有些太过于劳师动众了。后面,灵机一动,想到了将脚本打包的方式。通过使用pyinstaller
对脚本进行了打包。
使用pyinstaller
对脚本进行打包的方式是:
1
| pyinstaller -F -p ".venv/Lib/site-packages" ../main.py
|
最终得到了一个80M作用的镜像。
1 2 3 4 5 6 7
| FROM ubuntu:20.04 MAINTAINER maslke ENV TZ=Asia/Shanghai COPY spec/dist/main /root WORKDIR /root CMD ["./main"]
|
最后,在docker中创建容器的时候,设置好sender
、receiver
和password
环境变量,即可正常启动运行了。需要注意的是,发送邮箱需要开启smtp
服务。
至此,问题完美解决。