Crawl dnsdb subdomain 思路

零.dnsdb介绍:

https://dnsdb.io/zh-cn/

一.整体思路:

我们在dnsdb这个网站进行查询时的操作可分为两步:登录、查询。

  • 登录:

有些网站是在用户输错密码后,才会弹出验证码进行人机识别。

由于Dnsdb这个网站是一开始就有验证码,而且验证码似乎很难被绕过。

  • 查询:

输入域名进行查询,实际上就是向服务器发送了一个get请求。

爬虫就是为了模拟人在浏览器的操作。 知道我们在浏览器的操作步骤(登录+查询)后,我们就要用爬虫去模拟我们的操作。

  • 模拟登录(由于模拟登录难度太大,选择其他方案)
  • 模拟查询

二.解决登录问题:

利用已有cookie登录:

手工登录后将获取到的dnsdbid填入下面的cookie中

然后利用requests.get发送get请求。


三.模拟查询

模拟点击下一页的动作。

1.获取页面函数

def get_page_html(sub_domain,page_number):
    cookies = dict(
                    __cfduid='d78e41b631bda1bdbc7bf767c1baf19e81498723068',
                    csrftoken = '66NKPO8z5yvomv26d3DKUV5bsxaIhwFzZc0km3xQ6vW8dh3cbp5LuNUddPa2acVP',
                    UM_distinctid='15cf2e340ab9-0ac37b44d6c9f-49556d-13c680-15cf2e340ac312',
                    CNZZDATA1260172355 = '1259417091 - 1498722382 - % 7C1498796777',
                    Hm_lvt_a23e9746ef9e5704a5a2e6e758c55e21 = '1498723604, 1498789317',
                    #Hm_lpvt_a23e9746ef9e5704a5a2e6e758c55e21 = '1498797308',
                    dnsdbid = 'fjozyxxm8qnj47zchh9i1bgm3fnhhs46', #手工登录后,将dnsdbid复制到这里
                    )


    headers = {'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
                             'Chrome/52.0.2743.116 Safari/537.36',
               'Host':'www.dnsdb.io',
                'Upgrade-Insecure-Requests':'1',
                }
    # 'Referer':'https://www.dnsdb.io/zh-cn/',


    #https://www.dnsdb.io/zh-cn/search?q=baidu.com&page=1
    url = 'https://www.dnsdb.io/zh-cn/search?q='+sub_domain+'&page='+page_number
    html_page = requests.get(url, headers=headers, cookies=cookies).content
    return html_page

2.抓取host列表

如果直接用正则q=host:进行定位的话,爬取到的数据会有CNAME字段的干扰,所以先用cleaned_data1 = re.findall('<th scope="row">(.*?)</td>', html, re.S)定位一次,先清除其他数据。

保留剩下只有HOST的数据,然后再用一次正则cleaned_data2 = re.findall('q=host:(.*?)">', cleanedhtml, re.S)去匹配,就可以获得未去重的host列表

#2次正则匹配后获得未去重的host列表
def get_host_list(html):
    #<th scope="row">在页面中具有唯一性,可以定位
    cleaned_data1 = re.findall('<th scope="row">(.*?)</td>', html, re.S)
    #cleaned_data1将列表转换为字符串
    cleanedhtml = '/'.join(cleaned_data1)
    cleaned_data2 = re.findall('q=host:(.*?)">', cleanedhtml, re.S)

    return cleaned_data2

3.获得总页码数:

我们一般查询到结果之后就会点击第2页,第3页进行浏览,而实际过程中就是通过浏览器向服务器分别发送请求,而我们要做的就是将请求中有效的的参数一字不落地过去。

为了遍历所有的host数据,我们需要知道最后一页的页码,刚好页面上有显示。

而我们只要能定位到这个页码数就行了。 因为页码所在位置有唯一性的标记 依旧是采取正则表达式去定位并获取数据。 接着获得的数据是个分数,需要简单处理一下获得分母。

def get_page_totalnumber(html):
    page_number = re.findall('页码:</b>(.*?)</span>', html, re.S)
    #print page_number
    str = '/'.join(page_number)  #list转换为str
    page_totalnumber = str.split('/')[1]#获得分母
    print ("页码数为%s" % page_totalnumber)
    return int(page_totalnumber)

4.遍历所有页面

有了总页码数之后,只需要写个循环就可以遍历所有页面了。

# 循环获取页面

    for page_number in range(1,page_total_number+1,1):
        print ("正在访问第%d个页面"%(page_number))
        html_page = get_page_html(domain, str(page_number)) #访问页面
        hostlist = get_host_list(html_page)  #抓取hostlist存入一个list中
        if len(hostlist) > 0:
            #print hostlist
            print '正常爬取ing'
        else:
            print '无法爬取到数据'
            break
        sub_domains_rep.extend(hostlist)

定义了一个列表sub_domains_rep=[],将每个页面抓取到的host都添加进这个列表里。

由于可能有重复的host,所以我们需要对列表进行去重。

5.列表去重函数

# 列表去重函数
def remove_repeat(list):
    newlist = []
    for rep in list:
        if rep not in newlist:
            newlist.append(rep)
    return newlist

四.解决cookie会失效的问题

一开始以为这个网站存在会话固定的问题,cookie完全不会发生变化,可以一劳永逸地使用。

其实不然,在过一段时间后,原有的cookie就会失效,导致爬虫程序无法正常爬取网页了。

就像平时我们在浏览网站的时候,如果长时间停止在某个页面,没有对页面进行操作的话,服务器会注销我们的登录状态,即注销了我们的cookie。

为了保持会话,我们要定期向服务器发送请求,有两种思路。

1.python定时程序

每隔一段时间执行指定函数

# -*- coding:utf-8 -*-
__author__ = 'jerry'

import os
import time
def print_ts(message):
    print "[%s] %s"%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), message)
def run(interval, command):
    print_ts("-"*100)
    print_ts("Command %s"%command)
    print_ts("Starting every %s seconds."%interval)
    print_ts("-"*100)
    while True:
        try:
            # sleep for the remaining seconds of interval
            time_remaining = interval-time.time()%interval
            print_ts("Sleeping until %s (%s seconds)..."%((time.ctime(time.time()+time_remaining)), time_remaining))
            time.sleep(time_remaining)
            print_ts("Starting command.")
            # execute the command
            status = os.system(command)
            print_ts("-"*100)
            print_ts("Command status = %s."%status)
        except Exception, e:
            print e
if __name__=="__main__":
    interval = 60*30 #60秒*30=30分钟
    command = r"python keep_dnsdb_subdomain.py"
    run(interval, command)

每隔30分钟向服务器发送请求,就相当于我们每隔三十分钟点击一下页面。

2.设置Linux定时任务Crontab

Linux定时任务Crontab详解

如果用第一种方法的话,那么这个python程序就会一直占用进程,所以从这个角度上来考虑,使用第二种方法较好。


五.程序说明

1.get_dnsdb_subdomain.py

  • 第一次运行前需要手工登录dnsdb,将dnsdbid的值复制到cookies对应的数值中
  • 需要接受用户输入:xxx.com
    • python get_dnsdb_subdomain.python baidu.com

2.refresh_session.py

每隔一段时间执行指定函数

3.keep_dnsdb_subdomain.py

简单像dnsdb网站发送一两个请求,配合refresh_session.py使用

results matching ""

    No results matching ""