轻量级爬虫实例——爬取百度百科1000个页面的数据
爬虫程序baike_spider分为5个模块:
——爬虫总调度程序 (spider_main)
——url管理器 (url_manager)
——网页下载器 (html_downloader)
——网页解析器 ( html_parser)
——将数据处理好的数据写出到 html 的页面 (html_outputer)
爬虫总调度程序(spider_mian):
from baike_spider import url_manager,html_parser,html_downloader,html_outputer #总调度程序导入其余模块
class SpiderMain(object): #创建SpiderMain爬虫类
def __init__(self): #初始化模块对象
self.urls = url_manager.UrlManager() #urlg管理器对象
self.downloader = html_downloader.HtmlDownloader() #网页下载器对象
self.parser = html_parser.HtmlParser() #网页解析对象
self.outputer = html_outputer.HtmlOutputer() #网页输出对象
def crawling(self,root_url): #通过入口页爬取相关词条数据
count = 1 #爬取页面数量统计量
self.urls.add_new_url(root_url) #向待爬取url集合添加入口url
while self,urls.has_new_url(): #循环爬取,条件:待爬取url集合非空
try: # 可能出现解析出的url无法打开或者内容丢失,进行异常处理
new_url= self.urls.get_new_url() #从待爬取的url集合中取出一个url
print("crawling %d: %s"%(count,new_url) #打印爬取进度状态信息
html_cont = self.downloader.download(new_url) #下载url的html文本
new_urls,new_data = self.parser.parse(new_url,html_cont) #解析入口页html文本得出入口页中所有相关url存入new_urls变量
self.urls.add_new_urls(new_urls) #new_urls变量中的url加入到待爬取集合中
self.outputer.collect_data(new_data) #收集标题、简介数据存入列表
if count ==1000: #统计达到 1000退出循环
break
count+=1 #循环一次自加1
except:
print("crawling failed") #爬取失败则打印出错信息crawling failed
self.outputer.output_html() #将url 标题 简介 输出为html文件
if __name = "__mian__":
root_url = "https://baike.baidu.com/item/Python/407313" #入口页
obj_spider = SpiderMain() #创建爬虫对象
obj_spider.crawling(root_url) #爬取并输出数据
URL管理器模块(url_manager):
class UrlManager(object): #创建UrlManager类
def __init__(self): #初始化待爬取集合和已爬取集合
self.new_urls = set() #待爬取集合
self.old_urls = set() #已爬取集合
def add_new_url(self,url): #添加新url方法
if url is None: #url为空,返回
return
if url not in old_urls and url not in new_urls: #url都不在待爬取和已爬取集合中,进行添加
self.new_urls.add(url) #集合添加为内置add方法
def add_new_urls(self,urls): #添加多个url
if urls is None or len(urls) ==0: #urls为空或者urls的长度为0
return
for url in urls: #依次添加urls中的url进待爬取集合
self.add_new_url(url)
def has_new_url(self): #待爬取集合非空 返回True
return len(self.new_urls) != 0
def get_new_urls(self): #取出待爬取集合中的一个url
new_url = self.new_urls.pop() #取出
self.old_urls.add(new_url) #同时放入已爬取集合
return new_url #返回取出的url
网页下载器 (html_downloader)
from urllib2 import request #导入urllib2包的request模块
class HtmlDownloader(objct): #创建HtmlDownloader类
def download(self,url): #url非空则下载其对应的html文本
if url is None:
return None
request1= request.Request(url) #初始化request对象
request1.add_header("user-agent", "Mozilla/5.0") #添加请求头,模拟浏览器
response = request.urlopen(request1) #打开url对应的html
if response1.getcode != 200: #状态为 200,访问成功
return None
return respone.read() #返回html文本
网页解析器 ( html_parser)
from bs4 import BeautifooulSoup #导入模块
from urllib.parse import urlparse
import re #urlparse是url解析器
class HtmlParser(object): #创建HtmlParser类
def _get_new_urls(self, page_url, soup): #解析html文本获取urls的方法
in_page_urls = set()
links = soup.find_all('a', href=re.compile(r"/item/\S+/\d+")) #找到所有满足的url
for link in links:
new_url = link['href']
new_full_url = urljoin(page_url, new_url) #urljoin可以将基地址与相对地址形成一个绝对地址
in_page_urls.add(new_full_url)
return in_page_urls
def _get_new_data(self, page_url, soup): #解析url对应的标题、简介数据
res_data = {} #数据字典
res_data['url'] = page_url #键url
title_node = soup.find('dd', class_="lemmaWgt-lemmaTitle-title").find("h1") #找出标题
res_data['title'] = title_node.get_text() #键title
summary_node = soup.find('div', class_="lemma-summary") #找出简介
res_data['summary'] = summary_node.get_text() #键summary
return res_data #返回url对应的字典
def parse(self, page_url, html_cont): #url为非空或者html为非空时解析
if page_url is None or html_cont is None:
return
soup = BeautifulSoup(html_cont, 'html.parse', from_encoding='utf-8') #解析html文本
new_urls = self._get_new_urls(page_url, soup) #得出html中的urls
new_data = self._get_new_data(page_url, soup) #得出html中的标题和简介
return new_urls, new_data #返回html中的urls和输出数据字典
网页输出(html_outputer):
class HtmlOutputer(object): #创建HtmlOutputer类
def __init__(self):
self.datas=[] #列表
#收集数据
def collect_data(self,data): #数据非空放入列表
if data is None:
return
self.datas.append(data)
def output_html(self):
fout=open('output.html','w',encoding='utf-8')
#输出到output.html中,w为写模式,编码为utf-8
fout.write("<html>")
fout.write("<body>")
fout.write("<table>")
#ASCI
for data in self.datas: #列表分条写入fout这个html文档
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>")
fout.write("</table>")
fout.write("</body>")
fout.write("</html>")
fout.close() #关闭fout文件