域名遍历搜索python实现

前记——

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

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

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

正文——

程序的功能很简单,就遍历可能的域名组合,每个地址都访问一次,获取它的标题。
功能是很简单,但是网址数一旦多就变得很麻烦。
实现的方式是采用生产者——消费者模式。定义两个类,一个用于产生网址,另外一个用于判断网址。
具体使用到的技术:
1:产生地址是采用yield生成器,以便后续的程序改进。
2:判断地址的步骤分为五步。1:开启判断地址的多进程(默认为6个)
                                                 2:每个进程开启一定数目的协程(默认50个)
                                                 3:在进程安全的队列中取出网址,并初始化dns模块,以dns模块首先判断网址是否可以解析。
                                                 4:再使用urllib模块的urlopen方法判断访问网址的状态码。
                                                 5:采用urllib2模块获取网页的正文。
                                                 6:使用beautifulsoup处理html文本,获取标题。
简单的大致流程就是这样。
以下是代码:
# coding=utf-8
import dns
import sys
sys.setrecursionlimit(10000)
import string

import urllib
import urllib2
import multiprocessing
from multiprocessing import Queue
from multiprocessing import RLock
import gevent
from gevent import monkey
import MySQLdb
import dns.resolver
from 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 + m


class 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:
            pass


if __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个,速度还是比较慢)
我会尝试不同的协程数目以及优化代码尝试改进。
在这里很感谢大公司的代码,对我来说是宝贵的学习资料。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值