爬虫又被称为网页蜘蛛,网络机器人。是一种按照一定的规则,自动地抓取互联网上信息的程序或者脚本。
一、爬虫算法
在写爬虫时候有两种常用的算法可使用,即深度优先算法、广度优先算法。
深度优先算法
进群:548377875 即可获取数十套PDF哦!
对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。直到访问完成后再返回到最上层,然后重复上述步骤。
广度优先算法
从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。
负载均衡
当爬取量很大的话,需要负载到多台服务器同时运行(搜索引擎都是这么做的)。但这样会出现一个问题,当 A 服务器已经爬取完成的 URL,但 B 服务器并不知道 A 是否爬取完成,这样会造成资源的浪费,那怎么办呢?如何突破爬虫的瓶颈?
其中最简单的便是 URL 分类。举个栗子:现在有 A、B、C、D、X 五台服务器同时运行爬虫,X 为负载均衡服务器。所有的 URL 都要经过 X 服务器进行分配, X 服务器遇到域名是 .com 结尾的就分配给 A,遇到 .cn 结尾就分配给 B,遇到 .net 结尾就分配给 C,其他域名都分配给 D。这样就解决了爬虫瓶颈的问题,这个问题可是谷歌的面试题。
二、爬虫逻辑
爬虫可大致分为五个部分:
调度器:引擎,是爬虫逻辑实现的模块
管理器:URL 管理器,负责新增、删除、获取、存储、计数等功能,避免爬取重复的 URL。
下载器:HTML 下载器,将 URL 地址中的 HTML 内容获取到
解析器:HTML 解析器,将 HTML 获取到的内容进行分析
输出器:将分析完成后的数据进行输出、存储、利用等
爬虫逻辑可分解为如下几个部分:
查询管理器中是否有待爬取的 URL
调度器从管理器中获取一个待爬取 URL
将获取到的 URL 交给下载器处理
将下载器获取到的网页数据交给解析器处理
将解析器处理后的 URL 集合交给管理器
将解析器处理后的数据交给输出器
重复上述步骤
三、开发轻量级爬虫
1. 调度器
spider.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【调度器】
#
# 调度器又称为引擎,是爬虫逻辑实现的模块。
#
# 爬虫逻辑可分解为如下几个部分:
#
# 1. 查询管理器中是否有待爬取的 URL
# 2. 调度器从管理器中获取一个待爬取 URL
# 3. 将获取到的 URL 交给下载器处理
# 4. 将下载器获取到的网页数据交给解析器处理
# 5. 将解析器处理后的 URL 集合交给管理器
# 6. 将解析器处理后的数据交给输出器
# 7. 重复上述步骤
#
import manager, download, parser, output
class Spider(object):
def __init__(self):
# 实例化 管理器、下载器、解析器、输出器
self.manager = manager.Manager()
self.download = download.Download()
self.parser = parser.Parser()
self.output = output.Output()
def spider(self, url):
# 爬虫计数器
count = 0
# 将第一个 URL 放入管理器中
self.manager.add_url(url)
# 判断管理器中 URL 的数量是否为 0
while self.manager.num_url():
try:
# 从管理器中获取一个 URL
new_url = self.manager.get_url()
# 将获取到的 URL 交给下载器处理
html_cont = self.download.download(new_url)
# 将 URL 和 下载器处理后的网页数据交给解析器处理
new_manager, new_data = self.parser.parser(new_url, html_cont)
# 将解析器处理后的数据和新的 URL 分别交给管理器和输出器
self.manager.add_urls(new_manager)
self.output.add_data(new_data)
# 最多爬取 100 次
if count == 100:
break
count += 1
print('爬虫运行状态 ==> %d : %s' % (count, new_url))
except:
print('爬虫爬取失败')
# 当爬虫爬取完所有数据后,输出器将数据输出到文件
self.output.output()
if __name__ == '__main__':
# URL 入口(爬虫爬取的第一个 URL)
url = 'http://www.cnblogs.com/peida/archive/2012/12/05/2803591.html'
# 实例化调度器并启动爬虫
run = Spider()
run.spider(url)
2. 管理器
manager.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【管理器】
#
# 对外提供四个方法:
#
# 1. add_url(url)
# 添加一个 URL,接收一个参数 URL
#
# 2. add_urls(url)
# 批量添加 URL,接收一个参数 URL
#
# 3. num_url()
# 管理器中URL 的数量
#
# 4. get_url()
# 从管理器中获取一个 URL
#
class Manager(object):
def __init__(self):
# 待爬取的 URL 集合
self.new_urls = set()
# 已爬取的 URL 集合
self.old_urls = set()
# 添加一个 URL
def add_url(self, url):
# 判断 url 是否为空,为空则返回 None
if url is None:
return
# 判断 url 是否存在于【待爬取的 URL 集合】和【已爬取的 URL 集合】,如若都不在将新 url 添加到【待爬取的 URL 集合】中
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
# 批量添加 URL
def add_urls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
# 将 URL 集合交给 add_url() 函数处理
self.add_url(url)
# 返回管理器中 URL 的数量
def num_url(self):
return len(self.new_urls) != 0
# 从管理器中获取一个 URL
def get_url(self):
url = self.new_urls.pop()
self.old_urls.add(url)
return url
3. 下载器
download.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【下载器】
#
# requests 模块需要安装
# sudo pip3 install requests
#
# 对外提供一个方法:
#
# download(url)
# 下载网页中的内容,接收一个参数 URL
#
import requests
class Download(object):
def download(self, url):
# 判断 URL 是否为空,为空直接返回 None
if url is None:
return None
# 请求 URL
response = requests.get(url = url)
# 判断 URL 地址是否访问成功,若失败直接返回 None
if response.status_code != 200:
return None
return response.text
4. 解析器
parser.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【解析器】
#
# bs4 模块需要安装
# sudo pip3 install bs4
#
# 对外提供一个方法:
#
# 1. parser(url)
# BeautifulSoup DOM 树处理,接收两个参数 URL 和 html 内容
#
# 对内提供两个方法:
#
# 1. _get_urls(url, soup)
# 获取网页中的 URL,接收两个参数 URL 和 DOM 树
#
# 2. _get_data(url, soup)
# 获取网页中的数据,接收两个参数 URL 和 DOM 树
#
import re
from bs4 import BeautifulSoup
class Parser(object):
def _get_urls(self, url, soup):
urls = set()
links = soup.find_all('a', href=re.compile(r'/peida/archive/*'))
for link in links:
new_url = link['href']
urls.add(new_url)
return urls
def _get_data(self, url, soup):
res_data = {}
res_data['url'] = url
title_node = soup.find('div', class_='post').find('a')
res_data['title'] = title_node.get_text()
return res_data
def parser(self, url, html):
# 判断 url 是否为空,或网页数据是否为空,为空则返回 None
if url is None or html is None:
return
# 格式化 html 为 BeautifulSoup DOM 树
soup = BeautifulSoup(html, 'html.parser')
# 获取网页中的 URL
urls = self._get_urls(url, soup)
# 获取网页中的数据
data = self._get_data(url, soup)
return urls, data
5. 输出器
output.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# 【输出器】
#
# 对外提供两个方法:
#
# 1. add_data(data)
# 存储网页数据,接收一个参数 data
#
# 2. output()
# 将数据输出到 html 文件中
#
class Output(object):
def __init__(self):
self.datas = []
def add_data(self, data):
if data is None:
return
self.datas.append(data)
def output(self):
head = '''
'''
foot = '''
'''
with open('CSDN.html', 'w') as f:
f.write(head)
for data in self.datas:
f.write('%s' % (data['url'], data['url']))
f.write('%s' % data['title'])
f.write(foot)
运行结果:
今天就聊到这里啦,大家记得点赞收藏,分享转发,关注哟!
网友评论