scrapy框架基本使用
- 创建工程
- scrapy startproject proName
- 进入工程目录
- cd proName
- 创建爬虫文件
- scrapy genspider spiderName www.xxx.com
- 编写爬虫文件
- 执行工程
- scrapy crawl spiderName
爬虫文件的编写
- 定义好了一个类,该类的父类是Spider,Spider是scrapy所有类的父类
- 类中定义好了三个属性和一个方法
- name:爬虫文件的名称
- start_urls:起始url列表
- 作用:可以对列表中的url进行get请求的发送
- allow_domains:允许的域名
- parse(self, response):
- 将起始URL列表中的URL请求成功后,Response就是获取的响应对象。在该方法中负责实现数据解析。
- Scrapy工程默认是遵守robots协议的,需要在配置文件中进行操作。
- 不遵从robots协议。
- 可以指定日志的等级,这样在输出结果的时候就只会显示出对应等级以上的日志
- LOG_LEVEL = 'WARNING'
数据解析
- response.xpath()
- 注意:提取标签内容时返回的不是字符串。可是Selector对象,字符串是存储在该对象中的。需要调用extract()/getall()或者extract_first()/get()。将Selector对象中的字符串取出。
持久化存储
- 基于终端指令:
- 只可以将parse方法中的返回值存储到指定后缀的文本文件中。
- 通过指定方式执行工程
- scrapy crawl spiderName -o fileName.csv
- 基于管道:
- 实现流程:
- 1.在爬虫文件中解析数据
- 2.在Item类中定义相关属性(解析的数据有几个字段就定义几个属性)
- item就是一个字典
- 3.将在爬虫文件中解析的数据存储封装到Item对象中
- 只可以使用item['name'] = name来访问属性
- 4.将存储了解析数据的Item对象提交给管道
- yield item
- 5.在管道文件中接收Item对象,且对其进行任意形式的持久化存储操作
- process_item(self, item, spider)是用来接收爬虫文件提交过来的item对象(一次只能接收一个item对象)。
- 6.在配置文件中开启管道
- 如何实现数据的备份? -指的是将爬取到的一组数据存储到多个不同的载体(文件、MySQL、Redis)中
- 持久化存储的操作必须要写在管道文件中。
- 一个管道类对应一种形式的持久化存储。
- 如果想将数据存储到多个载体中则必须要有多个管道类。
- 问题:那两个管道类都接收到item,且对其进行持久化存储,爬虫文件提交的item可以同时提交给两个管道类吗?
- 爬虫文件提交的item只可以提交给优先级最高的那一个管道类。
- 如何让优先级低的管道类也可以获取接收到item呢?
- 可以让优先级高的管道类在process_item中通过return item的形式将item传递给下一个即将被执行的管道类。
手动请求发送实现的全栈数据爬取
- 如何通过代码手动对指定的URL进行请求发送。
- yield scrapy.Request(next_url, callback=self.parse):get请求
- 如何手动发起post请求?
- yield scrapy.FormRequest(next_url, formdata, callback=self.parse)
- 注意:在scrapy中一般不发送post请求。
请求传参
- 作用:实现深度爬取
- 深度爬取:爬取的数据没有在同一页面
- 实现:
- yield scrapy.Request(detail_url, callback=self.parse_detail, meta={"item": item})
- 可以将meta字典传递给callback
- callback接收meta:
- meta = response.meta
- item = meta['item]
如何提高scrapy的爬取效率?
- 增加并发:
- 默认scrapy开启的线程为32/16个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100 则并发设置成了100。
- 降低日志级别:
- 在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率,可以设置log输出信息为INFO或者ERROR或者WARNING即可。在配置文件中编写:LOG_LEVEL = 'INFO'
- 禁止cookie:
- 如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie,从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
- 禁止重试:
- 对失败的HTTP进行重新请求(重试),会减慢爬取速度。因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
- 减少下载超时:
- 如果对一个非常慢的链接进行爬取。减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中编写:DOWNLOAD_TIMEOUT = 10 则设置超时时间为十秒。
中间件
- 下载中间件
- 爬虫中间件
- 作用:
- 下载中间件处于引擎和下载器之间,批量拦截请求和响应。
- 拦截请求
- process_request(self, request, spider):每次发起请求请求都会被该方法拦截到。
- 参数request就是拦截到的请求
- 参数spider表示的就是爬虫文件中爬虫实例化好的对象,可以实现中间件和爬虫类之间的数据交互
- 请求头的伪装。
- process_request:
- request.headers['xxxx'] = 'xxx'
- 代理
- process_exception:
- request.meta['proxy'] = 'http://ip:port'
- 拦截响应。
- process_response(self, request, response, spider):只要返回一个响应就会被该方法拦截到。
- 篡改响应数据。
- 拦截异常的请求对象
- process_exception(self, request, exception, spider):只要有异常的请求就会被该方法拦截到。该方法负责对异常的请求进行修正。且使用return request将修正后的请求对象进行重新发送。
CrawlSpider
是Spider的一个子类,之前创建的爬虫文件的爬虫类的父类就是Spider
作用:用于实现全站数据爬取
使用:
- 创建工程
- cd 工程目录
- 创建爬虫文件:
- scrapy genspider -t crawl spiderName www.xxx.com
- 执行工程
链接提取器、规则解析器是crawlspider独有的
链接提取器LinkExtractor
- link = LinkExtractor(allow=r'Items/')
- 实例化一个链接提取器对象
- 作用:根据指定规则(allow)进行链接(url)的提取。
规则解析器Rule
- rules = ( Rule(link, callback='parse_item', follow=True), )
- 实例化一个Rule对象,用的是该对象的构造方法。
- 作用:可以接受链接提取器提取到的链接去对其进行请求发送,然后根据指定形式(callback)进行解析
- follow=True:将每一页码以此作为起始的url,可以捕获到所有页码对应的url
注意:
- 1.一个链接提取器对应一个规则解析器。
- 2.可以有多个链接提取器和规则解析器。
- 3.使用CrawlSpider实现深度爬取的话,最好结合着手动请求发送一起使用。
- 4.如何使用CrawlSpider将一个网站中所有的链接捕获到?
- LinkExtractor(allow=r'') 即可
follow=True:
- 让链接提取器继续作用到(链接提取器)所提取到的链接对应的页面中。
分布式
- 概念:使用多台机器搭建一个分布式机群在分布式机群中共同运行同一组程序,让其对于同一个网络资源进行联合数据爬取。
- 原生的scrapy框架是无法实现分布式?
- 调度器无法被分布式机群共享。
- 管道无法被共享。 -如何实现分布式?
- 使用scrapy结合的scrapy-redis组件实现分布式。
- scrapy-redis组件作用:
- 给scrapy提供可以被共享的管道和调度器。
- pip install scrapy-redis
- 实现流程:
- 1.创建工程。
- 2.cd 工程。
- 3.创建爬虫文件。
- 基于CrawlSpider的爬虫文件
- 4.修改爬虫文件。
- 导包:from scrapy_redis.spiders import RedisCrawlSpider
- 修改当前爬虫类的父类:RedisCrawlSpider
- 删除start_urls,添加一个新属性:
- redis_key = 'xxx',可以被共享的调度器队列的名称
- 基于常规的操作获取URL,发送请求解析数据。
- 5.修改配置文件settings.py
- 增加一个去重容器类的配置。作用,使redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化。
- DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
- 使用scrapy_redis组件自己的调度器。
- SCHEDULER = "scrapy_redis.scheduler.Scheduler"
- 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空ready中请求队列和去重指纹的set。如果是True,则表示要持久化存储。就不清空数据。否则清空数据。
- SCHEDULER_PERSIST = True
- 指定管道:
- ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 }
- 指定redis服务器:
- REDIS_HOST = 'redis服务的ip地址'
- REDIS_PORT = 6379
- 6.对redis的配置文件进行配置(redis.windows.conf)
- 取消默认绑定。
- 将bind 127.0.0.1进行注释
- 关闭保护模式。
- protected-mode yes:将yes改为no
- 7.启动redis的服务器和客户端
- redis服务端的启动:
- 在终端里执行:redis-server.exe redis.windows.conf
- 8.执行分布式程序
- 9.向调度器的队列中扔入一个起始的url:
- 在redis-cli:lpush sunQueue 爬取的url
- redis-cli:
- proName:items命名的数据结构
增量式
- 概念:监测网站数据更新的情况,爬取最新更新出来的数据。
- 核心:去重
- 实现增量:
- 对爬取数据的url进行检测,使用一个记录表存储爬取过得数据的url,但凡记录表中存有的URL,说明URL对应数据爬取过了,否则表示没有爬取过为新数据
- 记录表:
- redis的set集合充当记录表