Dnspod ドメイン名の設定 (Python スクリプトで DDNS を実装)

ホーム ブロードバンドがパブリック ネットワーク IP を申請した後、IP アドレスは動的になります。便宜上、固定ドメイン名を使用して独自のサーバーにアクセスできるように、ドメイン名を申請して DDNS を設定する必要があります。覚えにくく、頻繁に変更される IP アドレスを使用する必要はありません。

1. ドメイン名の申請

無料のサードレベルドメイン名を申請できる場所はいくつかありますが、便宜上、 Tencent Cloudでdigital.xyzドメイン名を購入しました。これは年間8元で、これも安いです。

無料のドメイン名解決機能を利用して設定する

  • サブドメインレベル5レベル

  • 2 つのロードバランサー

  • URL転送2

  • サブドメインの数は無制限

個人的にはドメイン名に対する需要はあまりなく、現状ではクラス3Aのドメイン名しか設定されておらず、解像度は無料で十分で、ddns機能は自分でスクリプトを書いて実現する必要があります。

2. スクリプト作成

これは Python で書かれており、dnspod API を通じて個人ドメイン名の DNS 解決レコードを取得し、それを外部ネットワーク アドレスと比較し、異なる場合は DNS レコードを更新し、5 ~ 10 分ごとにスクリプトを実行します。

2.1 外部ネットワークアドレスを取得する

https://myip.ipip.netにアクセスして、自分の IP アドレスを取得します。

2.2 ドメイン名解決情報の取得

dnspod API を使用します。手順については、Tencent Cloud のドキュメントを参照してください: https://docs.dnspod.cn/api/call-requency/

2.2.1 トークンの取得

https://console.dnspod.cn/account/token/tokenにアクセスして秘密キーを作成します。

完了後、プログラム内でIDとTOKENを使用してAPIにアクセスできるようになります。

2.2.2 機能

ドメイン名の取得、ドメイン名の追加、ドメイン名の削除、レコードの取得、レコードの追加、レコードの削除、レコードの変更の機能をリクエストのpostメソッドで実現しました。

  1. レコード取得関数にはパラメータとしてドメイン名が必要です

  1. レコードを削除するには、パラメーターとしてレコード ID とドメイン名が必要です。レコード ID は、レコード取得関数を通じて取得されます。

  1. レコードの変更に必要なパラメータは、ドメイン名、サブドメイン名、レコード ID、変更する値、レコード タイプ、および回線タイプです。

  1. レコードを追加するときに同一のサブドメイン名を 2 つしか追加できない場合、その理由は負荷分散の制限です。

具体的なコードは次のとおりです。

"""
访问和修改dnspod域名的类
get_domain无需参数,输出login_token对应的用户拥有的域名。
域名下包含记录,使用getrecord(domain_list)获取,每条记录对应一个子域名,也是最常用的。
对仅拥有一个域名一个公网ip的用户来说,日常使用仅需要在ip地址发生变化时,更新每条记录就可以了。
"""
import requests
import logging
import re
from dnspod_setting import Settings
from time import sleep


