Crawl dnsdb subdomain 思路
零.dnsdb介绍:
一.整体思路:
我们在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
如果用第一种方法的话,那么这个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使用