python自动获取家里宽带的公网IP,并发送邮件

python自动获取家里宽带的公网IP,并发送邮件

前言

有时候需要在公网访问家里的NAS或者家里的服务器或者连接家里的远程桌面,但是没有公网IP无法访问家里的设备。
目前已知的解决方案:

  1. 使用第三方公司提供的内网穿透服务,如:花生壳等等,缺点:要钱
  2. 购买一台云服务器,获得一个公网IP;通过在用frp、ngrok、nps等等开源工具在本地和云服务上建立隧道,缺点:要钱、不稳定,网速受限于云服务器
  3. 使用ssh反向隧道,缺点:需要公网服务器
  4. 跟宽带运营商联系申请公网IP,缺点:申请过程漫长,受限于联通和电信宽带
  5. 本地定时自动获取公网IP,发送通知,如:邮件

由于本人没钱所以还是选择省钱的方式吧,本地定时自动获取公网IP发送通知邮件。所以。。开始搞吧

一、准备工作

准备条件:

  1. 已经安装宽带
  2. 1台路由器(最好是可以刷第三方固件,或者已经刷成了三方固件)
  3. 1台可以敲代码的电脑

二、获取公网IP并发送邮件(重头戏)

最简单的获取公网IP的方式:打开浏览器,打开百度,输入ip, ok 你就可以看到自己的公网IP了,如图:

python自动获取家里宽带的公网IP,并发送邮件

很显然这种方式并不合适。

那么第二种,打开路由器管理界面查看公网IP 也不推荐

第三种通过代码获取,这才是比较推荐的方式
那么我所了解的通过代码获取IP的方式归纳起来应该有两种:
一种是直接访问提供查看公网IP功能的服务器,获取访问的IP
例如:

import requests

url= http://jsonip.com 
# 获取IP地址
resp = requests.get(url)
info = resp.json()
public_ip = info.get( ip )

OK 5行代码就搞定了获取公网IP的问题。但是这方式有两个缺点:1.需要寄托于别人的服务器正常运行的情况,如果出现服务器维护的时候那就获取不到公网IP,2.如果你在路由器中配置了代理,那么你获取到的公网IP会是你代理服务器的IP,不过通过代理IP是无法访问家里的设备的,所以不推荐这种方式。

另一种就是直接在路由器中获取公网IP
基本思路:使用代码访问路由器管理界面,获取公网IP。可选的技术:1.使用爬虫的方式,2.使用selenium

第一使用爬虫的方式:requests库,只是简单的获取IP,就没有必要使用scrapy

我使用的是路由器刷了三方固件PandoraBox,只是做演示,提供思路,不是通用的,如需尝试需要依据自己的路由情况

分析页面:
第一需要登录,需要找到登录按钮提交的链接:

python自动获取家里宽带的公网IP,并发送邮件

查找方法:
第一种,在浏览器中按F12,查看form节点,其action属性值就是访问链接,或者也可以在浏览器中按下F12切换到Network选项卡中,点击一次提交,查看提交数据的URI; 所以提交登录的链接为:http://192.168.1.1/cgi-bin/luci

python自动获取家里宽带的公网IP,并发送邮件

第二种,使用fiddler等等抓包工具,抓取提交登录的链接:

python自动获取家里宽带的公网IP,并发送邮件

接下来就是使用代码访问这个链接自动登录

import requests

login_info = {
     username :  root ,
     password :  XXXXX 
}
url =  http://192.168.1.1/cgi-bin/luci 
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
}

def get_ip():
    info_dict = {}
    session = requests.session()
    resp = session.post(url, headers=headers, data=login_info)
    resp_info= resp.text

然后分析resp.text返回的内容,发现其中并没有我们要的IP数据,通过查看网页源代可以知道,公网IP等信息有独立的url。
如图:

python自动获取家里宽带的公网IP,并发送邮件

根据源代码,注意看:XHR.poll(5, /cgi-bin/luci/;stok=4d9ab104d86153a97b35619e7f89dad9 , { status: 1 }, 这就是路由器后台管理返回数据的真实URL, 由此我们可以知道公网IP的真实链接为:http://192.168.1.1/cgi-bin/luci/;stok=b92111c0fa47d24429f29de8b974d6b8?status=1

由于链接中的stok是动态的所以需要先获取该值,然后构造一个新的链接。

在浏览器中访问该链接,会第一让你登录,登录之后会返回如图信息:

python自动获取家里宽带的公网IP,并发送邮件

接下来实现自动登录获取IP信息:

import requests
import re

login_info = {
     username :  root ,
     password :  XXXXX 
}

url =  http://192.168.1.1/cgi-bin/luci 
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
}

def get_ip():
    info_dict = {}
    session = requests.session()
    resp = session.post(url, headers=headers, data=login_info)
    # resp_info = resp.json()

    patter = "XHR.poll(5,  /cgi-bin/luci/;stok=(.*?) , { status: 1 },"
    stock = re.compile(patter).findall(resp.text)
    info_url = url +  /;stok=  +   .join(stock) +  ?status=1 
    resp_info = session.get(info_url, headers=headers).json()

    if not resp_info:
        info_dict = { msg :  获取信息出错 }
    wan_info = resp_info.get( wan )
    leases_info = resp_info.get( leases )
    # 由于返回的信息太多了,只需要获取自己想要的数据
    if wan_info and leases_info:
        leases_str =   .join([str(leases) for leases in leases_info])
        info_dict = {
             wan信息 : {
                 类型 : wan_info.get( proto ),
                 IP地址 : wan_info.get( ipaddr ),
                 子网掩码 : wan_info.get( netmask ),
                 网关 : wan_info.get( gwaddr ),
                 DNS : wan_info.get( dns ),
                 已连接 :  {}天 .format(wan_info.get( uptime ) / 60 / 60 / 24),
            },
             连接的设备数量 : len(leases_info),
             DHCP分配设备信息 : leases_str.replace( expires ,  剩余租期 ).replace( macaddr ,  MAC地址 ).replace( ipaddr ,  IPV4地址 ).replace( hostname ,  主机名 )
        }
    return info_dict

