接上章节 项目篇-爬虫1
https://blog.csdn.net/dengshengli123/article/details/113803173
5. 模块实现
5.4 baidu DNS提取
通过上面的https_get_html接口,我们可以得到一个完整的html源代码,此时要做的就是对这个html进行解析,获取到我们所需要的dns域名。
解析的方法有多种,可以通过对html进行解析成html对象,然后获取指定节点的值,或者是直接对整个html进行字符串搜索。
因为个是固定,逻辑简单,这里就用字符串搜索了。
5.4.1 原理解释
第一步,我们应该实现一个爬取一页html代码的接口,然后解析这个html中的dns域名,然后调整页码,循环N次。
如上图所示,请求这个url将会返回这个页面,我们分析一下这个url,如下:
https://www.baidu.com/s?wd=%E4%BC%A0%E5%A5%87%E7%A7%81%E6%9C%8D&pn=0&oq=%E4%BC%A0%E5%A5%87%E7%A7%81%E6%9C%8D&ie=utf-8&usm=1&fenlei=256&rsv_idx=1&rsv_pq=e21c2f4a003d8d79&rsv_t=3fb3HuQCP3KjfUu9l7PPnZSOYbTUoU2CtSnggUFM1dy%2BvQohv97jJgNVTx8
其中:
wd=%E4%BC%A0%E5%A5%87%E7%A7%81%E6%9C%8D, wd后面接的是(编码后)关键字。
pn=0 是索引位置,即从0开始,往后的10条数据(一页是10条数据)
其它参数可有可无,可以不传。
因此,根据这个原理,我们可以实现如下接口:
def get_baidu_keyword_dns_onepage(keyword, page):
'''
获取第几页的关键字域名
:param keyword: 关键字
:param page: 页数
:return: html字符串
'''
hosts = 'www.baidu.com'
page_index = page * 10
url = 'https://www.baidu.com/s?{}&pn={}'.format(keyword, page_index)
comm.log_print("url : {}".format(url))
html_content = http_comm.https_get_html(url, hosts)
return html_content
解释:
这个接口代码简洁,功能极度简单,就是通过传递进来的关键字和当前页数,调用之前实现的https_get_html函数获取html代码。
第二步,就是对这个获取下来的html代码来进行解析,获取dns域名。
我们这里使用的字符串搜索的方法,所以,就来找一下dns对应的关键的字符串有哪些。
鼠标放在域名上,然后右键->检查
通过红框的这些关键字,逐步查询截取,就能获取dns了,如下代码实现:
def parse_html_dns(html_content):
'''
解析html中的域名
:param html_content: html内容
:return: 域名列表
'''
dns_list = []
while(True):
start_flag = html_content.find('div class="f13')
if start_flag <= 0:
break
html_content = html_content[start_flag:]
start_flag = html_content.find('href="')
html_content = html_content[start_flag:]
start_flag = html_content.find('">')
html_content = html_content[start_flag + 2:]
end_flag = html_content.find('</a>')
url_str = html_content[:end_flag]
start_flag = url_str.find('/')
if start_flag > 0:
url_str = url_str[:start_flag]
url_str = rm_nouse_word(url_str)
# 简单判断是否为域名
if url_str.count('.') >= 2:
dns_list.append(url_str)
return dns_list
解释:
上一步get_baidu_keyword_dns_onepage 函数的返回结果就是作为parse_html_dns函数的参数,将每个解析出来的dns域名加入到list列表中,返回给外部调用函数。
最后一步,就是一个循环函数了,传递关键字和当前页数,将每次的返回结果保存。
for i in range(start_page, end_page):
html_content = get_baidu_keyword_dns_onepage(keyword_urlcode, i)
html_content = html_content
one_dns_list = parse_html_dns(html_content)
dns_list += one_dns_list
5.4.2 模块代码实现
# coding=utf-8
# Copyright Sangfor. All rights reserved
'''
通过百度关键字搜索,把对应的域名查询出来
'''
from urllib import quote,unquote
import http_common as http_comm
import comm
def get_baidu_keyword_dns_onepage(keyword, page):
'''
获取第几页的关键字域名
:param keyword: 关键字
:param page: 页数
:return: html字符串
'''
hosts = 'www.baidu.com'
page_index = page * 10
url = 'https://www.baidu.com/s?{}&pn={}'.format(keyword, page_index)
comm.log_print("url : {}".format(url))
html_content = http_comm.https_get_html(url, hosts)
return html_content
def rm_nouse_word(content):
'''
删除无用的单词
:param content:
:return: content
'''
if content.find('<') < 0 and content.find('>') < 0:
return content
while(True):
index = content.find('>')
if index < 0:
break
content = content[index + 1:]
return content
def parse_html_dns(html_content):
'''
解析html中的域名
:param html_content: html内容
:return: 域名列表
'''
dns_list = []
while(True):
start_flag = html_content.find('div class="f13')
if start_flag <= 0:
break
html_content = html_content[start_flag:]
start_flag = html_content.find('href="')
html_content = html_content[start_flag:]
start_flag = html_content.find('">')
html_content = html_content[start_flag + 2:]
end_flag = html_content.find('</a>')
url_str = html_content[:end_flag]
start_flag = url_str.find('/')
if start_flag > 0:
url_str = url_str[:start_flag]
url_str = rm_nouse_word(url_str)
# 简单判断是否为域名
if url_str.count('.') >= 2:
dns_list.append(url_str)
return dns_list
def get_baidu_keyword_dns(info_list):
'''
通过百度关键字搜索,获取相关的域名
:param key_word: 关键字
:param start_page: 起始页
:param end_page: 结束页
:return: 域名列表
'''
dns_list = []
for info_obj in info_list:
key_word = info_obj.keyword
start_page = info_obj.start_page
end_page = info_obj.end_page
keyword_dict = {'wd': key_word}
keyword_urlcode = 'wd=' + quote(key_word,'utf-8')
comm.log_print('查询关键字 ({})的域名,编码后关键字为 : {}'.format(key_word, keyword_urlcode))
for i in range(start_page, end_page):
html_content = get_baidu_keyword_dns_onepage(keyword_urlcode, i)
html_content = html_content
one_dns_list = parse_html_dns(html_content)
dns_list += one_dns_list
return dns_list
5.5 阶段性运行,看看结果如何
到这一步,我们可以调用已经实现的函数,看下运行结果
# coding=utf-8
# 爬虫
import info_ini
import baidu_wd_dns
def main():
'''
主函数
:return:
'''
info_obj = info_ini.read_info_ini()
dns_list = baidu_wd_dns.get_baidu_keyword_dns(info_obj.key_list)
print(dns_list)
if __name__ == '__main__':
main()
结果:
5.6 域名解析IP
这一步,就是将域名解析成IP,这个操作操作系统一般都支持,python可提供相关的库直接进行解析。
5.6.1 原理解释
我们可能应该用过ping命令,再试试看。
如上图,112.80.248.75这个ip就是www.baidu.com这个域名的ip。
通过nslookup www.baidu.com,即可解析出域名对应的IP了。
而在python中,用系统库socket的getaddrinfo函即可解析。
其实,都这里这里了,我还想获取一下这个IP的归属地,到底看看这些灰色网站的web服务器搭建在哪些地区。
通过这个https://ip-api.com/网站,我们可以轻松的查到IP的归属地,如下图
而通过http://ip-api.com/json/154.211.13.227 这个方式,则会返回一个json数据(这个就相当于北向接口)。
5.6.2 模块代码实现
- 域名解析IP
import socket
def get_dns_ip(dns_name):
'''
获取域名对应的ip
:param dns_name:
:return: 域名对应的IP
'''
web_ip = None
try:
res = socket.getaddrinfo(dns_name, None)
web_ip = res[0][4][0]
except Exception as ex:
comm.log_print('域名{}解析失败........'.format(dns_name))
return web_ip
- 获取IP的归属地
def get_ip_belong(ip): ''' 获取ip的归属地 ''' host = 'ip-api.com/json/' url = ' http://ip-api.com/json/{}'.format(ip) res = http_comm.https_get_html(url, host) res = res.replace("b\'", '') res = res.replace("\'", '') res_json = json.loads(res) belong = '{} - {}'.format(res_json['country'], res_json['city']) return belong
结果:
5.7 通过IP获取域名
这个模块将会将该IP下的所有域名搜索出来。
这个逻辑和百度获取dns原理一样,也是在某一个网站上将数据爬取下来。
5.7.1 原理解释
通过https://site.ip138.com/ 上,便可以将IP对应的所有域名查询出来
同理,我们构造https请求,将html源码拉取下来,然后解析获得我们所需要的数据即可。
5.7.2 模块代码实现
def get_dns_from_ip138(ip_addr):
'''
从site.ip138.com去查找ip对应的域名
https://site.ip138.com/192.126.127.240/
:param ip_addr:
:return: 域名列表
'''
dns_list = []
host = 'site.ip138.com'
url = 'https://site.ip138.com/{}/'.format(ip_addr)
html_content = http_comm.https_get_html(url, host)
# 解析出域名
start_index = html_content.find('<ul id="list">')
end_index = html_content.find('</ul>', start_index)
content = html_content[start_index:end_index]
# 最多提取100个域名
start_flag = 'target="_blank">'
end_flag = '</a></li>'
for i in range(0, 100):
start_index = content.find(start_flag)
if start_index <= 0:
break
end_index = content.find(end_flag, start_index)
dns_name = content[start_index + len(start_flag):end_index]
dns_list.append(dns_name)
content = content[end_index + 1:]
return dns_list
到这里,关键的逻辑就基本上都写完了,我们将最后一个逻辑,数据保存写完,就能完整的查看运行结果了。
5.8 数据保存
数据保存为将上面处理得到的最终结果保存到cvs文件中(类似excel文件,方便查看)
实现如下:
def save_collect_info(filename, dns_detail_info):
'''
保存收集的信息到cvs文件中
:param filename: 文件名
:return:
'''
fp = open(filename, 'w')
title = '"主机IP"\t"归属地"\t"主域名"\t"关联域名"\n'
fp.write(title)
for web_ip, host_info in dns_detail_info.items():
content = '"{}"'.format(web_ip)
content += '\t"{}"'.format(host_info['belong'])
content += '\t"{}"'.format(','.join(comm.dictkey_to_list(host_info['dns_info'])))
#关联域名
link_dns = {}
for dns_name, dns_info in host_info['dns_info'].items():
for tmp_dns_name in dns_info['link_dns']:
link_dns[tmp_dns_name] = {}
content += '\t"{}"'.format(','.join(comm.dictkey_to_list(link_dns)))
content += '\n'
fp.write(content)
fp.close()
结果如下:
用wps或者excel打开
参考代码 :
https://github.com/minlixia/python (project/crawler)