自己动手实现爬虫scrapy框架思路汇总
这里先简要温习下爬虫实际操作:
cd ~/Desktop/spider scrapy startproject lastspider # 创建爬虫工程
cd lastspider/ # 进入工程 scrapy genspider github github.cn # 创建scrapy爬虫 scrapy genspider -t crawl gitee gitee.com # 创建crawlspider爬虫 # github==========================================
# github.py
# -*- coding: utf-8 -*-
import scrapy class GithubSpider(scrapy.Spider):
name = 'github'
allowed_domains = ['github.cn']
start_urls = ['http://github.cn/'] def parse(self, response):
# 实现逻辑--简单以大分类-中间分类-小分类-商品列表-商品详情-商品价格为例
# 1. 获取大分类的分组
# 2. 获取和大分类呼应的中间分类组
# 3. 遍历提取大分类组和中间分类组的数据,同时获取小分类组
# 4. 遍历小分类组,提取小分类对应的列表页url,发送列表页请求,callback指向列表页处理函数
# 5. 如果当前分类页有翻页,则提取下一页url继续发送请求,callback指向自己,以便循环读取
# 6. 构建列表页数据处理函数,获取列表页商品的分组,遍历提取各商品列表页数据,包括url
# 7. 根据提取的商品url,构建请求,callback指向详情页数据处理函数
# 8. 如果当前商品列表页有翻页,则提取下一页url继续发送请求,callback指向自己,以便循环读取
# 9. 构建商品详情页函数,提取详情页信息,如果其中有数据需要单独发送请求,则再构建请求获取该数据,在最后的请求函数中,需要yield item
# 10. 上述最终yield 的item 通过在settings中设置spiderpiplelines,就会进入指定的spiderpiplelines中通过process_item()进行进一步数据处理,比如清洗和保存。
pass # pipelines.py
class GithubPipeline:
def open_spider(self,spider): #在爬虫开启的时候执行一次
if spider.name == 'github':
# 准备好mongodb数据库及集合,以便接收数据
client = MongoClient() # host='127.0.0.1',port=27017 左侧是默认值
self.collection = client["db"]["col"]
# self.f = open("a.txt","a")
pass def process_item(self, item, spider):
if spider.name == 'github':
# 数据清洗
item['content'] = self.process_content(item["content"])
# 往数据库集合中添加数据
# 如果item是通过item.py中定义的类实例对象,则不能直接存入mongodb,需要dict(item),如果是字典则可以直接存入。
self.collection.insert_one(item)
# pprint(item)
return item def process_content(self,content): #处理content字段的数据
# 对数据进行处理,常用方法-正则匹配,字符串切片,替换等待
return content def close_spdier(self,spider): #爬虫关闭的时候执行一次
if spider.name == 'github':
# self.f.close()
pass #================================================
# 字典推导式
# {keys(i):values(i) for i in list1}
# eg: dict1 = {i.split('=')[0]:i.split('=')[1] for i in str_list} # gitee ===========================================================
# gitee.py
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule class GiteeSpider(CrawlSpider):
name = 'gitee'
allowed_domains = ['gitee.com']
start_urls = ['http://gitee.com/'] rules = (
Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
) def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
return item
# =============================================
爬虫框架
什么是框架,为什么需要开发框架
框架:为了解决一类问题而开发的程序,能够提高开发效率
第三方的框架不能够满足需求,在特定场景下使用,能够满足特定需求
scrapy_plus中有哪些内置对象和核心模块
core
engine
scheduler
downloader
pipeline
spider
http
request
response
middlewares
downloader_middlewares
spider_middlewares
item
scrapy_plus实现引擎的基础逻辑
调用爬虫的start_request方法,获取start_request请求对象
调用爬虫中间件的process_request方法,传入start-request,返回start_request
调用调度器的add_request,传入start_request
调用调度器的get_request方法,获取请求
调用下载器中间件的process_request,传入请求,返回请求
调用下载器的get_response方法,传入请求,返回response
调用下载器中间件的process_response方法,传入response,返回response
调用爬虫中间件的process_response方法,传入response,返回response
调用spider的parse方法,传入resposne,得到结果
调用爬虫中间件的process_request方法,传入request,返回request
判断结果的类型,如果是请求对象,调用调度器的add_request,传入请求对象
否则调用管道的process_item方法,传入结果
如何在项目文件中添加配置文件能够覆盖父类的默认配置
在框架中conf文件夹下,建立default_settings,设置默认配置
在框架的conf文件夹下,建立settings文件,导入default_settings中的配置
在项目的文件夹下,创建settings文件,设置用户配置
在框架的conf文件夹下的settings中,导入settings中的配置,会覆盖框架中的默认配置
url地址补全
urllib.parse.urljoin(完整的url地址demo,不全的url地址)
返回的是补全后的url地址.
提取对象身上的方法,再单独使用.
class Test:
def func(self,param):
print('this is {}'.format(param))
test = Test()
ret = getattr(test,'func')
print(ret)
ret('python') # this is python
python中内置发送请求的方法
import requests
# 发送get请求
req = requests.get('https://www.python.org')
# 或者--上面的实现其实本质上是下面代码
req = requests.request('GET', 'http://httpbin.org/get')
# 发送post请求
payload = dict(key1='value1', key2='value2')
req = requests.post('http://httpbin.org/post', data=payload)
# 或者--上面的实现其实本质上是下面代码
req = requests.request('POST', 'http://httpbin.org/post',data=payload)
# put,options,patch,delete方法同上
框架开发分析:
了解框架,框架思路分析
框架雏形
http模块和item模块(传递的数据)
core模块(五大核心模块)
框架中间件
框架安装
框架运行
框架完善
日志模块使用
import logging
# 日志的五个等级,等级依次递增
# 默认是WARNING等级
logging.DEBUG
logging.INFO
logging.WARNING
logging.ERROR
logging.CRITICAL
# 设置日志等级
logging.basicConfig(level=logging.INFO)
# 使用
logging.debug('DEBUG')
logging.info('INFO')
logging.warning('WARNING')
logging.error('ERROR')
logging.critical('CRITICAL')
# 捕获异常信息到日志
try:
raise Exception('异常')
expect Exception as e:
logging.exception(e)
# 可以对日志输出格式进行自定义
%(name)s Logger的名字
%(asctime)s 字符串形式的当前时间
%(filename)s 调用日志输出函数的模块的文件名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(levelname)s 文本形式的日志级别
%(message)s 用户输出的消息
# 默认日志格式
'%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s'利用logger封装日志模块
# 自行封装了一个Logger类
# 思考在框架中那些地方需要输出日志信息
# 1. 引擎启动时--输出开始时间
# 2. 引擎结束时--输出结束时间,以及总耗时配置文件实现
# 思路
# 1. 先设置个默认配置文件default_settings.py(里面可以包含用户看不到的配置)
# 2. 再在同级下生成settings,把default_Settings中配置全部导入
# 3. 考虑到用户使用在外部要修改配置,所以框架也会在外层生成个settings.py文件
# 4. 如何把用户和默认的同时兼顾呢,就是把用户修改了的覆盖默认的
# 5. 覆盖的方法就是在default_settings的同级目录下创建settings.py,先导入默认配置,再导入用户配置。参考项目的启动顺序,到外部的settings.py文件,直接 from settings import * 即可。实现同时多请求
"""
实现思路:
1.设置start_url为一个列表
2.爬虫组件 遍历该列表,来构造请求,返回生成器对象
3.引擎组件 遍历上述请求生成器,逐一添加到调度器中
4.同时为了避免阻塞,取的过程设置为不等待取,queue.get(block=False),取不到返回none
5.可能得到none,所以引擎中要先判断,如果没有,就直接return
"""实现同时多解析函数
"""
多解析函数就是:实现scrapy请求中的callback
实现思路:
1.在request模块,添加callback,meta两个参数
2.在response中添加meta参数,接收request中的meta
3.在引擎中调用callback指定的函数解析响应
即使用getattr方法,获取爬虫对象上的callback方法,然后来解析响应
parse = getattr(self.spider,request.callback)
"""实现同时多个spider文件执行
"""
多个spider文件,先考虑传列表
首先引擎中修改代码,spiders参数变成列表,遍历该列表,进行各爬虫请求入调度器队列
然后,执行 请求-响应-item,但是考虑上上述多解析函数,解析请求的方法是爬虫中的callback,该过程中,并不知道请求对应的爬虫是谁,只知道爬虫列表。 考虑构建请求对应的爬虫,即绑定,遍历爬虫文件构建请求的构成中,给请求赋值个爬虫索引属性-
request.spider_index = self.spiders.index(spider)
如此可知请求对应的爬虫是,spider = self.spiders[request.spider_index]
上述方法用字典页可以实现。看效率,字典可能好些吧。
"""实现多个管道
"""
修改引擎,让pipeline由外界传入,在爬虫传item到pipelines中处理时,process_item()传入两个参数,item和spider,以此来决定用哪个爬虫
"""实现项目中传入多个中间件
"""
不同的中间件可以实现对请求或者是响应对象进行不同的处理,通过不同的中间件实现不同的功能,让逻辑更加清晰
修改engine让spider_middleware,download_middleware由外界传入,因为传入的都是列表
所以,使用中间件时,遍历使用。
for downloader_mid in self.download_mids:
request = downloader_mid.process_request(request) for spider_mid in self.spider_mids:
start_resquest = spider_mid.process_request(start_resquest)
"""动态模块导入
"""
通过前面的代码编写,我们已经能够完成大部分的任务,但是在main.py 中的代码非常臃肿,对应的我们可以再•settings.py 配置哪些爬虫,管道,中间件需要开启,•能够让整个代码的逻辑更加清晰
利用importlib.import_modle能够传入模块的路径,即可即可实现根据模块的位置的字符串,导入该模块的功能.
1.从conf.settings配置中导入 SPIDERS,PIPELINES,SPIDER_MIDDLEWARE,DOWNLOAD_MIDDLEWARE,
上述分别都是列表
把 self.spiders=spiders 列表替换为,从配置文件中构建的列表
eg:‘spider.kcspider.BaiduSpider’
eg:'pipelines.BaiduPipeline
首先从中切割出模块和类--从右按.切割,左边为模块名,右边为类名
调用方法
self.spiders = _auto_import_instance(SPIDERS,isspider=True)
def _auto_import_instance(path,isspider)
if isspider:
instance={}
else:
instanct=[]
for p in path:
module_name = p.rsplit('.',1)[0]
cls_name = p.rsplit('.',1)[1]
# 动态导入模块
ret = importlib.import_module(module_name)
cls = getattr(module,cls_name)
if isspider:
instance[cls_name]=cls()
else:
instance.append(cls())
return instance
"""请求去重
"""
去重的是爬虫创建的request对象,scrapy_redis中使用的是hash.sha1创建文件指纹的方式。
根据请求的url、请求方法、请求参数、请求体进行唯一标识,进行比对,由于这四个数据加到一起,内容较长,因此使用求指纹的方式来进行去重判断。
指纹计算方法,最常用的就是md5、sha1等hash加密算法,来求指纹
考虑去重的调用
引擎中在添加爬虫组件生成的请求对象入调度器队列之前,会先去重再添加
"""使用线程/协程池
def _callback(self, temp):
'''执行新的请求的回调函数,实现循环'''
if self.running is True: # 如果还没满足退出条件,那么继续添加新任务,否则不继续添加,终止回调函数,达到退出循环的目的
self.pool.apply_async(self._execute_request_response_item, callback=self._callback)
def _start_engine(self):
'''依次调用其他组件对外提供的接口,实现整个框架的运作(驱动)'''
self.running = True # 启动引擎,设置状态为True
# 向调度器添加初始请求
self.pool.apply_async(self._start_requests) # 使用异步
self.pool.apply_async(self._execute_request_response_item, callback=self._callback) # 利用回调实现循环
# ===========================================
# 协程
# scrapy_plus/async/coroutine.py
'''
由于gevent的Pool的没有close方法,也没有异常回调参数
引出需要对gevent的Pool进行一些处理,实现与线程池一样接口,实现线程和协程的无缝转换
'''
from gevent.pool import Pool as BasePool
import gevent.monkey
gevent.monkey.patch_all() # 打补丁,替换内置的模块
class Pool(BasePool):
'''协程池
使得具有close方法
使得apply_async方法具有和线程池一样的接口
'''
def apply_async(self, func, args=None, kwds=None, callback=None, error_callback=None):
return super().apply_async(func, args=args, kwds=kwds, callback=callback)
def close(self):
'''什么都不需要执行'''
pass
框架升级
分布式爬虫,scrapy-redis
"""
利用redis实现队列
注意pickle模块的使用:如果将对象存入redis中,需要先将其序列化为二进制数据,取出后反序列化就可以再得到原始对象
接口定义一致性:利用redis使用一个Queue,使其接口同python的内置队列接口一致,可以实现无缝转换
redis -- 存储指纹和待抓取的请求对象
mysql -- 数据存储
"""增量爬虫
"""
增量抓取,意即针对某个站点的数据抓取,当网站的新增数据或者该站点的数据发生了变化后,自动地抓取它新增的或者变化后的数据
设计原理:
1.定时向目标站点发起请求
2.关闭对目标站点请求的去重判断
3.对抓取来的数据,入库前进行数据判断,只存储新增或改变的数据
"""断点续爬
"""
断点续爬的效果:爬虫程序中止后,再次启动,对已发送的请求不再发起,而是直接从之前的队列中获取请求继续执行。
意味着要实现以下两点:
1.去重标识(历史请求的指纹)持久化存储,使得新的请求可以和之前的请求进行去重对比
2.请求队列的持久化
之前的分布式实现了上述两点,但可能会出现问题:
1.如果其中部分或全部执行体被手动关闭或异常中止,那么这不未被正常执行的请求体,就会丢失,因为请求后,队列中就删除了该请求。
解决办法:创建请求备份容器,当请求成功后,再把该请求从容器中删除,而不是队列中的,pop删除取出请求。这样的话,当队列中请求全部执行完毕后,备份容器中的请求就是丢失的请求,接下来只需要把它们重新放回请求队列中重新执行就好了。
2.如果某个请求无论如何都无法执行成功,那么这里可能造成死循环。
解决办法:考虑给request请求对象设置‘重试次数’属性。
a1.每次从队列中弹出一个请求时,就把它在备份容器中对应的‘重试次数’+1
a2. 每次从队列中弹出一个请求后,先判断它的'重试次数'是否超过配置的'最大重试次数',如果超过,就不再处理该对象,把它记录到日志中,同时从备份容器中删除该请求。否则就继续执行。
"""
框架项目实战
自己动手实现爬虫scrapy框架思路汇总的更多相关文章
- python爬虫scrapy框架——人工识别登录知乎倒立文字验证码和数字英文验证码(2)
操作环境:python3 在上一文中python爬虫scrapy框架--人工识别知乎登录知乎倒立文字验证码和数字英文验证码(1)我们已经介绍了用Requests库来登录知乎,本文如果看不懂可以先看之前 ...
- 爬虫scrapy框架之CrawlSpider
爬虫scrapy框架之CrawlSpider 引入 提问:如果想要通过爬虫程序去爬取全站数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模 ...
- 安装爬虫 scrapy 框架前提条件
安装爬虫 scrapy 框架前提条件 (不然 会 报错) pip install pypiwin32
- 爬虫Ⅱ:scrapy框架
爬虫Ⅱ:scrapy框架 step5: Scrapy框架初识 Scrapy框架的使用 pySpider 什么是框架: 就是一个具有很强通用性且集成了很多功能的项目模板(可以被应用在各种需求中) scr ...
- Python爬虫Scrapy框架入门(2)
本文是跟着大神博客,尝试从网站上爬一堆东西,一堆你懂得的东西 附上原创链接: http://www.cnblogs.com/qiyeboy/p/5428240.html 基本思路是,查看网页元素,填写 ...
- python爬虫scrapy框架
Scrapy 框架 关注公众号"轻松学编程"了解更多. 一.简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. 框架的力量 ...
- 爬虫Scrapy框架运用----房天下二手房数据采集
在许多电商和互联网金融的公司为了更好地服务用户,他们需要爬虫工程师对用户的行为数据进行搜集.分析和整合,为人们的行为选择提供更多的参考依据,去服务于人们的行为方式,甚至影响人们的生活方式.我们的scr ...
- 爬虫--Scrapy框架课程介绍
Scrapy框架课程介绍: 框架的简介和基础使用 持久化存储 代理和cookie 日志等级和请求传参 CrawlSpider 基于redis的分布式爬虫 一scrapy框架的简介和基础使用 a) ...
- 爬虫--Scrapy框架的基本使用
流程框架 安装Scrapy: (1)在pycharm里直接就可以进行安装Scrapy (2)若在conda里安装scrapy,需要进入cmd里输入指令conda install scrapy ...
随机推荐
- 设置同一个域名同一个源通过cdn用不同的端口访问网站设置
下图例子是设置80和88访问,因为80是默认的访问,所以只要设置88就行 进入站点管理-->应用防火墙-->高级设置 这个设置用到了url和host模块 在站点设置里设置要用到的端口:
- FOB cost---从工厂到码头的费用
1.主要是港杂费:陆运400元起,2个方450元,三个方500元,3个方以上按100元/方算起.
- 算法练习LeetCode初级算法之排序和搜索
合并两个有序数组 class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { System.arrayco ...
- [剑指Offer]5-替换空格
链接 https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPa ...
- c#: WebBrowser控制台输出
还是处理视频下载所相关的问题. 有些网站,它的页面代码是由页面加载后js动态生成,那么其原始的html便不能用.页面渲染后的代码,是我们需要的 c#中,我用WebBrowser这个控件处理.设置项目类 ...
- 【mac环境】终端配色 & 配置使用ll命令
1.MAC OS X 命令终端的颜色显示 打开 terminal 会发现 ls 和 grep 后的结果是没有色彩的,这时候可以这么干: 用 vim 打开文件 ~/.bash_profile,然后把下边 ...
- 学习Hibernate的体会
这个学期老师让我们做一个系统(服务器和客户端),语言自选,我也随大家开始学习java web 和android . 下面是我自学的一些体会和遇到的问题. 关于jar包. jsds.jar javasi ...
- Linux 下编译 有多个子程序文件的Fortran程序
第一种方法 ifort -o outprogram Source1.f90 Source2.f90 第二种 在主程序中include 'Source2.f90' program main call p ...
- zeromq学习记录(九)练习代码学习ZMQ_ROUTER ZMQ_READLER
/************************************************************** 技术博客 http://www.cnblogs.com/itdef/ ...
- IoGetRelatedDeviceObject学习
PDEVICE_OBJECT IoGetRelatedDeviceObject( IN PFILE_OBJECT FileObject ) /*++ Routine Description: This ...