您现在的位置: 365建站网 > 365学习 > (Python)基础爬虫架构及运行流程

(Python)基础爬虫架构及运行流程

文章来源:365jz.com     点击数:354    更新时间:2018-01-22 10:55   参与评论

基础爬虫架构及运行流程

首先讲解一下基础爬虫的架构,如图6-2所示。介绍基础爬虫包含哪些模块,各个模块之间的关系是什么。


图6-2 基础爬虫框架

基础爬虫框架主要包括五大模块,分别为爬虫调度器、URL管理器、HTML下载器、HTML解析器、数据存储器。功能分析如下:

·爬虫调度器主要负责统筹其他四个模块的协调工作。

·URL管理器负责管理URL链接,维护已经爬取的URL集合和未爬取的URL集合,提供获取新URL链接的接口。

·HTML下载器用于从URL管理器中获取未爬取的URL链接并下载HTML网页。

·HTML解析器用于从HTML下载器中获取已经下载的HTML网页,并从中解析出新的URL链接交给URL管理器,解析出有效数据交给数据存储器。

·数据存储器用于将HTML解析器解析出来的数据通过文件或者数据库的形式存储起来。

下面通过图6-3展示一下爬虫框架的动态运行流程,方便大家理解。

图6-3 运行流程



 

6.2 URL管理器

URL管理器主要包括两个变量,一个是已爬取URL的集合,另一个是未爬取URL的集合。采用Python中的set类型,主要是使用set的去重复功能,防止链接重复爬取,因为爬取链接重复时容易造成死循环。链接去重复在Python爬虫开发中是必备的功能,解决方案主要有三种:1)内存去重2)关系数据库去重3)缓存数据库去重。大型成熟的爬虫基本上采用缓存数据库的去重方案,尽可能避免内存大小的限制,又比关系型数据库去重性能高很多。由于基础爬虫的爬取数量较小,因此我们使用Python中set这个内存去重方式。

URL管理器除了具有两个URL集合,还需要提供以下接口,用于配合其他模块使用,接口如下:

·判断是否有待取的URL,方法定义为has_new_url()。

·添加新的URL到未爬取集合中,方法定义为add_new_url(url),add_new_urls(urls)。

·获取一个未爬取的URL,方法定义为get_new_url()。

·获取未爬取URL集合的大小,方法定义为new_url_size()。

·获取已经爬取的URL集合的大小,方法定义为old_url_size()。

程序URLManager.py的完整代码如下:

# coding:utf-8 class UrlManager(object):
def __init__(self):
self.new_urls = set()# 未爬取URL集合

self.old_urls = set()# 已爬取URL集合

def has_new_url(self):
'''
判断是否有未爬取的URL :return:
'''
return self.new_url_size()!=0

def get_new_url(self):
'''
获取一个未爬取的URL :return:
'''
new_url = self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
def add_new_url(self,url):
'''
将新的URL添加到未爬取的URL集合中

:param url:单个URL :return:
'''
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
def add_new_urls(self,urls):
'''
将新的URL添加到未爬取的URL集合中

:param urls:url集合

:return:
'''

if urls is None or len(urls)==0:
return
for url in urls:
self.add_new_url(url)
def new_url_size(self):
'''
获取未爬取URL集合的大小

:return:
'''
return len(self.new_urls)
def old_url_size(self):
'''
获取已经爬取URL集合的大小

:return:
'''
return len(self.old_urls)

6.3 HTML下载器

HTML下载器用来下载网页,这时候需要注意网页的编码,以保证下载的网页没有乱码。下载器需要用到Requests模块,里面只需要实现一个接口即可:download(url)。程序HtmlDownloader.py代码如下:

# coding:utf-8

import requests
class HtmlDownloader(object):
def download(self,url):
if url is None:
return None
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers={'User-Agent':user_agent} r = requests.get(url,headers=headers)
if r.status_code==200:
r.encoding='utf-8'
return r.text
return None

6.4 HTML解析器

HTML解析器使用BeautifulSoup4进行HTML解析。需要解析的部分主要分为提取相关词条页面的URL和提取当前词条的标题和摘要信息。

先使用Firebug查看一下标题和摘要所在的结构位置,如图6-4所示。


图6-4 HTML结构位置

从上图可以看到标题的标记位于<dd class=“lemmaWgt-lemmaTitle-title”><h1></h1>,摘要文本位于<div class=“lemma-summary”label-module=“lemmaSummary”>。

最后分析一下需要抽取的URL的格式。相关词条的URL格式类似于<a target=“_blank”href=“/view/7833.htm”>万维网</a>这种形式,提取出a标记中的href属性即可,从格式中可以看到href属性值是

一个相对网址,可以使用urlparse.urljoin函数将当前网址和相对网址拼接成完整的URL路径。

HTML解析器主要提供一个parser对外接口,输入参数为当前页面的URL和HTML下载器返回的网页内容。解析器HtmlParser.py程序的代码如下:

