# 如何提升scrapy爬取数据的效率?
推荐: 单线程加异步协程
增加并发: 默认scrapy开启的并发线程为32个,可以适当进行增加。在settings.py中修改
CONCURRENT_REQUESTS = 100 降低日志级别: 在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。
LOG_LEVEL = ‘INFO’ 或 'ERROR'
禁止cookie: 如果不是真的需要cookie,则在scrapy爬取数据时可以进制cookie从而减少CPU的使用率,提升爬取效率。
COOKIES_ENABLED = False 禁止重试: 对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。
RETRY_ENABLED = False 减少下载超时: 如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。DOWNLOAD_TIMEOUT = 10 超时时间为10s
# 配置文件
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' ROBOTSTXT_OBEY = False # 遵守robots协议 CONCURRENT_REQUESTS = 100 #线程 COOKIES_ENABLED = False # cookie处理 LOG_LEVEL = 'ERROR' # 日志等级 RETRY_ENABLED = False # 重试 DOWNLOAD_TIMEOUT = 3 # 超时 DOWNLOAD_DELAY = 3 #下载延迟 一个个 类似sleep
# crawlSpider 是 Spider的一个子类
- crawlSpider全站数据爬取 - crawlSpider就是spider一个子类(派生) - crawlSpider具有的机制: - 链接提取器 LinkExtractors链接提取器 - 规则解析器 根据链接提取器中提取到的链接,根据指定规则提取解析器链接网页中的内容。 - 创建爬虫文件: - 深度爬取:
# 创建一个工程
创建工程 scrapy startproject projectName 切换目录 cd projectName 创建爬虫 scrapy genspider -t crawl spiderName www.xxx.com
此指令对比以前的指令多了 "-t crawl",表示创建的爬虫文件是基于CrawlSpider这个类的,而不再是Spider这个基类
# 爬虫.py # -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class CrawlSpider(CrawlSpider): name = 'c' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] # 链接提取器:可以根据指定规则进行连接的提取 # allow表示的就是提取连接规则:正则 link = LinkExtractor(allow=r'') # 一层层全取得链接
link = LinkExtractor(allow=r'/text/page/d+/') link1 = LinkExtractor(allow=r'/text/') rules = ( #规则解析器:根据指定规则进行响应数据的解析 #follow:将连接提取器继续作用到连接提取器提取出的连接所对应的页面源码中 Rule(link, callback='parse_item', follow=True), Rule(link1, callback='parse_item'), ) #回调函数调用的次数是由连接提取器提取连接个数决定 def parse_item(self, response): print(response)
--------------------------------------------------------
LinkExtractor(
allow=r'Items/',# 满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
deny=xxx, # 满足正则表达式的则不会被提取。
restrict_xpaths=xxx, # 满足xpath表达式的值会被提取
restrict_css=xxx, # 满足css表达式的值会被提取
deny_domains=xxx, # 不会被提取的链接的domains。
)
# 下面是BOSS直聘 页面及详情页的数据获取
# spider.py # -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from bossPro.items import DetailItem,FirstItem # 爬取的是岗位名称(首页)和 岗位描述(详情页) class BossSpider(CrawlSpider): name = 'boss' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.zhipin.com/c101010100/?query=python%E5%BC%80%E5%8F%91&page=1&ka=page-prev'] # 获取所有的页码link link = LinkExtractor(allow=r'page=d+') link_detail = LinkExtractor(allow=r'/job_detail/.*?html') #/job_detail/f2a47b2f40c53bd41XJ93Nm_GVQ~.html #/job_detail/47dc9803e93701581XN80ty7GFI~.html rules = ( Rule(link, callback='parse_item', follow=True), Rule(link_detail, callback='parse_detail'), ) #将页码连接对应的页面数据中的岗位名称进行解析 def parse_item(self, response): # print(response) li_list = response.xpath('//div[@class="job-list"]/ul/li') for li in li_list: item = FirstItem() # 实例化 job_title = li.xpath('.//div[@class="job-title"]/text()').extract_first() item['job_title'] = job_title print(job_title) yield item def parse_detail(self,response): # print(response) job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract() item = DetailItem() # 实例化 job_desc = ''.join(job_desc).strip() item['job_desc'] = job_desc yield item
# items.py # -*- coding: utf-8 -*- import scrapy class DetailItem(scrapy.Item): job_desc = scrapy.Field() class FirstItem(scrapy.Item): job_title = scrapy.Field()
# pipelines.py
# -*- coding: utf-8 -*- class BossproPipeline(object): f1,f2 = None,None def open_spider(self,spider): self.f1 = open('a.txt','w',encoding='utf-8') self.f2 = open('b.txt', 'w', encoding='utf-8') def process_item(self, item, spider): #item在同一时刻只可以接收到某一个指定item对象 if item.__class__.__name__ == 'FirstItem': job_title = item['job_title'] self.f1.write(job_title+' ') else: job_desc = item['job_desc'] self.f2.write(job_desc+' ') return item
# 分布式爬虫实例 -- 东莞阳光网 http://wz.sun0769.com/index.php/question/questionType?type=4&page=
- 分布式爬虫 - 概念:使用多台机器组成一个分布式的机群,在机群中运行同一组程序,进行联合数据的爬取。 - 原生的scrapy是不可以实现分布式: - 原生的scrapy中的调度器不可以被共享 - 原生的scrapy的管道不可以被共享 - 如何实现分布式? 就必须使用scrapy-redis(模块) - 可以给原生的scrapy提供可以被共享的管道和调度器 - pip install scrapy_redis - 搭建流程: - 创建工程 - 爬虫文件 crawlSpider - 修改爬虫文件: - 导包:from scrapy_redis.spiders import RedisCrawlSpider - 将当前爬虫类的父类进行修改 RedisCrawlSpider - allowed_domains,start_url删除,添加一个新属性redis_key(调度器队列的名称) - 数据解析,将解析的数据封装到item中然后向管道提交 - 配置文件的编写: - 指定管道: ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } - 指定调度器: # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True - 指定具体的redis: REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 REDIS_ENCODING = ‘utf-8’ REDIS_PARAMS = {‘password’:’123456’} - 开启redis服务(携带redis的配置文件:redis-server ./redis.windows.conf),和客户端redis-cli: - 对redis的配置文件进行适当的配置: - #bind 127.0.0.1 # 注释host的绑定 - protected-mode no # 关闭保护模式 - 开启 - 启动程序:scrapy runspider xxx.py - 向调度器队列中扔入一个起始的url(redis的客户端):lpush redis_key的值 www.xxx.com(起始url) tips: 机群分布式实测试现的时候,可以修改线程数小点(比如3-5个).
flushdb 命令用于清空当前数据库中的所有 key
# 爬虫文件 # -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis.spiders import RedisCrawlSpider from fbsPro.items import FbsproItem class TestSpider(RedisCrawlSpider): #修改这个 name = 'test' # allowed_domains = ['www.xxx.com'] # start_urls = ['http://www.xxx.com/'] #调度器队列的名称 redis_key = 'dongguan' rules = ( Rule(LinkExtractor(allow=r'type=4&page=d+'), callback='parse_item', follow=True), ) def parse_item(self, response): a_list = response.xpath('//a[@class="news14"]') for a in a_list: item = FbsproItem() #实例化 item['title']= a.xpath('./text()').extract_first() yield item
# items.py
# -*- coding: utf-8 -*-
import scrapy
class FbsproItem(scrapy.Item):
title = scrapy.Field()
# settings.py 里面 ITEM_PIPELINES = { # 'fbsPro.pipelines.FbsproPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400 } # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True # 增量式的实现,爬过的数据就不再爬取了 REDIS_HOST = '192.168.11.154' REDIS_PORT = 6379
# 关于Redis存储时候的一些注意事项
服务端启动 redis-server ./redis.windows.conf
客户端 redis-cli -h '192.168.11.161' -p 密码 (这样可以连接别人的远程redis server端)