域名遍历搜索python实现

来源:互联网 发布:ubuntu 安装sqlserver 编辑:程序博客网 时间:2024/05/17 06:33

前记——

其实我的第一篇博客就是写如何获取给定网址的信息,也有那家公司的面试题。

最近一个星期想把那个面试题中的程序研究透彻..

毕竟既然有可以参考的大公司的大神的代码,肯底要学习一番嘛,(师夷长技以制夷..不对,额反正就大概是这个意思啦)

正文——

程序的功能很简单,就遍历可能的域名组合,每个地址都访问一次,获取它的标题。
功能是很简单,但是网址数一旦多就变得很麻烦。
实现的方式是采用生产者——消费者模式。定义两个类,一个用于产生网址,另外一个用于判断网址。
具体使用到的技术:
1:产生地址是采用yield生成器,以便后续的程序改进。
2:判断地址的步骤分为五步。1:开启判断地址的多进程(默认为6个)
                                                 2:每个进程开启一定数目的协程(默认50个)
                                                 3:在进程安全的队列中取出网址,并初始化dns模块,以dns模块首先判断网址是否可以解析。
                                                 4:再使用urllib模块的urlopen方法判断访问网址的状态码。
                                                 5:采用urllib2模块获取网页的正文。
                                                 6:使用beautifulsoup处理html文本,获取标题。
简单的大致流程就是这样。
以下是代码:
# coding=utf-8import dnsimport syssys.setrecursionlimit(10000)import stringimport urllibimport urllib2import multiprocessingfrom multiprocessing import Queuefrom multiprocessing import RLockimport geventfrom gevent import monkeyimport MySQLdbimport dns.resolverfrom bs4 import BeautifulSoup# 一定不能让猴子补丁覆盖掉线程thread类,不然程序不会运行。参考 http://xiaorui.cc/2016/04/27/%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90%e4%b9%8bgevent-monkey-patch_all%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86/monkey.patch_all(thread=False)#socket.setdefaulttimeout(8)domainqueue = Queue()Message = Queue()Mesdict = {}usefulIp = Queue()uselessIP = Queue()class subsearch(object):    def __init__(self):        self.set = set()        net = self._creatdomain()        for i in range(100000):            a = net.next()            self.set.add('www.' + a + '.com')        for i in self.set:            domainqueue.put_nowait(i)    def _creatdomain(self):        ergodicword = ['', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']        for word in string.lowercase:            ergodicword.append(word)  # 生成26个英文字母并添加进去        for i in ergodicword:            for j in ergodicword:                for k in ergodicword:                    for l in ergodicword:                        for m in ergodicword:                            yield i + j + k + l + mclass subvisit(object):    def __init__(self, DNSSERVER, headers):        self.dnsserver = DNSSERVER        self.run()        self.headers = headers    def start(self, i):        self.id = i        self.work()    def work(self):        resolver1 = dns.resolver.Resolver()        resolver1.lifetime = resolver1.timeout = 5.0        resolver1.nameservers = [self.dnsserver]        A = ''        while not domainqueue.empty():            try:                url = domainqueue.get()            except BaseException:                break            try:                A = resolver1.query(url)            except dns.resolver.NXDOMAIN as xxx_todo_changeme:                dns.resolver.Timeout = xxx_todo_changeme                continue            except dns.exception.DNSException:                continue            if A:                headers['Host'] = url                try:                    codenum = urllib.urlopen('http://' + url).getcode()                    if codenum == 200 or codenum == 304:                        pass                    else:                        continue                    req = urllib2.Request('http://' + url, headers=headers)                    res = urllib2.urlopen(                        req, timeout=10)  # urllib2的get方法访问url                    response = res.read()  # 获取正文                    res.close()                    del res                    soup = BeautifulSoup(response)                    title = soup.title                except Exception as e:                    #title = "Not Found"                    continue                if title:                    title = str(title)[7:-8]                    lock.acquire()                    print str(url).ljust(13),str(A[0].address).ljust(15),title                    lock.release()                #Message.put_nowait((url, A[0].address,title))                # usefulIp.put_nowait(url)    def run(self):        try:            threads = [gevent.spawn(self.start, i) for i in range(50)]            gevent.joinall(threads)        except KeyboardInterrupt as e:            passif __name__ == '__main__':    lock = RLock()    DnsServer = ['114.114.114.114', '114.114.115.115', '223.5.5.5',                 '223.6.6.6', '112.124.47.27', '114.215.126.16']    headers = {        'Host': '',        'User-Agent': 'Mozilla / 5.0(X11;Ubuntu;Linuxx86_64;rv:55.0) Gecko / 20100101Firefox / 55.0',        'Accept': 'text / html, application / xhtml + xml, application / xml;q = 0.9, * / *;q = 0.8',        'Accept-Language': 'zh - CN, zh;q = 0.8, en - US;q = 0.5, en;q = 0.3',        'Accept-Encoding': 'gzip, deflate, br',        'Connection': 'keep - alive',    }    b = subsearch()    pool = multiprocessing.Pool(processes=6)    result = []    for i in xrange(6):        result.append(pool.apply_async(subvisit, args=(DnsServer[i], headers)))    pool.close()    pool.join()
可以改进的部分:
1:把成功获取的数据存入到数据库,或者写入文件。需要注意的是获取到的标题有可能是韩文、日文、阿拉伯文等等,主要文本处理。
2:采用optparse模块。不要使用ide编写(特别是pycharm)太占用内存了(分分钟内存80以上)
3:有些网址可以访问,可是没有标题。在这里我的处理是把它剔除,但其实beautifulsoup的处理能力很强,我只略懂皮毛,或许可以获取网页的小标题代替主标题。
4:dns模块的处理可以更优化。在这里我是每一个进程采用一个dns解析器,但其实这样风险挺大,一旦一个解析器出现问题,整个进程所有协程都讲停工。
5:程序有时会卡在urllib2.read()上面,这个问题烦扰我两天还是解决不了。(网上的解决方案是采用超时的办法,但其实好像..没什么用..),我觉得这个问题的出现有两种可能,一是在访问的时候确实没有超时,只是在read()下载文档的时候速度过慢,导致程序都在等待,而不会抛出异常;二就是read()的时候由于协程也都在工作,在处理gerrnlet.lock()程序内部出现阻塞或死锁(虽然是协程,好像源码也有lock(),但就算如此为什么不抛出异常?..)

下面是程序运行的截图:



还是要打码的...

网站遍历的速度会与网速有挺大的关系。(可惜校园网比较渣,每次测试的速度都有较大程度的不同)
半个小时检索出2000个有标题的网址(按照测试的经验,大概判断的域名与可使用的域名比例是15比1,即半小时判断30000个,速度还是比较慢)
我会尝试不同的协程数目以及优化代码尝试改进。
在这里很感谢大公司的代码,对我来说是宝贵的学习资料。