18910140161

使用Python脚本进行域名解析

顺晟科技

2021-06-16 10:42:00

363

因为研究爬行动物,也了解了域名解析。为了提高爬虫的效率,有必要提高域名解析的效率。我以爬虫记录的域名作为待解析的域名来测试各种域名解析方法的效率。我尝试了以下四种方法:1。用单线程依次解析每个域名;2.用多线程解析每个域名;3.用线程池解析每个域名;4.用adns库解析每个域名。其中第四种方法更高效安全,推荐大家使用。完整代码见:https://github.com/sunada/dnsResolve

1.单线程依次解析域名

这个方法最直观。使用循环,然后使用socket.getaddrinfo('host ',None)进行解析。这种方法效率不高:解析100个域名需要392s,解析500个域名需要1695s。这是因为getaddrinfo方法解析单个域名需要时间,而使用单线程阻塞方法解析500个域名自然需要很长时间。

导入时间

导入套接字

def ReadHost(文件):

hosts=[]

.

返回主机

def SynResolve(fr):

主机=读取主机(fr)

IPs={}

对于主机:中的主机

try:

结果=socket.getaddrinfo(主机,无)

对于结果:中结果

打印主机,结果[4][0]

IPS[结果[4][0]]=主机

例外,e:

打印e

file.close()

if __name__=='__main__':

start=time.time()

打印'从:开始',开始

同步解析(“主机”)

打印“结束于:”,time.time()-start

2.多线程域名解析

这个方法很好理解。每次解析域名时,都会创建一个新的线程来解析域名。这种方法效率更高,但是当线程数量太大时,错误率更高。要解析500个域名,需要创建500个线程。实验结果表明,“线程”的提示。程序执行时出现错误:“无法启动新线程”。可能会受此影响,在运行脚本时,它会提示无法解析单个域名。当这些域名被socket.getaddrinfo单独解析时,可以成功解析。如果只需要解析100个线程,则不会提示thread.error提示,只能成功解析一个域名提示。使用这种方法,解析100个域名大约需要1分钟。因为我运行脚本的机器网速不稳定,也许这个方法用的时间比较少。

代码主要分为两部分:一部分是继承自线程的子类。线程,每个线程要做的工作是通过run()的成员函数实现的;另一部分是一个函数,负责把要完成的任务分配给新创建的线程。因为解析的域名和对应的IP需要保存和下载,所以需要每个线程一起修改IPhost的字典。代码中使用了互斥体,只有得到锁的线程才能写入IPhost,否则只能等待。这样可以保证IPhost中IP和域名信息的正确对应。否则,在线程的快速来回切换中,这种对应关系可能会被不准确地记录。

导入时间

导入套接字

def ReadHost(文件):

hosts=[]

.

返回主机

类ThreadClass(线程。螺纹):

def __init__(自身,主机):

self.host=主机

threading.Thread.__init__(self)

def run(self):

全球IPhost

try:

RES=socket . getaddrinfo(self . host,None)

if mutex.acquire(1):

re in res:

IPhost[re[4][0]]=self.host

mutex.release()

例外,e:

打印self.host,e

def MulThreadResolve(fr):

start=time.ctime()

打印“在:处启动MulThreadResolve”,开始

主机=读取主机(fr)

线程=[]

对于主机:中的主机

t=ThreadClass(主机)

threads.append(t)

cntHost=len(主机)

对于I在范围内(cntHost):

线程[i]。开始()

对于I在范围内(cntHost):

线程[i]。join()

打印“结束于:”,time.ctime()

if __name__='__main__':

iphone ost={ }

互斥体=线程。锁定()

MulThreadResolve('host1 ')

打印IPhost

3.使用线程池的域名解析

通过方法2,我们知道在一个进程中创建太多线程来执行任务是危险的。自然,我们会考虑使用有限数量的线程进行域名解析。例如,使用100个线程来解析500个域名。当一个线程完成解析时,它不需要关闭,继续从队列中取出一个域名进行解析。这种情况会一直重复,直到队列为空并且所有域名都被解析。使用线程池的另一个优点是省略了创建新线程和关闭线程的时间;如果线程执行的任务时间很短,线程池节省的时间会相当可观。可以预期,这种方法将比方法2花费更多的时间。

使用线程池的域名解析的代码比方法2复杂,但也不难理解。代码仍然主要分为两部分:一部分是继承自线程的子类。线程,成员函数run(),用来实现每个线程需要完成的工作。在run()函数中,当一个线程空闲时,它去队列取出一个域名;直到队列为空,函数的任务才完成。另一部分是一个类,负责将任务分配给新创建的线程,并检查是否所有移交给run()的任务都已完成。因为管理行为比方法2多,所以这些管理行为被集成到一个类中。因为一个线程需要多次解析域名,所以需要对要解析的域名进行排队。队列的容量有限。只有当队列未满时,才能将域名添加到队列中,等待线程将其取走并解析。

导入系统

导入队列

导入线程

导入时间

导入套接字

def ReadHost(文件):

f=打开(文件)

hosts=[]

lines=f.readlines()

对于线路:中的线路

hosts.append(第[7:-2]行)

f.close()

返回主机

def WriteHost(文件,ips):

f=打开(文件,“a”)

对于ips:中的ip

f.write(ip[1] ' ' ip[0] '\n ')

f.close()

def getIP(主机):

try:

res=socket.getaddrinfo(主机,无)

对于res:中的项目

打印主机,项目[4][0]

例外,e:

打印e

班级作业(穿线。螺纹):

def __init__(self,queue):

threading.Thread.__init__(self)

self.queue=队列

#self.getQueueItem()

self.start()

def getQueueItem(self):

而不是self.queue.empty():

print self.queue.get()

def run(self):

而True:

try:

func,args=self . queue . get(block=False)

apply(func,(args,)

#func(args)

self.queue.task_done()

例外,e:

打印e

破裂

类别工作管理器(对象):

def __init__(self,hosts,threadMax):

队列=队列。队列()

self.threads=[]

自我。__initWorkQueue(getIP,主机)

自我。__initThreadPool(threadMax)

def __initWorkQueue(自身、功能、主机):

对于主机:中的主机

#打印功能。__名称_ _,主机

self.queue.put((func,host))

def __initThreadPool(self,threadMax):

适用于I范围(threadMax):

自我.线程.附加(工作(自我.队列))

def检查队列(自身):

return self.queue.qsize()

def checkComplete(self):

for thread in self.threads:

if thread.isAlive():

thread.join()

if __name__=='__main__':

hostFile=sys.argv[1]

主机=读取主机(主机文件)

#打印主机

start=time.time()

打印“启动dns”。

wm=工作管理器(主机,10)

wm.checkComplete()

print 'spending: ',time.time()-start

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航