爬虫 7 scrapy的图片存储 crawlspider深度爬取 分布式 增量氏
- 图片懒加载
- 应用到标签的伪属性,数据捕获的时候一定是基于伪属性进行!!!
就比如本来是<img src='xxx',,,, 由于懒加载机制 变成了 <img src2='xxx'... 爬取src数据 就需要变成爬取src2数据啦~~~!!!
图片存储到本地 -----ImagesPipeline
专门用作于二进制数据下载和持久化存储的管道类
1、获取网页的img地址src
import scrapy from imgPro.items import ImgproItem class ImgSpider(scrapy.Spider): name = 'img' # allowed_domains = ['www.xxx.com'] start_urls = ['http://sc.chinaz.com/tupian/meinvtupian.html'] def parse(self, response): div_list = response.xpath('//*[@id="container"]/div') for div in div_list: img_src = div.xpath('./div / a / img/@src2').extract_first() print(img_src) item = ImgproItem() item['img_src'] = img_src yield item
2 声明item
# Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class ImgproItem(scrapy.Item): # define the fields for your item here like: img_src = scrapy.Field()
3重写管道
import scrapy from scrapy.pipelines.images import ImagesPipeline class ImgproPipeline(ImagesPipeline): def get_media_requests(self, item, info): # 是用来对媒体资源进行请求(数据下载),参数item就是接收到的爬虫类提交item对象 yield scrapy.Request(item['img_src']) # 指明输数据存储的路径 def file_path(self, request, response=None, info=None): return request.url.split('/')[-1] # 将item传递给下一个即将执行的管道类 def item_completed(self,results,item,info): return item
4、setting 存储文件的地址配置
IMAGES_STORE = './imgLibs'
- CrawlSpider
- 一种基于scrapy进行全站数据爬取的一种新的技术手段。
- CrawlSpider就是Spider的一个子类
- 连接提取器:LinkExtractor
- 规则解析器:Rule
- 使用流程:
- 新建一个工程
- cd 工程中
- 新建一个爬虫文件:scrapy genspider -t crawl spiderName www.xxx.com (区别在此!!)
这个网站会封IP T T
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.cox.com'] start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1'] # 实例化一个连接提取器 # 作用:根据指定规则(allow=r'正则表达式')进行指定连接的提取 link = LinkExtractor(allow=r'id=1&page=\d+') rules = ( # 将link 作用到了rule构造方法的参数1中 # 作用:将连提取器提取到的连接进行请求发送且根据指定规则对请求到的数据进行数据解析 # Rule(link, callback='parse_item', follow=True), ) def parse_item(self, response): print(response)
深度爬取
# -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from sunCrawlPro.items import SuncrawlproItem,Detail_item class SunSpider(CrawlSpider): name = 'sun' # allowed_domains = ['www.xxx.com'] start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page='] #实例化了一个连接提取器对象 #作用:根据指定规则(allow=’正则表达式‘)进行指定连接的提取 link = LinkExtractor(allow=r'type=4&page=\d+')#获取页码连接 #获取新闻详情页的连接 link_detail = LinkExtractor(allow=r"question/\d+/\d+\.shtml") rules = ( #将link作用到了Rule构造方法的参数1中 #作用:将连接提取器提取到的连接进行请求发送且根据指定规则对请求到的数据进行数据解析 Rule(link, callback='parse_item', follow=False), #follow=True:将连接提取器 继续作用到 连接提取器提取到的 连接 所对应的 页面中 Rule(link_detail, callback='parse_detail'), ) def parse_item(self, response): #xpath表达式中是不可以出现tbody标签 tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr') for tr in tr_list: title = tr.xpath('./td[2]/a[2]/text()').extract_first() num = tr.xpath('./td[1]/text()').extract_first() item = SuncrawlproItem() item['title'] = title item['num'] = num yield item def parse_detail(self,response): content = response.xpath('/html/body/div[9]/table[2]//tr[1]/td/text()').extract_first() num = response.xpath('/html/body/div[9]/table[1]//tr/td[2]/span[2]/text()').extract_first() num = num.split(':')[-1] item = Detail_item() item['content'] = content item['num'] = num yield item
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class SuncrawlproItem(scrapy.Item): title = scrapy.Field() num = scrapy.Field() class Detail_item(scrapy.Item): content = scrapy.Field() num = scrapy.Field()
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html class SuncrawlproPipeline(object): def process_item(self, item, spider): if item.__class__.__name__ == 'Detail_item': content = item['content'] num = item['num'] print(item) else: title = item['title'] num = item['num'] print(item) return item
因为 Rule 不能传递Item 所以深度爬取 存取在两个地方 :
建立两个item,分别存取
那如何区分不同的item 呢? 在管道中判断来存取 最后存在两个表里, 想办法找到两个页面中的相关联的key 比如mun 后期表操作 ,关联相同的mun就可以合并连接起来了
- 分布式
- 概念:
需要搭建一个分布式的机群,然后在机群的每一台电脑中执行同一组程序,让其对某一个网站的数据进行联合分布爬取。
- 原生的scrapy框架是不可以实现分布式?
- 因为调度器不可以被共享
- 管道不可以被共享
- 如何实现分布式?
- scrapy+scrapy_redis实现分布式
- scrapy-redis组件的作用是什么?
- 可以提供可被共享的调度器和管道
- 特性:数据只可以存储到redis数据库。
- 分布式的实现流程:
- 1.pip install scrapy-redis
- 2.创建工程
scrapy startproject xxx
- 3. cd 工程目录中
- 4.创建爬虫文件(a.创建基于Spider的爬虫文件 b.创建CrawlSpider的爬虫文件)
b: scrapy genspider -t crawl xxx www.xxx.com
- 5.修改爬虫类:
- 导报:from scrapy_redis.spiders import RedisCrawlSpider
- 修改当前爬虫类的父类为RedisCrawlSpider
- allowed_domains和start_urls删除
- 添加一个新属性:redis_key = 'fbsQueue',表示的是可以被共享的调度器队列的名称
- 编写爬虫类的其他操作(常规操作)
- 6.settings配置文件的配置
- UA伪装
- Robots
- 管道的指定(settings):
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
-7 指定redis数据库
REDIS_HOST = 'redis服务的ip地址'
REDIS_PORT = 6379
-8 redis的配置文件进行配置redis.windows.conf:
-关闭默认绑定:56Line:#bind 127.0.0.1
- 关闭保护模式:75Line:protected-mode no
-9 启动redis的服务端和客户端:
- redis-server.exe redis.windows.conf
- redis-cli
-10 启动程序:
scrapy runspider xxx.py
- 11 向调度器的队列中仍入一个起始的url:
- 队列是存在于redis中
- 开启redis的客户端: lpush fbsQueue http://wz.sun0769.com/index.php/question/questionType?type=4&page=
- 增量式
- 概念:用于监测网站数据更新的情况。
- 核心机制:去重。redis的set实现去重
- 总结反爬机制:
- robots
- UA伪装
- 验证码
- 代理
- cookie
- 动态变化的请求参数
- js加密
- js混淆
- 图片懒加载
- 动态数据的捕获
- seleium:规避检测