Python3 开发轻量级爬虫教程!

爬虫又被称为网页蜘蛛,网络机器人。是一种按照一定的规则,自动地抓取互联网上信息的程序或者脚本。

一、爬虫算法

在写爬虫时候有两种常用的算法可使用,即深度优先算法、广度优先算法。

深度优先算法

进群: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 获取到的内容进行分析
  • 输出器:将分析完成后的数据进行输出、存储、利用等

Python3 开发轻量级爬虫教程!

爬虫逻辑可分解为如下几个部分:

  1. 查询管理器中是否有待爬取的 URL
  2. 调度器从管理器中获取一个待爬取 URL
  3. 将获取到的 URL 交给下载器处理
  4. 将下载器获取到的网页数据交给解析器处理
  5. 将解析器处理后的 URL 集合交给管理器
  6. 将解析器处理后的数据交给输出器
  7. 重复上述步骤

三、开发轻量级爬虫

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 = '''
 <html>
 <meta charset="UTF-8">
 <body>
 <table>
 '''
 foot = '''
 </table>
 </body>
 </html>
 '''
 with open('CSDN.html', 'w') as f:
 f.write(head)
 for data in self.datas:
 f.write('<tr><td><a href =%s>%s</a></td>' % (data['url'], data['url']))
 f.write('<td>%s</td></tr>' % data['title'])
 f.write(foot)

运行结果:

Python3 开发轻量级爬虫教程!

Python3 开发轻量级爬虫教程!

今天就聊到这里啦,大家记得点赞收藏,分享转发,关注哟!

猜你喜欢

转载自blog.csdn.net/qq_42156420/article/details/82908405
今日推荐