引子
我想你們應該都很熟悉DNS了,這回在DNS前面加了一個D又變成了什麼呢?這個D就是Dynamic(動態),也就是說,按照傳統,一個域名所對應的IP地址應該是定死的,而使用了DDNS後,域名所對應的IP是能夠動態變化的。那這個有什麼用呢?
好比,在家裏的路由器上連着一個raspberry pi(樹莓派),上面跑着幾個網站,我應該如和在外網環境下訪問網站、登錄樹莓派的SSH呢?
還有,家裏的NAS(全稱Network Attach Storage 網絡附屬存儲,能夠理解爲私有的百度網盤)上存儲着大量的視頻、照片,如何在外網環境下和朋友分享呢?
這時,就要靠DDNS了!它會動態偵運營商分配給你的IP變化,並映射到域名上,這時就能夠用域名來訪問家庭環境中的內容了~
哈!有了域名,走遍天下都不怕有木有
實現效果(由於我已經更新過了,因此它提示IP地址已存在,阿里雲是不容許同一個IP重複更新的)
本地:
使用DDNS後,在外網環境下:
注:html
這篇帖子適用於家庭寬帶的IP是公網IP的小夥伴,可是注意,這種公網IP是臨時的,會不定時進更改。判斷方法很簡單:先去百度搜索IP,查到本身的IP地址;接着本地開一個網站,好比在Windows下直接啓動IIS,Linux下安裝一個Apache或者Nginx啓動,使用它們的默認頁面;而後在路由器上設置好轉發規則,公網IP的網絡訪問端口最好不要用80,80端口可能被運營商封了;最後利用前面查到的公網IP+端口號訪問一下,看看能不能顯示內網上的頁面,若是能夠,恭喜你!
本文涉及到的技術點會比較多,好比爬蟲啊,設計模式啊,函數修飾符啊等等,能夠算是一個綜合運用了吧~
實現思路
前面引文已經說的很清楚了,就是探測家庭寬帶公網IP的變化,而後利用咱們的程序將這個IP更新到它所綁定的二級域名上~
綜上,個人思路是這樣的:
一、利用Python去網上爬取本身真實的IP地址
二、利用阿里雲所提供的接口更新IPgit
前期準備
一、一個域名(國內須要備案,港澳臺和國外據說是不要的,我也沒嘗試過)
二、將域名的解析設置到阿里雲的雲解析上
三、爲咱們的DDNS建立一個二級域名(例如 ddns.expamle.com)
四、安裝阿里雲Python SDK(具體教程能夠去阿里雲上找
五、建議先去閱讀一下Python SDK的使用示例
六、約定:全部的API請求都返回JSON格式,因此要使用Python的JSON模塊進行解析github
環境版本
一、Python 3.6
二、網頁解析利用BeautifulSoup 4
三、阿里的雲解析API和Python SDK直接使用官方最新版本便可算法
實現步驟
項目結構
注:json
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的過程進行封裝,放進工具類裏dom
查文檔
阿里云云解析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())12345678910111213141516171819202122至此結束~而後設置好路由器端口映射,這時候你就可使用ddns.example.com:XXX來進行訪問設置在家庭網絡中的資源了~ 而後能夠將這個Python代碼設置爲定時任務,好比天天執行一次,或者根據運營商的IP變化策略調整~ 源碼(最新):https://github.com/mgsky1/DDNS 源碼(結構與文章同樣的):點擊這裏--------------------- 做者:mgsky1 來源:CSDN 原文:https://blog.csdn.net/mgsky1/article/details/80466840 版權聲明:本文爲博主原創文章,轉載請附上博文連接!