一个查找可用IP并自动生成 hosts 的脚本

标签: python ;


某些时候下载东西会撞墙,大抵是因为该域名被ban了。在最简单的情况下,只要找到该域名还可以访问的IP,写进hosts文件中就可以访问了。不过查找可用IP的过程比较繁琐, 比较适合让脚本来干。

下面的脚本就是干这个事的。基本用法:

# 不带任何参数运行,直接查找 raw.githubusercontent.com
python findip.py

# 或者把目标域名作为参数传递给它
python findip.py xxx.xxx.xxx

程序会尝试读取系统hosts文件的内容,替换或者添加指定域名的条目,再把内容打印到标准输出。可以用管道生成新的 hosts 文件,再替换回去。

python findip.py > hosts

程序运行依赖系统中安装有可运行的nslookup命令,Windows 默认是安装了的,Linux 就不一定了,需要自行安装。

#!/usr/bin/env python3

import subprocess
import re
import platform
from pathlib import Path
import sys

arch = platform.system().lower()
coding = 'gb2312' if arch == 'windows' else 'utf-8'
pattern = '\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\s*?'

if platform.system().lower() == 'linux': pattern = 'Address:' + pattern

# 通过 DNS 查询主机的 IP 地址
def nslookup(domain):
    ns_out = subprocess.Popen(['nslookup', domain, '8.8.8.8'],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE).stdout.readlines()
    iplist = []
    for line in ns_out:
        m = re.match(pattern, line.decode(coding))
        if m: 
            iplist.append(m.group(1))
    return iplist

# 返回 ping 的时延(ms)
def ping(host):
    param = '-n' if arch == 'windows' else '-c'
    cmd = ['ping', param, '1', host]
    response = subprocess.run(cmd, stdout=subprocess.PIPE)
    if response.returncode == 0:
        out = response.stdout.decode(coding).splitlines()
        for line in out:
            m = re.match('.*=(\\d+\\.?\\d*) *ms', line)
            if m: return(float(m.group(1)))
    return None

# 查找可用 IP,并返回时延最短的主机IP
def lookup_ip(domain):
    ips = nslookup(domain)
    if len(ips) > 0:
        timeout = None
        ip = None
        for i in ips:
            t = ping(i)
            if t:
                if not timeout or t < timeout:
                    ip = i
                    timeout = t
        if ip:
            return(ip, timeout)
        print('No available IP address found')
        return None
    print('No valid IP address for', domain, 'found')
    return None

def update_hosts(ip, domain):
    hosts = Path('C:/Windows/System32/drivers/etc/hosts') if arch == 'windows' else Path('/etc/hosts')
    found_host = False
    with open(hosts, 'r') as f:
        for line in f:
            if line.find(domain) >= 0:
                print(f'{ip}\t{domain}')
                found_host = True
            else:
                print(line, end="")
        if not found_host:
            print(f'\n{ip}\t{domain}')

if __name__ == '__main__':
    domain = 'raw.githubusercontent.com'
    if len(sys.argv) > 1:
        domain = sys.argv[1]
    
    ip, timeout = lookup_ip(domain)
    if ip:
        #print(f'{ip}\t{domain}')
        update_hosts(ip,domain)