Python入门到逆袭11(项目篇-爬虫2)

接上章节 项目篇-爬虫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)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaxiadeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值