class Dnspod:
    """
    操作dnspod域名的类
    """

    def __init__(self, login_token):
        self._login_token = login_token
        self._format = 'json'

        # 设置 logger
        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        self._logger = logging.getLogger(__name__)
        handler = logging.FileHandler(Settings.log_file, encoding='utf-8')
        handler.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        self._logger.addHandler(handler)

        self._add_domain_URL = 'https://dnsapi.cn/Domain.Create'  # 添加域名
        self._del_domain_URL = 'https://dnsapi.cn/Domain.Remove'  # 删除域名
        self._add_record_URL = 'https://dnsapi.cn/Record.Create'  # 添加记录
        self._alter_record_URL = 'https://dnsapi.cn/Record.Modify'  # 修改记录
        self._get_record_URL = 'https://dnsapi.cn/Record.List'  # 获取记录列表
        self._del_record_URL = 'https://dnsapi.cn/Record.Remove'  # 删除记录
        self._get_domain_list_URL = 'https://dnsapi.cn/Domain.List'  # 获取域名列表

    @staticmethod
    def get_outer_ip():
        """
        获取外网ip
        :return:
        """
        ip = ''
        for count in range(10):     # 重试10次
            try:
                res = requests.get('https://myip.ipip.net', timeout=5).text
                ip = re.findall(r'(\d+\.\d+\.\d+\.\d+)', res)
                ip = ip[0] if ip else ''
            except:
                pass
            if ip:          # 一旦ip不为空,直接返回
                return ip
            sleep(1)
        return ip

    def get_domain_list(self):
        """
        获取域名列表
        :return: 域名列表: ['xxx.xx', 'xxx.xx'] or []
        """
        try:
            r = requests.post(self._get_domain_list_URL, data={'login_token': self._login_token,
                                                               'format': self._format})
            response_record_json = r.json()
            if response_record_json['status']['code'] == '1':
                domains_list = [doms['name'] for doms in response_record_json['domains'] if doms['status'] == 'enable']
                for domain in domains_list:
                    self._logger.info(f"域名:{domain}")
                return domains_list
            else:
                return []
        except Exception as e:
            self._logger.error(e)

    def add_domain(self, domains_list):
        """
        添加域名
        :param domains_list:['xxx.xxx.xxx', 'xx2.xxx.xxx']
        :return: True or False
        """
        try:
            for domain in domains_list:
                r = requests.post(self._add_domain_URL, data={'login_token': self._login_token,
                                                              'format': self._format,
                                                              'domain': domain,
                                                              })
                response_record_json = r.json()
                if response_record_json["status"]["code"] == "1":
                    self._logger.info(
                        f'域名:{response_record_json["domain"]["domain"]},添加成功;域名ID:{response_record_json["domain"]["id"]}')
                else:
                    self._logger.error(f'域名:{domain},添加失败,错误信息:{response_record_json["status"]["message"]}')
        except Exception as e:
            self._logger.error(e)

    def del_domain(self, domains_list):
        """
        删除域名
        :param domains_list:['xxx.xxx.xxx', 'xx2.xxx.xxx']
        :return: True or False
        """
        try:
            for domain in domains_list:
                r = requests.post(self._del_domain_URL, data={'login_token': self._login_token,
                                                              'format': self._format,
                                                              'domain': domain,
                                                              })
                response_record_json = r.json()
                if response_record_json["status"]["code"] == "1":
                    self._logger.info(f'删除域名:{domain}成功.')
                else:
                    self._logger.error(f'删除域名:{domain},添加失败,错误信息:{response_record_json["status"]["message"]}')
        except Exception as e:
            self._logger.error(e)

    def add_record(self, domain, sub_domain_list, value, record_type='A', record_line_value='默认'):
        """
        添加域名和解析记录
        :param domain: 要添加记录的域名
        :param sub_domain_list: 要添加的子域名列表
        :param record_type: 添加记录类型:默认 A
        :param record_line_value: 线路类型, 使用 ‘默认’即可
        :param value: 要添加的值, A类型是ip地址。
        :return:
        """
        try:
            for sub_domain in sub_domain_list:
                r = requests.post(self._add_record_URL, data={'login_token': self._login_token,
                                                              'format': self._format,
                                                              'domain': domain,
                                                              'sub_domain': sub_domain,
                                                              'record_type': record_type,
                                                              'record_line': record_line_value,
                                                              'value': value,
                                                              })
                response_record_json = r.json()
                if response_record_json["status"]["code"] == "1":
                    self._logger.info(f'域名:{domain},成功添加记录:{response_record_json["record"]["name"]} ')
                else:
                    self._logger.error(
                        f'域名:{domain},添加:{sub_domain},记录失败,错误信息:{response_record_json["status"]["message"]}')
        except Exception as e:
            self._logger.error(e)

    def get_record(self, domain):
        """
        获取域名下的记录
        :param domain: "xxx.xxx.xx"
        :return:
        """
        records_list = []
        try:
            r = requests.post(self._get_record_URL, data={'login_token': self._login_token,
                                                          'format': self._format,
                                                          'domain': domain,
                                                          })

            response_record_json = r.json()
            if response_record_json['status']['code'] == '1':
                self._logger.info(
                    f"域名:{response_record_json['domain']['name']},"
                    f"共有:{response_record_json['info']['sub_domains']}个子域名"
                    f"和:{response_record_json['info']['record_total']}条解析记录.")
                for record in response_record_json['records']:
                    if record['type'] == 'A':  # 只修改A类型的记录,避免修改dnspod默认记录。
                        records_list.append({
                            'domain': domain,
                            'id': record['id'],
                            'name': record['name'],
                            'line': record['line'],
                            'type': record['type'],
                            'value': record['value']
                        })
        except Exception as e:
            self._logger.error(e)
            return []

        return records_list

    def alter_record(self, domain, sub_domain, record_id, value, record_type='A', record_line='默认'):
        """
        修改解析记录
        :param domain: 域名,不可缺
        :param sub_domain: 子域名,可选,默认值为@
        :param record_id: 记录id
        :param value: 值
        :param record_type: 记录类型,默认 A
        :param record_line: 线路类型,使用‘默认’即可
        :return:
        """
        try:
            r = requests.post(self._alter_record_URL, data={'login_token': self._login_token,
                                                            'format': self._format,
                                                            'domain': domain,
                                                            'sub_domain': sub_domain,
                                                            'record_id': record_id,
                                                            'record_type': record_type,
                                                            'record_line': record_line,
                                                            'value': value,
                                                            })
            response_record_json = r.json()
            if response_record_json['status']['code'] == '1':
                self._logger.info(f"修改记录{record_id}成功。")
            else:
                self._logger.error(f"记录ID:{record_id}修改失败,错误信息: {response_record_json['status']['message']}。")
        except Exception as e:
            self._logger.error(e)

    def del_record(self, domain, record_id):
        """
        删除解析记录
        :param domain: 域名,
        :param record_id: 记录id,通过get_record获取
        :return:
        """
        try:
            r = requests.post(self._del_record_URL, data={'login_token': self._login_token,
                                                          'format': self._format,
                                                          'domain': domain,
                                                          'record_id': record_id,
                                                          })
            response_record_json = r.json()
            if response_record_json['status']['code'] == '1':
                self._logger.info(
                    f"您正在解析记录,ID为:{record_id},"
                    f"删除状态值为:{response_record_json['status']['code']}, "
                    f"信息为:{response_record_json['status']['message']}")
            else:
                self._logger.error(
                    f"您正在解析记录,ID为:{record_id},"
                    f"删除状态值为:{response_record_json['status']['code']}, "
                    f"信息为:{response_record_json['status']['message']}")
        except Exception as e:
            self._logger.error(e)


def update_ip():
    """
    更新公网ip地址,定期运行保证域名正确,
    因为只有一个域名,只有一个ip,所以每次运行,获取域名的A类型解析记录,将所有记录ip修改为外网ip
    :return:
    """
    pod = Dnspod(Settings.login_token)
    ip_public = pod.get_outer_ip()
    if ip_public:
        records = pod.get_record(Settings.domain)
        for record in records:
            if ip_public == record['value']:
                print('地址未改变,无需更改')
            else:
                pod.alter_record(record['domain'], record['name'], record['id'], ip_public)


if __name__ == '__main__':
    update_ip()

おすすめ

転載: blog.csdn.net/reverie_2007/article/details/128668503