写个python脚本爬域名可用性

最近想申请个域名玩玩儿, 有不少网站提供域名检索服务, 比较有名的有godaddy, namecheap, name等. 但是想到一个搜一下太麻烦了. 有的网页做得也不是很友好, 没法指定检索自己感兴趣的顶级域名(TLD, Top Level Domain), 如.com, .io.

发现某网站的API是直接暴露在外的, 可以直接GET到. 那索性写个脚本把自己感兴趣的都爬一下得了.

为了保护该网站, 该API地址以API_URL指代.

该API的设计是:

// Request
GET: API_URL?domain=a.com,b.io
// Response
{
  status: [
            { name: "a.com", available: false },
            { name: "b.io", available: true }
           ]
}

请求的domain部分是逗号分隔的域名. 返回结果我略去了其他细节, 只保留核心部分.

代码

代码见的Github.

这东西其实不复杂, 不值得作为一个Repo, 更适合作为Gist. 但是Gist的版本控制感觉比较弱, 适合比较小的代码片段. 我以后也许会在现在的代码上做修改, 就还是建了个Repo.

我的python写得很少, 可能比较蹩脚. 欢迎吐槽.

使用

命令行里面输入python domain-scanner.py即可.

接受三个参数, start, endingsinterval.

start代表你想从哪个域名开始查找, 如想从abc.com开始查找, 就--start=abc. 默认值为a.

endings代表你感兴趣的TLD. 如你对comio感兴趣, 就--endings=com,io. 默认值为com

interval代表间隔多久发一个请求. 如你想2秒发一次, 就--interval=2, 默认值为1.

示例: python domain-scanner.py --start=abc --endings=com,io --interval=2.

另外你还可以python domain-scanner.py --help查看帮助信息.

示例:

$ python domain-scanner.py  --start=abc --ending=com,io --interval=1
start=abc, endings=com,io, interval=1
from: abc, to: acp, available: []
from: acq, to: aed, available: []
from: aee, to: afr, available: ['aez.io', 'afq.io', 'aev.io']
from: afs, to: ahf, available: ['afz.io', 'agq.io', 'afv.io', 'ahe.io', 'agj.io']
from: ahg, to: ait, available: ['ahw.io', 'aij.io', 'ahz.io', 'ahj.io', 'ahy.io']
from: aiu, to: akh, available: ['ajy.io', 'ajh.io', 'ajd.io', 'ajq.io']
from: aki, to: alv, available: ['alh.io', 'alq.io', 'akw.io', 'akq.io', 'akz.io']
from: alw, to: anj, available: []
from: ank, to: aox, available: ['aor.io', 'aoq.io', 'anl.io']
...

注:

  1. 4个字母(包含)以下的.com域名都不是available
  2. 我发现有些不available不代表被别人注册了, 也可能是你需要花大价钱出offer.

知识点

GET + JSON

GET请求并把结果解析为JSON.

from urllib.request import urlopen
import json
data = json.load(urlopen(url))

字符串加一操作

本代码里我只考虑英文字母的情况, 从指定的start域名开始向后遍历, 相当于26进制的数字.

python里面字符和int之间的转换要通过chrord两个函数来做. 其中, chr的作用是, 将数字转换成字符, 如chr(97) == 'a'; ord则反过来.

每次字符串加一都要来回来去chrord我觉得有些麻烦. 所以写了两个函数.

def stringify(s):
    return ''.join([chr(ord("a") + ch) for ch in s])
def codify(s):
    return [ord(ch) - ord("a") for ch in s]

codify的作用是把字符串转化为数字数组(a对应0), 如codify("abc") === [0,1,2]; stringify则反过来. 这样我对数组直接进行加一操作简单一些.

def next(a):
    carry = 1
    index = -1
    while -index <= len(a) and carry:
        a[index] += 1
        carry = a[index] // 26
        a[index] %= 26
        index -= 1
    if carry:
        a.insert(0, 0)

这样, 用户输入字符串如"abc", 我就先codify[0,1,2], 然后内部都对数字数组进行next操作, 当给用户显示的时候再stringify即可.

Python3整除

我发现Python3里面1/2等于0.5. 默认是浮点数除法. 想整除咋办? 用a // b即可
参考: Python integer division yields float

Python的setInterval

JS写多了, 发现Python居然连setInterval都没有.

需要用threading.Timer进行一下封装.

def setInterval(func, sec):
    def funcWrapper():
        setInterval(func, sec)
        func()
    t = threading.Timer(sec, funcWrapper)
    t.start()
    return t

这只是最基础的封装, 不支持cancel. 更详细的见参考: Python Equivalent of setInterval()?

另外, 要往调用函数里面传参咋整? 没想到好办法, 这次简单用全局变量来做的.

Python读取命令行参数

直接让人修改代码里面的参数再用好像太麻烦了. 开点命令行参数让用户自己设置变量吧? 怎么做?

参考了这篇文章Command Line Arguments in Python, 不过只用到了前面的sys.argvgetopt. 刚刚发现后面还有Python3的简便argparse模块... 下次再说吧.

简单例子如下:

import sys, getopt
unixOptions = "sh"  
gnuOptions = ["start=", "help"]
try:
    arguments, values = getopt.getopt(sys.argv[1:], unixOptions, gnuOptions)
    for arg, val in arguments:
        if arg in ("-s", "--start"):
            start = val
        elif arg in ("-h", "--help"):
            help = True
except getopt.error as err:
    print("Failed to parse arguments.", str(err))
    sys.exit(2)

其中定义了两个命令行参数starthelp, 其中, start=表示start接收参数, 而help不接收.

Python print flush

OK, 现在脚本支持命令行参数了, 赶紧去试试, 结果又踩坑.

我在Sublime或者CMD里面运行脚本, print的内容都能够正常输出, 但是发现在Git Bash里面输出不出来. 查了一下, 这种情况下需要手动flush.

两种方案:

  1. 运行脚本的时候加上-u, 如python -u domain-scanner.py.
  2. print函数调用的时候指定flush=True. 如print("yo", flush=True).

我采用了第二种方案.

参考: How to flush output of Python print?

429

发送到一段时间后, 开始有一些请求返回HTTP Error 429. 查了一下这个代表Too Many Requests. 嗯, 发得太快了. 这也是为什么加入了interval这个参数.

目前对于失败的请求并没有失败重传, 只打印了个fail. 因为想想如果重传也一样会导致后面的请求数增多, 然后后面更加拥挤. 不如记下失败的那些, 然后之后整体重发.

你可能感兴趣的:(写个python脚本爬域名可用性)