1. 简介
网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。
2. 需求
需求,即是这个爬虫项目,需要实现什么内容,实现到什么程度,我们在这里定义一下,然后围绕着这个目标去实现。
需求:
- 模拟百度搜索,定义关键字,搜索百度的前N页的域名。
- 并通过收集到的域名去解析该域名的主机IP。
- 然后通过主机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成功就是安装成功了。
5. 模块实现
5.1 http/https请求模块
5.1.1 原理解释
从实现思路来看,我们第一步应该是要能够模拟谷歌浏览器正常访问百度一样,如果熟悉http协议,就能够比较清楚的直到浏览器是怎么向百度发起请求,获得这个页面的。
在谷歌浏览器上按F12,然后刷新一下页面,如下图。
我们能看到发起的是一个https请求,关键参数如下:
Request URL : 请求的url地址
Request Method : GET, 是一个get请求(HTTP的请求有GET/POST/PUT/DELETE等)
Request Headers : 请求头,请求发起方构造(这里指的是浏览器)
Respons Headers : 返回头,web服务器返回(这里指的是百度的web 服务器)
按右键,选择 查看网页源代码
将会新开一个页面
而实际上,百度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源码是一模一样的。
解释:
参数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域名。