Python入门到逆袭10(项目篇-爬虫1)

1. 简介

网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本

 

2. 需求

需求,即是这个爬虫项目,需要实现什么内容,实现到什么程度,我们在这里定义一下,然后围绕着这个目标去实现。

需求:

  1. 模拟百度搜索,定义关键字,搜索百度的前N页的域名。
  2. 并通过收集到的域名去解析该域名的主机IP。
  3. 然后通过主机IP获取这个主机IP下所有的域名。

 

3. 实现思路

爬虫作为一个入门级项目,实现的思路比较简单,主要是根据需求来分析整理对应的逻辑即可。

通过需求分析如下:模拟百度搜索,定义关键字,搜索百度的前N页的域名, 需要实现功能

通过需求分析如下:

模拟百度搜索,定义关键字,搜索百度的前N页的域名, 需要实现功能

  • 实现一个https请求接口。
  • 解析https请求得到的html代码中的dns域名。
  • 然后重复前N页(这里是一个设计点,N是放在代码中还是定义在配置文件中)。

并通过收集到的域名去解析该域名的主机IP

  • 通过python的接口将dns域名解析出主机IP。

然后通过主机IP获取这个主机IP下所有的域名

  • 这里相当于是另外一个爬虫了,去爬取IP解析dns域名的网站了。
  • 然后将所有结果保存下来。

 

4. 第三方库安装

pip install requests --default-timeout=600

pip install xmltodict --default-timeout=600

通过pip install命令直接安装reqeusts库即可,安装后,能够import requests成功就是安装成功了。

Python入门到逆袭10(项目篇-爬虫1)

 

5. 模块实现

5.1 http/https请求模块

5.1.1 原理解释

从实现思路来看,我们第一步应该是要能够模拟谷歌浏览器正常访问百度一样,如果熟悉http协议,就能够比较清楚的直到浏览器是怎么向百度发起请求,获得这个页面的。

在谷歌浏览器上按F12,然后刷新一下页面,如下图。

Python入门到逆袭10(项目篇-爬虫1)

我们能看到发起的是一个https请求,关键参数如下:

Request URL : 请求的url地址

Request Method : GET, 是一个get请求(HTTP的请求有GET/POST/PUT/DELETE等)

Request Headers : 请求头,请求发起方构造(这里指的是浏览器)

Respons Headers : 返回头,web服务器返回(这里指的是百度的web 服务器)

 

按右键,选择 查看网页源代码

Python入门到逆袭10(项目篇-爬虫1)

 

将会新开一个页面

Python入门到逆袭10(项目篇-爬虫1)

而实际上,百度web服务器返回的只是这个源代码,并不是我们看的多姿多彩的页面,多姿多彩的页面,是谷歌浏览器根据web服务器返回的html源代码进行解释之后的结果。

 

5.1.2 模块代码实现

爬虫的主要核心即使实现一个http/https的请求功能,然后将web服务器返回的html源码进行分析,提取所需要的内容。

然而,http/https的请求功能压根就不需要你实现,pytnon的第三方库requests已经集成了相关的功能,例如get、post等接口。

例如:

import requests

res = requests.get(url, headers=headers, verify=False)

print(str(res.content))

结果:

返回的结果,和谷歌浏览器上看到的html源码是一模一样的。

Python入门到逆袭10(项目篇-爬虫1)

解释:

参数url : Request URL

参数headers :Request Headers

参数 : ssl证书告警

 

完整代码实现:

# coding=utf-8
# Copyright Sangfor. All rights reserved

'''
http/https操作通用接口
'''

import requests
import exceptions as comm_ex

requests.packages.urllib3.disable_warnings()


