概述
一个完整的爬虫,一般由以下5个组件构成:
1.URL管理器
负责维护待爬取URL队列和已爬取URL队列,必须拥有去重功能。
2.HTML下载器
负责根据调度器从URL管理器中取出的url,下载html页面数据
3.HTML解析器
负责解析HTML下载器下载的网页数据,从中提取新的url和目标数据,并将其返回
4.数据存储器
负责将HTML下载器返回的的数据保存到本地或数据库中
5.调度器
爬虫的核心组件。根据业务流程,调用其它组件完成数据抓取。
一般情况下,爬虫的入口URL在这里提供。
源码
首先,项目结构如下,每个组件使用一个模块文件
1.URL管理器
# !/usr/bin/env python
# -*- coding:utf-8 -*-
class UrlManager(object):
"""URL管理器"""
def __init__(self):
# 待爬去url集合
self.new_urls=set()
# 已爬取url集合
self.old_urls=set()
def new_urls_size(self):
"""获取新的url数量"""
return len(self.new_urls)
def old_urls_size(self):
"""获取已爬取url数量"""
return len(self.old_urls)
def has_new_url(self):
"""判断是否有新的url"""
return self.new_urls_size()
def get_new_url(self):
"""从待爬取url集合中获取一个url"""
# 从未爬取url集合中取出并移除一个url
new_url=self.new_urls.pop()
# 将取出的url添加到已爬取url集合中
self.old_urls.add(new_url)
return new_url
def add_new_url(self,url):
"""添加一个新的url到待爬取url集合中"""
if url:
self.new_urls.add(url)
def add_new_urls(self,urls):
"""添加多个个新的url到待爬取url集合中"""
if urls:
for url in urls:
self.add_new_url(url)
2.HTM下载器
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import requests
class HtmlDownloader(object):
"""HTML下载器"""
def download(self,url):
if not url:
return None
headers={
"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36",
}
resp=requests.get(url,headers=headers,timeout=5)
if resp.status_code==requests.codes.ok:
resp.encoding='utf-8'
return resp.text
3.HTML解析器
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import urllib.parse
from lxml import etree
class HtmlParser(object):
"""HTML解析器"""
def parse(self,url,html):
html_et=etree.HTML(html)
new_urls=self.__get_new_urls(url,html_et)
new_data=self.__get_new_data(html_et)
return new_urls,new_data
def __get_new_urls(self,url,html_et):
"""提取当前词条页面下所有相关链接"""
urls=[]
link_list=html_et.xpath('//div[@class="main-content"]//a/@href')
for link in link_list:
if link.startswith('/item/'):
urls.append(urllib.parse.urljoin(base=url,url=link))
return urls
def __get_new_data(self,html_et):
"""提取当前词条的摘要"""
summary=[]
text_list=html_et.xpath('//div[@class="lemma-summary"]//text()')
for text in text_list:
summary.append(text+'\n')
return ''.join(summary)
4.数据存储器
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import codecs
class DataStore(object):
"""数据存储器"""
def store(self,data):
"""将数据保存到本地文本文件中"""
path='../../data'
if not os.path.exists(path):
os.makedirs(path)
with codecs.open(filename=os.path.join(path,'python百科词条.txt'),mode='a',encoding='utf-8') as f:
f.write(data+'\n')
5.调度器
# !/usr/bin/env python
# -*- coding:utf-8 -*-
"""
爬虫调度器
"""
import requests
import time
from urlmanager import UrlManager
from htmldownloader import HtmlDownloader
from htmlpaser import HtmlParser
from datastore import DataStore
class Scheduler(object):
def __init__(self):
"""初始化各组件"""
self.urlmanager=UrlManager()
self.htmldownloader=HtmlDownloader()
self.htmlparser=HtmlParser()
self.datastore=DataStore()
def crawl(self,start_url):
try:
# 添加起始url
self.urlmanager.add_new_url(start_url)
# 最多只爬取500个词条数据
while self.urlmanager.old_urls_size()<500:
time.sleep(0.5)
# 从待爬取url集合中提取一条url
url=self.urlmanager.get_new_url()
if url:
# 使用HTML下载器下载网页
html=self.htmldownloader.download(url)
# 使用HTML解析器解析网页,提取url和摘要信息
new_urls,new_data=self.htmlparser.parse(url,html)
# 将新提取出的url添加到待爬取url集合中
self.urlmanager.add_new_urls(new_urls)
# 将提取出的摘要文本保存到本地文件中
self.datastore.store(new_data)
print('已爬取{}个词条'.format(self.urlmanager.old_urls_size()))
else:
print("爬取完成")
except requests.RequestException as e:
print('爬取失败',e)
if __name__ == '__main__':
start_url='https://baike.baidu.com/item/Python/407313'
scheduler=Scheduler()
scheduler.crawl(start_url)
运行结果
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)