最近TeamViewer不能正常使用(检测到商业用途,无法启动新的会话;又想骗我去用商业付费版),家里宽带的公网IP是动态分配的,远程很不方便。为摆脱被不能远程支配的恐惧,我使用python编写了一个脚本来获取动态的IP地址,并利用windows服务来管理。
在这里碰到了一些坑,一起来了解一下。
(这里也可以不用创建windows服务,直接设置任务计划定期执行脚本就行,只是执行的时候会有一个小黑窗弹出来,可以将脚本打包为exe文件并屏蔽黑窗)
创建Windows服务
1.python实现windows服务需要借助第三方库pywin32
pip install pywin32
2.这里先来一段简单的创建服务的代码(Create_WindowsService.py),暂不涉及功能
import win32serviceutil
import win32service
import win32event
import time
class PythonService(win32serviceutil.ServiceFramework):
_svc_name_ = "Send IP" # 服务名
_svc_display_name_ = "Send IP" # 服务在windows系统中显示的名称
_svc_description_ = "Send My Computer IP to me" # 服务的描述
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.run = True
def SvcDoRun(self):
while self.run:
# 功能函数()
time.sleep(60)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.run = False
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(PythonService)
这段代码中PythonService类继承了win32serviceutil.ServiceFramework类,初始化后系统服务开始启动,并调用函数SvcDoRun。由于属性self.run为真,因此SvcDoRun函数不会停止,直到我们触发停止服务,调用SvcStop函数,属性self.run被赋予假,循环跳出,windows服务停止。我们需要实现的功能也就放在该循环中。
3.这里是一些创建服务命令,当然我们也可以安装后直接在windows服务里进行操作
#1.安装服务
python Create_WindowsService.py install
#2.让服务自动启动
python Create_WindowsService.py --startup auto install
#3.启动服务
python Create_WindowsService.py start
#4.重启服务
python Create_WindowsService.py restart
#5.停止服务
python Create_WindowsService.py stop
#6.删除/卸载服务
python Create_WindowsService.py remove
4.这里可能会遇到一些问题,简单说下我遇到的一些
(1)首先,在cmd下操作以上命令,如安装服务 python Create_WindowsService.py install时,报错:拒绝服务
这是因为cmd需要管理员权限(我使用的不是administrator用户),很简单,开始——cmd——右键 以管理员身份运行
(2)出现 “1063 服务没有及时响应启动或控制请求” 类型的错误
将安装目录Python3\Lib\site-packages\win32路径下的pythonservice.exe注册一下。
这需要win32目录下有pywintypes36.dll文件,或者有该文件的环境变量
复制Python36\Lib\site-packages\pywin32_system32\pywintypes36.dll到Python3\Lib\site-packages\win32下
最后,注册命令:pythonservice.exe /register
可能会报sys.winver数据类型的错误,不会有影响,可以查看下该值,是字符型(版本号)
实现获取动态IP并发送至手机
这里采用的是把获取的IP发送到手机钉钉(也可以短信或邮件、微信等),以方便随时查看。
1.使用钉钉创建一个群聊或者团队,添加自定义机器人,复制机器人地址,之后调用api接口就可以通过机器人发送信息。
def message(text):
headers = {'Content-Type': 'application/json;charset=utf-8'}
api_url = "你的机器人webhok地址"
json_text = {
"msgtype": "text",
"at": {
"atMobiles": [“可以添加你的手机号,机器人会@你”],
"isAtAll": False
},
"text": {
"content": text
}
}
response_code = requests.post(api_url, json.dumps(json_text), headers=headers).status_code
print(response_code)
2.获取你的公网IP地址
def get_public_ip():
url = "https://ip.cn/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
}
try:
response = requests.get(url, headers=headers)
flag_num = 0
public_ip = None
while flag_num < 3:
if response.status_code == 200:
public_ip = re.search('\d+\.\d+\.\d+\.\d+', str(response.text)).group()
break
else:
flag_num += 1
return public_ip
except Exception as e:
return None
3.定义一个属性,获取并赋予其公网ip值,如果ip变化,则更新类变量的值(每次服务停止后,该属性会重新初始化)
def __init__(self, args):
…………
# 定义属性
self.local_ip = None
4.下面给出完整代码(最新升级版)
import datetime
import requests
import re
import json
import win32serviceutil
import win32service
import win32event
import time
def get_public_ip():
url = "https://ip.cn/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
}
try:
response = requests.get(url, headers=headers)
flag_num = 0
public_ip = None
while flag_num < 3:
if response.status_code == 200:
public_ip = re.search('\d+\.\d+\.\d+\.\d+', str(response.text)).group()
break
else:
flag_num += 1
return public_ip
except Exception as e:
return None
def message(text):
headers = {'Content-Type': 'application/json;charset=utf-8'}
api_url = "https://oapi.dingtalk.com/robot/send?" \
"access_token=你的机器人webhok地址"
json_text = {
"msgtype": "text",
"at": {
"atMobiles": [],
"isAtAll": False
},
"text": {
"content": text
}
}
try:
response_code = requests.post(api_url, json.dumps(json_text), headers=headers).status_code
print(response_code)
except Exception as e:
print("Send Message to DingDing failed!")
print(e)
class PythonService(win32serviceutil.ServiceFramework):
_svc_name_ = "Send IP" # 服务名
_svc_display_name_ = "Send IP" # 服务在windows系统中显示的名称
_svc_description_ = "Send My Computer IP to me" # 服务的描述
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.run = True
self.local_ip = None
def SvcDoRun(self):
while self.run:
try:
t_number = datetime.datetime.now().strftime('%M%S')
# 每小时0分、30分检测一次ip变化
if t_number == "0000" or t_number == "3000":
IP = get_public_ip()
if self.local_ip != IP:
self.local_ip = IP
message(IP)
except Exception as e:
print(e)
time.sleep(1) # 不加此延迟会占用很高的cpu
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.run = False
if __name__ == '__main__':
try:
win32serviceutil.HandleCommandLine(PythonService)
except Exception as e:
print(e)