利用Python+阿里云實現(xiàn)DDNS動態(tài)域名解析的方法
引子
我想大家應該都很熟悉DNS了,這回在DNS前面加了一個D又變成了什么呢?這個D就是Dynamic(動態(tài)),也就是說,按照傳統(tǒng),一個域名所對應的IP地址應該是定死的,而使用了DDNS后,域名所對應的IP是可以動態(tài)變化的。那這個有什么用呢?
比如,在家里的路由器上連著一個raspberry pi(樹莓派),上面跑著幾個網(wǎng)站,我應該如和在外網(wǎng)環(huán)境下訪問網(wǎng)站、登陸樹莓派的SSH呢?
還有,家里的NAS(全稱Network Attach Storage 網(wǎng)絡附屬存儲,可以理解為私有的百度網(wǎng)盤)上存儲著大量的視頻、照片,如何在外網(wǎng)環(huán)境下和朋友分享呢?
這時,就要靠DDNS了!它會動態(tài)偵運營商分配給你的IP變化,并映射到域名上,這時就可以用域名來訪問家庭環(huán)境中的內(nèi)容了~
哈!有了域名,走遍天下都不怕有木有
實現(xiàn)效果(因為我已經(jīng)更新過了,所以它提示IP地址已存在,阿里云是不允許同一個IP重復更新的)
本地:
使用DDNS后,在外網(wǎng)環(huán)境下:
注:
這篇帖子適用于家庭寬帶的IP是公網(wǎng)IP的小伙伴,但是注意,這種公網(wǎng)IP是臨時的,會不定時進更改。判斷方法很簡單:先去百度搜索IP,查到自己的IP地址;接著本地開一個網(wǎng)站,比如在Windows下直接啟動IIS,Linux下安裝一個Apache或者Nginx啟動,使用它們的默認頁面;然后在路由器上設置好轉(zhuǎn)發(fā)規(guī)則,公網(wǎng)IP的網(wǎng)絡訪問端口最好不要用80,80端口可能被運營商封了;最后利用前面查到的公網(wǎng)IP+端口號訪問一下,看看能不能顯示內(nèi)網(wǎng)上的頁面,如果可以,恭喜你!
本文涉及到的技術點會比較多,比如爬蟲啊,設計模式啊,函數(shù)修飾符啊等等,可以算是一個綜合運用了吧~
實現(xiàn)思路
前面引文已經(jīng)說的很清楚了,就是探測家庭寬帶公網(wǎng)IP的變化,然后利用我們的程序?qū)⑦@個IP更新到它所綁定的二級域名上~
綜上,我的思路是這樣的:
1、利用Python去網(wǎng)上爬取自己真實的IP地址
2、利用阿里云所提供的接口更新IP
前期準備
1、一個域名(國內(nèi)需要備案,港澳臺和國外聽說是不要的,我也沒嘗試過)
2、將域名的解析設置到阿里云的云解析上
3、為我們的DDNS創(chuàng)建一個二級域名(例如 ddns.expamle.com)
4、安裝阿里云Python SDK(具體教程可以去阿里云上找
5、建議先去閱讀一下Python SDK的使用示例
6、約定:所有的API請求都返回JSON格式,所以要使用Python的JSON模塊進行解析
環(huán)境版本
1、Python 3.6
2、網(wǎng)頁解析利用BeautifulSoup 4
3、阿里的云解析API和Python SDK直接使用官方最新版本即可
實現(xiàn)步驟
項目結(jié)構(gòu)
注:
AcsClientSingleton.py => 阿里云AcsClient單實例類
CommonRequestSingleton.py => 阿里云CommonRequest的單實例類,獲取阿里云Common Request請求類
DDNS.py => 主程序
IpGetter.py =>獲取家庭寬帶實際的公網(wǎng)IP
Utils.py => 工具類
爬IP
首當其沖的就是要獲得我們實際的IP地址,推薦ip138.com
你看到的頁面是這樣的:
畫紅框的部分是一個iframe
其中的URL是一直會變化的,所以第一步是要獲取這個URL,我這里用到的解析框架是BeautifulSoup,感覺用Scrapy有點大材小用了
#獲得IP檢測的網(wǎng)頁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"]
獲取到檢測IP地址的URL后,我們可以觀察一下網(wǎng)頁結(jié)構(gòu)
發(fā)現(xiàn),我們只需要獲取到center標簽的內(nèi)容,然后用正則提取出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]
然后我們爬的工作就完成了,可以將這個獲取IP的過程進行封裝,放進工具類里
查文檔
阿里云云解析API文檔
我們需要用到的是UpdateDomainRecord這個Action。
可以觀察一下它的請求參數(shù)
在阿里的請求中,有一個公共參數(shù)(上面沒有提及),里面有一個簽名,這個簽名雖然官方提供了簽名生成的算法,不過如果自己實現(xiàn)很容易出錯,所以我們使用它的Python SDK。在簽名中,有一個至關重要的是AccessKey,AccessKey的生成可以在管理控制臺的AccessKeys模塊獲取
生成之后一定要保管好這個密鑰哦?。。。?!
由于云解析官方并沒有提供對應的SDK模塊,只提供了API,不過我們可以利用SDK中的CommonRequest對象來進行API操作。不知道各位有木有發(fā)現(xiàn)在更新域名解析記錄的請求參數(shù)中有一個RecordId,這個RecordId要利用DescribeDomainRecords這個Action來獲取。
如果每次請求都要使用CommonRequest對象,這樣難免會造成一定的內(nèi)存浪費,所以使用面向?qū)ο笤O計模式中的單例模式進行優(yōu)化。
class CommonRequestSing: #私有類變量 __request = None #該修飾符將實例方法變成類方法 #,因為類方法無法操作私有的類變量,所以使用實例方法進行操作,再進行轉(zhuǎn)換為類方法 @classmethod def getInstance(self): if self.__request is None: self.__request = CommonRequest() return self.__request
同時,在構(gòu)造請求式,也會用到AcsClient對象,也可使用單例模式優(yōu)化
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
這里用到了函數(shù)修飾符@classmethod,主要功能是將實例方法轉(zhuǎn)換為類方法。
這兩個單實例都可封裝進工具類中,直接調(diào)用工具類獲取實例就可以了,代碼會更美觀一些。
獲取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"]
更新解析記錄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())
至此結(jié)束~然后設置好路由器端口映射,這時候你就可以使用ddns.example.com:XXX來進行訪問設置在家庭網(wǎng)絡中的資源了~
然后可以將這個Python代碼設置為定時任務,比如每天執(zhí)行一次,或者根據(jù)運營商的IP變化策略調(diào)整~
源碼(最新):https://github.com/mgsky1/DDNS
源碼(結(jié)構(gòu)與文章一樣的):點擊這里
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Django Channels 實現(xiàn)點對點實時聊天和消息推送功能
這篇文章主要介紹了Django Channels 實現(xiàn)點對點實時聊天和消息推送功能,本文分步驟給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-07-07python實現(xiàn)決策樹C4.5算法詳解(在ID3基礎上改進)
下面小編就為大家?guī)硪黄猵ython實現(xiàn)決策樹C4.5算法詳解(在ID3基礎上改進)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Python數(shù)據(jù)擬合實現(xiàn)最小二乘法示例解析
這篇文章主要為大家介紹了Python數(shù)據(jù)擬合實現(xiàn)最小二乘法的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2021-10-10