# coding:utf-8 import re
import urlparse
from bs4 import BeautifulSoup
class HtmlParser(object):
def parser(self,page_url,html_cont):
'''

用于解析网页内容,抽取URL和数据

:param page_url: 下载页面的URL :param html_cont: 下载的网页内容

:return:返回URL和数据

'''
if page_url is None or html_cont is None:
return
soup = BeautifulSoup(html_cont,'html.parser',from_encoding='utf-8')
new_urls = self._get_new_urls(page_url,soup)
new_data = self._get_new_data(page_url,soup)

return new_urls,new_data
def _get_new_urls(self,page_url,soup):
'''
抽取新的URL集合

:param page_url: 下载页面的URL :param soup:soup
:return: 返回新的URL集合

'''
new_urls = set()
# 抽取符合要求的a标记

links = soup.find_all('a',href=re.compile(r'/view/\d+\.htm'))
for link in links:
# 提取href属性

new_url = link['href'] # 拼接成完整网址

new_full_url = urlparse.urljoin(page_url,new_url)
new_urls.add(new_full_url)
return new_urls
def _get_new_data(self,page_url,soup):
'''
抽取有效数据

:param page_url:下载页面的URL :param soup:
:return:返回有效数据

'''

data={} data['url']=page_url
title = soup.find('dd',class_='lemmaWgt-lemmaTitle-title').find('h1')
data['title']=title.get_text()
summary = soup.find('div',class_='lemma-summary')
# 获取tag中包含的所有文本内容,包括子孙tag中的内容,

并将结果作为Unicode字符串返回

data['summary']=summary.get_text()
return data

6.5 数据存储器

数据存储器主要包括两个方法:store_data(data)用于将解析出来的数据存储到内存中,output_html()用于将存储的数据输出为指定的文件格式,我们使用的是将数据输出为HTML格式。

DataOutput.py程序如下:

# coding:utf-8 import codecs
class DataOutput(object):
def __init__(self):
self.datas=[] def store_data(self,data):
if data is None:
return

self.datas.append(data)
def output_html(self):
fout=codecs.open('baike.html','w',encoding='utf-8')
fout.write("<html>")
fout.write("<body>")
fout.write("<table>")
for data in self.datas:
fout.write("<tr>")
fout.write("<td>%s</td>"%data['url'])
fout.write("<td>%s</td>"%data['title'])
fout.write("<td>%s</td>"%data['summary'])
fout.write("</tr>")
self.datas.remove(data)
fout.write("</table>")
fout.write("</body>")
fout.write("</html>")
fout.close()

其实上面的代码并不是很好的方式,更好的做法应该是将数据分批存储到文件,而不是将所有数据存储到内存,一次性写入文件容易使系统出现异常,造成数据丢失。但是由于我们只需要100条数据,速度很快,所以这种方式还是可行的。如果数据很多,还是采取分批存储的办法。

6.6 爬虫调度器

以上几节已经对URL管理器、HTML下载器、HTML解析器和数据存储器等模块进行了实现,接下来编写爬虫调度器以协调管理这些模块。爬虫调度器首先要做的是初始化各个模块,然后通过

crawl(root_url)方法传入入口URL,方法内部实现按照运行流程控制各个模块的工作。爬虫调度器SpiderMan.py的程序如下:

# coding:utf-8 from firstSpider.DataOutput import DataOutput
from firstSpider.HtmlDownloader import HtmlDownloader
from firstSpider.HtmlParser import HtmlParser
from firstSpider.UrlManager import UrlManager
class SpiderMan(object):
def __init__(self):
self.manager = UrlManager()
self.downloader = HtmlDownloader()
self.parser = HtmlParser()
self.output = DataOutput()
def crawl(self,root_url):
# 添加入口URL self.manager.add_new_url(root_url)
# 判断url管理器中是否有新的url,同时判断抓取了多少个url
while(self.manager.has_new_url() and self.manager.old_url_size()<100):
try:
# 从URL管理器获取新的url
new_url = self.manager.get_new_url()
# HTML下载器下载网页

html = self.downloader.download(new_url)
# HTML解析器抽取网页数据

new_urls,data = self.parser.parser(new_url,html)
# 将抽取的url添加到URL管理器中

self.manager.add_new_urls(new_urls)
# 数据存储器存储文件

self.output.store_data(data)
print "已经抓取%s个链接"%self.manager.old_url_size()
except Exception,e:
print "crawl failed"
# 数据存储器将文件输出成指定格式

self.output.output_html()
if __name__=="__main__":
spider_man = SpiderMan()
spider_man.crawl("http://baike.baidu.com/view/284853.htm")

到这里基础爬虫架构所需的模块都已经完成,启动程序,大约1分钟左右,数据都被存储为baike.html。使用浏览器打开,效果如图

6-5所示。


图6-5 baike.html


小结

本章介绍了基础爬虫架构的五个模块,无论大型还是小型爬虫都不会脱离这五个模块,希望大家对整个运行流程有清晰的认识。

 

如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛


发表评论 (354人查看0条评论)
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
用户名: 验证码: 点击我更换图片
最新评论
------分隔线----------------------------