最后再加上发送邮件的代码

直接上代码吧:

# coding: utf-8

import requests
import re
import smtplib
from email.mime.text import MIMEText
from email.header import Header

login_info = {
     username :  XXXX ,
     password :  XXXXX 
}
url =  http://192.168.1.1/cgi-bin/luci 
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
}
mail_info = {
     recv_address :  XXXX@qq.com ,
     sender_name :  XXXX@qq.com ,
     sender_pwd :  XXXXXXX ,
     smtp_server :  smtp.qq.com ,
     subject :  路由器IP信息已更新 ,
     content :  您的公网IP信息: {},其他相关信息如下:{} 

}


def get_ip():
    info_dict = {}
    session = requests.session()
    resp = session.post(url, headers=headers, data=login_info)
    # resp_info = resp.json()

    patter = "XHR.poll(5,  /cgi-bin/luci/;stok=(.*?) , { status: 1 },"
    stock = re.compile(patter).findall(resp.text)
    info_url = url +  /;stok=  +   .join(stock) +  ?status=1&_=0.3290479886974037 
    resp_info = session.get(info_url, headers=headers).json()

    if not resp_info:
        info_dict = { msg :  获取信息出错 }
    wan_info = resp_info.get( wan )
    leases_info = resp_info.get( leases )

    if wan_info and leases_info:
        leases_str =   .join([str(leases) for leases in leases_info])
        info_dict = {
             wan信息 : {
                 类型 : wan_info.get( proto ),
                 IP地址 : wan_info.get( ipaddr ),
                 子网掩码 : wan_info.get( netmask ),
                 网关 : wan_info.get( gwaddr ),
                 DNS : wan_info.get( dns ),
                 已连接 :  {}天 .format(wan_info.get( uptime ) / 60 / 60 / 24),
            },
             连接的设备数量 : len(leases_info),
             DHCP分配设备信息 : leases_str.replace( expires ,  剩余租期 ).replace( macaddr ,  MAC地址 ).replace( ipaddr ,  IPV4地址 ).replace( hostname ,  主机名 )
        }
    return info_dict


def send_message(content):
    # 设置发送邮件的内容
    msg = MIMEText(content,  plain ,  utf-8 )
    msg[ From ] = Header(mail_info.get( sender_name ))
    msg[ Subject ] = Header(mail_info.get( subject ),  utf-8 )
    msg[ To ] = Header(mail_info.get( recv_address ))
    # 发送邮件
    smtp = smtplib.SMTP()
    smtp.connect(mail_info[ smtp_server ])
    smtp.login(mail_info[ sender_name ], mail_info[ sender_pwd ])
    smtp.sendmail(mail_info[ sender_name ], mail_info[ recv_address ], msg.as_string())


info_dict = get_ip()
content =   
if info_dict.get( msg ):
    content = info_dict.get( msg )
else:
    content = mail_info.get( content ).format(info_dict.get( wan信息 ).get( IP地址 ), str(info_dict))
send_message(content)

** 另外一种直接使用selenium的方式,直接放代码:


import platform
import time
import os
from selenium import webdriver

url =  http://192.168.1.1/cgi-bin/luci 
username =  xxxx 
password =  xxxx 


def get_ip():
    option = webdriver.ChromeOptions()
    option.add_argument("--headless")  # 通过ChromeOptions设置隐藏浏览器
    option.add_argument( --no-sandbox )  # 在Linux上禁用浏览器沙盒
    driver_path = loading_file_path(
         chromedriver.exe ) if platform.system() ==  Windows  else loading_file_path(
         chromedriver )
    driver = webdriver.Chrome(executable_path=driver_path, options=option)
    driver.get(url)

    user = driver.find_element_by_xpath( //form/div[1]/fieldset/fieldset/div[1]/div/input )
    user.clear()
    user.send_keys(username)
    pwd = driver.find_element_by_id( focus_password )
    pwd.clear()
    pwd.send_keys(password)
    login_btn = driver.find_element_by_xpath( //*[@id="maincontent"]/form/div[2]/input[1] )
    login_btn.click()

    time.sleep(2)
    ip_info = driver.find_element_by_id( wan4_s ).text
    return ip_info


def loading_file_path(filename):
    # 获取当前文件路径
    current_path = os.path.abspath(__file__)
    # 获取当前文件的父目录
    father_path = os.path.abspath(os.path.dirname(current_path) + os.path.sep + ".")
    # chromedriver文件路径,获取当前目录的父目录与chromedriver拼接
    webdriver_path = os.path.join(father_path, filename)
    return webdriver_path


wan_ip = get_ip()

是不是觉得这种方式很简单,的确 使用selenium可以很简单的获取到公网IP 信息,但是这种方式部署的时候不适合NAS或者路由器

三、部署脚本

1.可以在nas中添加一个定时任务,不做演示
2.在路由器中添加定时任务,不做演示(需要使用刷了三方固件的路由器)

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容