利用Python+阿裏雲實現DDNS(動態域名解析)
引子
我想大家應該都很熟悉DNS了,這回在DNS前面加了一個D又變成了什麽呢?這個D就是Dynamic(動態),也就是說,按照傳統,一個域名所對應的IP地址應該是定死的,而使用了DDNS後,域名所對應的IP是可以動態變化的。那這個有什麽用呢?
比如,在家裏的路由器上連著一個raspberry pi(樹莓派),上面跑著幾個網站,我應該如和在外網環境下訪問網站、登陸樹莓派的SSH呢?
還有,家裏的NAS(全稱Network Attach Storage 網絡附屬存儲,可以理解為私有的百度網盤)上存儲著大量的視頻、照片,如何在外網環境下和朋友分享呢?
這時,就要靠DDNS了!它會動態偵運營商分配給你的IP變化,並映射到域名上,這時就可以用域名來訪問家庭環境中的內容了~
實現效果(因為我已經更新過了,所以它提示IP地址已存在,阿裏雲是不允許同一個IP重復更新的)
本地:
使用DDNS後,在外網環境下:
註:
這篇帖子適用於家庭寬帶的IP是公網IP的小夥伴,但是註意,這種公網IP是臨時的,會不定時進更改。判斷方法很簡單:先去百度搜索IP,查到自己的IP地址;接著本地開一個網站,比如在Windows下直接啟動IIS,Linux下安裝一個Apache或者Nginx啟動,使用它們的默認頁面;然後在路由器上設置好轉發規則,公網IP的網絡訪問端口最好不要用80,80端口可能被運營商封了;最後利用前面查到的公網IP+端口號訪問一下,看看能不能顯示內網上的頁面,如果可以,恭喜你!
實現思路
前面引文已經說的很清楚了,就是探測家庭寬帶公網IP的變化,然後利用我們的程序將這個IP更新到它所綁定的二級域名上~
綜上,我的思路是這樣的:
1、利用Python去網上爬取自己真實的IP地址
2、利用阿裏雲所提供的接口更新IP
前期準備
1、一個域名(國內需要備案,港澳臺和國外聽說是不要的,我也沒嘗試過)
2、將域名的解析設置到阿裏雲的雲解析上
3、為我們的DDNS創建一個二級域名(例如 ddns.expamle.com)
4、安裝阿裏雲Python SDK(具體教程可以去阿裏雲上找
5、建議先去閱讀一下Python SDK的使用示例
環境版本
1、Python 3.6
2、網頁解析利用BeautifulSoup 4
3、阿裏的雲解析API和Python SDK直接使用官方最新版本即可
實現步驟
項目結構
註:
AcsClientSingleton.py => 阿裏雲AcsClient單實例類
CommonRequestSingleton.py => 阿裏雲CommonRequest的單實例類,獲取阿裏雲Common Request請求類
DDNS.py => 主程序
IpGetter.py =>獲取家庭寬帶實際的公網IP
Utils.py => 工具類
爬IP
首當其沖的就是要獲得我們實際的IP地址,推薦ip138.com
你看到的頁面是這樣的:
畫紅框的部分是一個iframe
其中的URL是一直會變化的,所以第一步是要獲取這個URL,我這裏用到的解析框架是BeautifulSoup,感覺用Scrapy有點大材小用了
#獲得IP檢測的網頁URL
def getIpPage():
url = "http://www.ip138.com/"
response = urllib.request.urlopen(url)
html = response.read().decode("gb2312")
soup = BeautifulSoup(html, "lxml")
_iframe = soup.body.iframe
return _iframe["src"]
1
2
3
4
5
6
7
8
獲取到檢測IP地址的URL後,我們可以觀察一下網頁結構
發現,我們只需要獲取到center標簽的內容,然後用正則提取出IP即可
#獲取IP地址
def getRealIp(url):
response = urllib.request.urlopen(url)
html = response.read().decode("gb2312")
pattern = r"(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)"
matchs = re.search(pattern,html)
ip_addr = ""
for i in range(1,5):
ip_addr += matchs.group(i) + "."
return ip_addr[:-1]
1
2
3
4
5
6
7
8
9
10
然後我們爬的工作就完成了,可以將這個獲取IP的過程進行封裝,放進工具類裏
查文檔
阿裏雲雲解析API文檔
我們需要用到的是UpdateDomainRecord這個Action。
可以觀察一下它的請求參數
在阿裏的請求中,有一個公共參數(上面沒有提及),裏面有一個簽名,這個簽名雖然官方提供了簽名生成的算法,不過如果自己實現很容易出錯,所以我們使用它的Python SDK。在簽名中,有一個至關重要的是AccessKey,AccessKey的生成可以在管理控制臺的AccessKeys模塊獲取
生成之後一定要保管好這個密鑰哦!!!!!
由於雲解析官方並沒有提供對應的SDK模塊,只提供了API,不過我們可以利用SDK中的CommonRequest對象來進行API操作。不知道各位有木有發現在更新域名解析記錄的請求參數中有一個RecordId,這個RecordId要利用DescribeDomainRecords這個Action來獲取。
如果每次請求都要使用CommonRequest對象,這樣難免會造成一定的內存浪費,所以使用面向對象設計模式中的單例模式進行優化。
class CommonRequestSing:
#私有類變量
__request = None
#該修飾符將實例方法變成類方法
#,因為類方法無法操作私有的類變量,所以使用實例方法進行操作,再進行轉換為類方法
@classmethod
def getInstance(self):
if self.__request is None:
self.__request = CommonRequest()
return self.__request
1
2
3
4
5
6
7
8
9
10
11
同時,在構造請求式,也會用到AcsClient對象,也可使用單例模式優化
class AcsClientSing:
__client = None
@classmethod
def getInstance(self):
if self.__client is None:
self.__client = AcsClient(‘Your_AccessKeyId‘, ‘Your_AccessKeySecret‘, ‘cn-hangzhou‘)
return self.__client
1
2
3
4
5
6
7
這裏用到了函數修飾符@classmethod,主要功能是將實例方法轉換為類方法。
這兩個單實例都可封裝進工具類中,直接調用工具類獲取實例就可以了,代碼會更美觀一些。
獲取RecordID
利用DescribeDomainRecords 這個Action來獲得。
#獲取二級域名的RecordId
def getRecordId(domain):
client = Utils.getAcsClient()
request = Utils.getCommonRequest()
request.set_domain(‘alidns.aliyuncs.com‘)
request.set_version(‘2015-01-09‘)
request.set_action_name(‘DescribeDomainRecords‘)
request.add_query_param(‘DomainName‘, ‘Your_DomainName eg.example.com‘)
response = client.do_action_with_exception(request)
jsonObj = json.loads(response.decode("UTF-8"))
records = jsonObj["DomainRecords"]["Record"]
for each in records:
if each["RR"] == domain:
return each["RecordId"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
更新解析記錄IP,DDNS邏輯核心
def DDNS():
client = Utils.getAcsClient()
recordId = Utils.getRecordId(‘ddns‘)
ip = Utils.getRealIP()
request = Utils.getCommonRequest()
request.set_domain(‘alidns.aliyuncs.com‘)
request.set_version(‘2015-01-09‘)
request.set_action_name(‘UpdateDomainRecord‘)
request.add_query_param(‘RecordId‘, recordId)
request.add_query_param(‘RR‘, ‘ddns‘)
request.add_query_param(‘Type‘, ‘A‘)
request.add_query_param(‘Value‘, ip)
response = client.do_action_with_exception(request)
return response
if __name__ == "__main__":
try:
result = DDNS()
print("成功!")
except (ServerException,ClientException) as reason:
print("失敗!原因為")
print(reason.get_error_msg())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
至此結束~然後設置好路由器端口映射,這時候你就可以使用ddns.example.com:XXX來進行訪問設置在家庭網絡中的資源了~
然後可以將這個Python代碼設置為定時任務,比如每天執行一次,或者根據運營商的IP變化策略調整~
源碼(最新):https://github.com/mgsky1/DDNS
源碼(結構與文章一樣的):點擊這裏
---------------------
作者:mgsky1
來源:CSDN
原文:https://blog.csdn.net/mgsky1/article/details/80466840
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
利用Python+阿裏雲實現DDNS(動態域名解析)