解決方案
- 靜態ip(放棄)
- 申請一個靜態ip
- 價格較貴,只有公司可以申請
- 使用DDNS(放棄)
- 通過域名解析到公網ip
- 通過域名訪問設備
- 官方光貓不支持DDNS
- 使用腳本(采用)
- 通過腳本獲取公網ip
- 通過腳本發送到郵箱
- 通過腳本配置DDNS
我的方案
在服務器上部署一個腳本,每隔一段時間登陸本地網關控制網站爬取公網ip信息,如果發生變化,就通過郵箱發送給我。
我設想的進一步是發生變化就自動配置DDNS,但是由于我暫未使用域名,所以暫時沒完成這個功能。
我的網關是TP-Link電信的,可能對于不同平臺的網關設備的管理界面,需要特定的爬取方法,請更具自己的設備做個性化調整。
動態ip解決腳本
一個從網關管理界面拉取信息的工具
- 爬取ip信息
- 每隔1小時爬取一次
- 發送到對應設備
- 使用郵箱發送通知
- DDNS配置(未完成)
- 使用阿里云的DDNS服務
- 通過腳本配置DDNS
- 通過腳本修改域名解析
遇到的問題
我在windows上很快完成了這個腳本,但是在linux上卻遇到了很多問題
首先是selenium的chrome驅動問題,它無法自動下載
于是我手動下載了對應的驅動,寫明了路徑,解決此問題
systemctl服務配置問題,原先ai說需要使用虛擬屏幕環境,但是實際不需要。只需要把selenium的配置添加–headless參數即可
源碼
import sys # 導入sys模塊以獲取命令行參數import requests # 導入requests庫用于發送HTTP請求
from bs4 import BeautifulSoupfrom selenium import webdriver # 導入selenium庫用于自動化瀏覽器操作
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Optionsimport timeimport traceback # 導入traceback模塊以獲取完整的錯誤信息import smtplib # 導入smtplib庫用于發送電子郵件
from email.mime.text import MIMEText
from email.utils import formataddrROUTER_IP = "your_router_ip" # 替換為路由器IP地址
USERNAME = "your_username" # 替換為路由器登錄用戶名
PASSWORD = "your_password" # 替換為路由器登錄密碼
WANIP = "0.0.0.0" # 初始WAN IP地址CHECK_GAP = 60 * 60 # 檢查間隔時間(秒),1小時SENDER_EMAIL = "your_email@example.com" # 替換為發送通知的郵箱
QQ_EMAIL_PASSWORD = "your_email_password" # 替換為郵箱的授權碼或密碼
TARGET_EMAIL = "target_email@example.com" # 替換為目標郵箱ChromeDriverPath = "/path/to/chromedriver" # 替換為ChromeDriver的路徑def selenium_login():# 配置Chrome選項(可選)chrome_options = webdriver.ChromeOptions()chrome_options.add_argument('--headless') # 無頭模式必須chrome_options.add_argument("--no-sandbox") # Linux 必加chrome_options.add_argument('--disable-dev-shm-usage')chrome_options.add_argument("--disable-blink-features=AutomationControlled")service = webdriver.ChromeService(executable_path=ChromeDriverPath) # 修改為你的路徑driver = webdriver.Chrome(service=service, options=chrome_options)#driver = webdriver.Chrome(options=chrome_options)try:driver.get(f"http://{ROUTER_IP}/cgi-bin/luci")# 關鍵修復:等待元素可交互(最多等10秒)wait = WebDriverWait(driver, 3)# 方法1:直接移除disabled屬性(更可靠)driver.execute_script("""document.getElementById("login_username").removeAttribute("disabled");document.getElementById("login_password").removeAttribute("disabled");""")# 方法2:傳統等待方式username_field = wait.until(EC.element_to_be_clickable((By.ID, "login_username")))password_field = wait.until(EC.element_to_be_clickable((By.ID, "login_password")))# 輸入憑據username_field.clear()username_field.send_keys(USERNAME)password_field.send_keys(PASSWORD)# 提交登錄submit_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[type='submit']")))submit_button.click()# 驗證登錄time.sleep(3)if "/cgi-bin/luci/" in driver.current_url:print("? 登錄成功")return driver;else:print("? 登錄失敗,當前URL:", driver.current_url)driver.save_screenshot("login_fail.png")except Exception as e:print("\n? 完整錯誤信息:")print(traceback.format_exc()) # 關鍵修改:打印完整錯誤
def get_ip_from_html(driver):try:driver.get(f"http://{ROUTER_IP}/cgi-bin/luci/admin/settings/info")time.sleep(1.5) # 等待頁面加載完成html = driver.page_sourcesoup = BeautifulSoup(html, "html.parser")# 解析IP地址ip_element = soup.find("span", id="WANIP")if ip_element:wan_ip = ip_element.text.strip()return wan_ipelse:print("? 無法找到WAN IP元素")return Noneexcept Exception as e:print("\n? 完整錯誤信息:")raise
def send_notification_by_email(ip, sender_email, password, target_email):try:content = f"新的WAN IP地址是 {ip}"msg = MIMEText(content, "plain", "utf-8")msg["Subject"] = "WAN IP地址變化通知"msg["From"] = formataddr(("IP監控系統", sender_email))msg["To"] = formataddr(("管理員", target_email)) with smtplib.SMTP_SSL("smtp.qq.com", 465) as server:server.login(sender_email, password)server.sendmail(sender_email, [target_email], msg.as_string())print("? 郵件發送成功(即使可能報錯,但郵件已送達)")except smtplib.SMTPResponseException as e:# 忽略SMTP關閉時的異常(如果郵件已發送成功)print(f"?? SMTP服務器返回異常,但郵件可能已發送: {e}")except Exception as e:print(f"? 郵件發送失敗: {e}")raise
def check_ip():"""檢查IP地址是否變化變化返回True,否則返回False"""driver = selenium_login()global WANIPif driver:newWANIP = get_ip_from_html(driver)if newWANIP == WANIP:print("? WAN IP 沒有變化",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))driver.quit()return False;else:print("? WAN IP 發生變化:", newWANIP,"當前查詢時間為:" ,time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))WANIP = newWANIPdriver.quit()return True;else:print("? 無法獲取新的WAN IP, 請檢查登錄是否成功")driver.quit()return False; if __name__ == "__main__":print("Start")while True:if(check_ip()):print("ready to send email")send_notification_by_email(WANIP, SENDER_EMAIL, QQ_EMAIL_PASSWORD, TARGET_EMAIL)time.sleep(CHECK_GAP) # 等待指定的檢查間隔時間
常用命令
sudo journalctl -u 服務名.service -n 50 # 查看最近 50 行