def get_http_header(host,  cookie=None):
    '''
    获取http请求头
    :param host:  host
    :param cookie:
    :return:
    '''

    header = {}
    header[
        'Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
    header['Accept-Encoding'] = 'gzip, deflate, br'
    header['Accept-Language'] = 'zh-CN,zh;q=0.9'
    header['Connection'] = 'keep-alive'
    if cookie:
        header['Cookie'] = cookie

    header['Host'] = host
    header['Upgrade-Insecure-Requests'] = '1'
    header[
        'User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    return header


def https_get_html(url, host, cookie=None):
    '''
     https请求
    :param url:
    :param host:
    :param cookie:
    :return: 返回html页面
    '''

    headers = get_http_header(host, cookie)
    res = requests.get(url, headers=headers, verify=False)
    if res.status_code != 200:
        raise comm_ex("请求{}失败,错误码:{}".format(url, res.status_code))
    return str(res.content)

 

函数接口解释:

为什么要着实现,而不是在一个函数中全部实现。

函数的功能越简单越好,一个函数最好只做一件事情。

https_get_html参数通过传递url、host、cookie参数供外部调用。

 

5.2 配置文件操作模块

关键逻辑写完之后,我们幻想一个场景,当我们查询关键字key1的时候,我们可以在代码中将url拼接出来,但是明天我们想查询关键字key2的时候,我们来改代码吗? 那后天呢?不可能每天都来改代码吧。

所以,我们可以考虑将关键字存储在配置文件中,当想要换关键字的时候,只要改下配置文件,然后重新运行下脚本,就完事了。

结合之前文件读写章节中,有一个就是ini配置文件,这个文件在实际项目中用的非常非常多,也很好用。

我们定义一个配置文件。

[keyword]

keyword_cnt=1 #关键词个数

keyword0=传奇私服 #第1个关键字,如果有多个,keyword_cnt=N

start_page0=1 # 从百度查询的第1页开始

end_page0=10 # 到第10页结束

step = 0 # 当前运行到第0步(读取、分析、查询主机是3个步骤)

 

[sys]

step=3

 

5.2.1 配置模块类

配置模块单独定义一个info.py,专门用于处理配置文件的读写等操作。

设计两个结构对象:

class KeyIniObj:
    '''
    关键字类对象
    '''
    def __init__(self):
        self.keyword = ''
        self.start_page = 0
        self.end_page = 3

KeyIniObj这个类直接对应Info.ini配置文件中的一个关键字对象。

class InfoIniObj:
    def __init__(self):
        self.key_list = []      #关键字列表
        self.step = 0           #当前运行的步骤

InfoIniObj这个类则是对应整个info.ini配置文件,key_list是所有的关键字对象列表。

 

5.2.2 模块代码实现

# coding=utf-8
# Copyright Sangfor. All rights reserved

'''
配置读取
'''

import ConfigParser
import const

class KeyIniObj:
    '''
    关键字类对象
    '''
    def __init__(self):
        self.keyword = ''
        self.start_page = 0
        self.end_page = 3


class InfoIniObj:
    def __init__(self):
        self.key_list = []      #关键字列表
        self.step = 0           #当前运行的步骤

def read_info_ini():
    '''
    读取配置
    :return:
    '''
    conf = ConfigParser.ConfigParser()
    conf.read(const.INFO_INI_FILE)

    info_obj = InfoIniObj()
    cnt = int(conf.get('keyword','keyword_cnt'))
    for i in range(0, cnt):
        key_obj = KeyIniObj()
        key_obj.keyword = conf.get('keyword', 'keyword{}'.format(i))
        key_obj.start_page = int(conf.get('keyword', 'start_page{}'.format(i)))
        key_obj.end_page = int(conf.get('keyword', 'end_page{}'.format(i)))
        info_obj.key_list.append(key_obj)

    info_obj.step = int(conf.get('sys','step'))
    return info_obj

def upd_info_ini(info_obj):
    '''
    更新step
    :param info_obj: info_obj对象
    :return:
    '''

    config = ConfigParser.ConfigParser()
    config.add_section("keyword")

    cnt = len(info_obj.key_list)
    i = 0
    config.set('keyword','keyword_cnt', str(cnt))
    for key_info in info_obj.key_list:
        config.set('keyword', 'keyword{}'.format(i), key_info.keyword)
        config.set('keyword', 'start_page{}'.format(i), str(key_info.start_page))
        config.set('keyword', 'end_page{}'.format(i), str(key_info.end_page))
        i = i + 1

    config.add_section("sys")
    config.set('sys','step', str(info_obj.step))
    config.write(open(const.INFO_INI_FILE, "w"))

 

5.3 公共模块

公共模块,见名知意,就是干一些很普遍通用的函数,提供给各个模块使用。

例如在项目中一个很常用的接口,日志打印接口。

这里直接列一下代码,没有需要怎么解释的地方。

# coding=utf-8
# Copyright Sangfor. All rights reserved

'''
公共函数
'''

import os
import const
import comm

def dictkey_to_list(src_dict):
    '''
    保存指定内容到文件中
    :param filename: 文件名
    :param content: 文件内容
    :return:
    '''
    key_list = []
    for key, val in src_dict.items():
        key_list.append(key)

    return key_list

def save_file(filename, content):
    '''
    保存指定内容到文件中
    :param filename: 文件名
    :param content: 文件内容
    :return:
    '''
    fp = open(filename, 'w')
    fp.write(content)
    fp.close()

def read_file(filename):
    '''
    读取制动文件中的内容
    :param filename: 文件名
    :return: 文件内容
    '''
    fp = open(filename)
    content = fp.read()
    fp.close()
    return content


def log_print(content):
    '''
    写日志
    '''
    print(content)
    fp = open(const.LOG_FILE, 'a+')
    fp.write(content + '\r\n')
    fp.close()

 

 

章节太长,下一个章节写baidu dns关键字提取、dns -> ip解析,然后通过IP在解析出当前IP下的所有dns域名。

上一篇:QT运行出现identifier ‘nullptr’ is a keyword in C++11的解决办法


下一篇:非常实用的七个Python 库,你都知道吗