引子
我想大家應該都很熟悉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的使用示例
6、約定:所有的api請求都返回json格式,所以要使用python的json模塊進行解析
環境版本
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有點大材小用了
1
2
3
4
5
6
7
8
|
#獲得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" ] |
獲取到檢測ip地址的url后,我們可以觀察一下網頁結構
發現,我們只需要獲取到center標簽的內容,然后用正則提取出ip即可
1
2
3
4
5
6
7
8
9
10
|
#獲取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。
可以觀察一下它的請求參數
在阿里的請求中,有一個公共參數(上面沒有提及),里面有一個簽名,這個簽名雖然官方提供了簽名生成的算法,不過如果自己實現很容易出錯,所以我們使用它的python sdk。在簽名中,有一個至關重要的是accesskey,accesskey的生成可以在管理控制臺的accesskeys模塊獲取
生成之后一定要保管好這個密鑰哦!!!!!
由于云解析官方并沒有提供對應的sdk模塊,只提供了api,不過我們可以利用sdk中的commonrequest對象來進行api操作。不知道各位有木有發現在更新域名解析記錄的請求參數中有一個recordid,這個recordid要利用describedomainrecords這個action來獲取。
如果每次請求都要使用commonrequest對象,這樣難免會造成一定的內存浪費,所以使用面向對象設計模式中的單例模式進行優化。
1
2
3
4
5
6
7
8
9
10
11
|
class commonrequestsing: #私有類變量 __request = none #該修飾符將實例方法變成類方法 #,因為類方法無法操作私有的類變量,所以使用實例方法進行操作,再進行轉換為類方法 @classmethod def getinstance( self ): if self .__request is none: self .__request = commonrequest() return self .__request |
同時,在構造請求式,也會用到acsclient對象,也可使用單例模式優化
1
2
3
4
5
6
7
|
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 |
這里用到了函數修飾符@classmethod,主要功能是將實例方法轉換為類方法。
這兩個單實例都可封裝進工具類中,直接調用工具類獲取實例就可以了,代碼會更美觀一些。
獲取recordid
利用describedomainrecords 這個action來獲得。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#獲取二級域名的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邏輯核心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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()) |
至此結束~然后設置好路由器端口映射,這時候你就可以使用ddns.example.com:xxx來進行訪問設置在家庭網絡中的資源了~
然后可以將這個python代碼設置為定時任務,比如每天執行一次,或者根據運營商的ip變化策略調整~
源碼(最新):https://github.com/mgsky1/ddns
源碼(結構與文章一樣的):點擊這里
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/mgsky1/article/details/80466840