实战项目:基础爬虫 -- 爬取百度100词条

基础爬虫架构及运行流程

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

  • 爬虫调度器主要负责统筹其他四个模块的协调工作。
  • URL管理器负责管理URL链接,维护已经爬取的URL集合和未爬取的URL集合,提供获取新URL链接的接口。
  • HTML下载器用于从URL管理器中获取未爬取的URL链接并下载HTML网页。
  • HTML解析器用于从HTML下载器中获取已经下载的HTML网页,并从中解析出新的URL链接交给URL管理器,解析出有效数据交给数据存储器。
  • 数据存储器用于将HTML解析器解析出来的数据通过文件或者数据库的形式存储起来。

在这里插入图片描述

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):
		'''
		:return:
		'''
		return len(self.new_urls)
		
	def old_url_size(self):
		'''
		获取已经爬取URL集合的大小
		:return:
		'''
		return len(self.old_urls())

HTML下载器

HTML下载器用来下载网页,这是很需要注意网页的编码,以保证下载的网页没有乱码。下载器需要用到Requests模块,里面只需要实现一个接口即可:download(url)。程序HtmlDownloaders.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

HTML解析器

HTML解析器使用BeautifulSoup4进行HTML解析。需要解析的部分主要分为提取相关词条页面的URL和提取当前词条的标题和摘要信息。
查看百度百科页的源码,可以看到标题的标记位于< 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: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

数据存储器

数据存储器主要包括两个方法: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()

爬虫调度器

以上对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")
百度百科约500万条词条,可用于语义关联或简单的知识网络建立,xml格式,解压后638M,请不要用记事本直接打开,我是用UE打开的,sublime好像会有乱码,文件内容示例如下: =1 <TITLE>=百度百科 =百度百度百科,网络,百科全书,百科词典 =百度知道;贴吧;百度空间;百度地图;百度新闻搜索;百度图片;百度mp3;百度Hi;百度工具栏;百度影视;百度邮编搜索;百度黄页搜索;百度杀毒;百度语音搜索;科利尔百科全书;苏联大百科全书;汇吃百科;维基百科;百度视频;百度蜘蛛;百度文库;互动百科;百度文档;百度文化共享搜索;百度盲道;百度会员;百度搜藏;百度TV;百科全书;知识掌门人;百度游戏;百度有啊;张震;科友; =http://baike.baidu.com/view/1.htm =2 <TITLE>=词条 =百度百度百科 =相关词条; =http://baike.baidu.com/view/2.htm =3 <TITLE>=编辑实验 =百度百度百科,帮助,词条,编辑 =词条;百度; =http://baike.baidu.com/view/3.htm =4 <TITLE>=馒头 =食品,饮食,面食,食谱,蒸馍 =大饼;油条;面包;饺子;花卷;包子;麻花;食品;主食;糯米饭;蛋糕;鲜肉包;米粥;面条;凉拌菜;年糕;饼干;窝头;粽子;烤饼;酥饼;汤圆;烧饼; =http://baike.baidu.com/view/4.htm =6 <TITLE>=雁荡山 =地理,旅游,旅游景点,自然景观,地质公园 =华山;泰山;普陀山;恒山;嵩山;莫干山;浣江;衡山;括苍山;双龙洞;雪窦山;天台山;天目山;楠溪江;天柱山;景宁香炉山;乌镇;杭州西湖;泽雅;白水洋;武夷山;洞宫山;桃花岛;三清山;黄山;新安江水库;崂山区;溪口;太姥山;南麂列岛;野鹤湫;庐山;江心屿;瑶溪;朱家尖;石桅岩;绍兴县;杨家溪;仙岩山;仙都风景区;大龙湫;三折瀑;五岳; =http://baike.baidu.com/view/6.htm =7 <TITLE>=灵峰 =风景,雁荡山,地理,旅游,温州 = =http://baike.baidu.com/view/7.htm
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值