十八、scrapy内置媒体(图片和文件)下载方式
scrapy为下载的item中包含的文件提供了一个可重用的item pipeline(scrapy.pipelines.media.MediaPipeline),这些Pipeline有些共同的方法和结构。
MediaPipeline共同实现了以下特性:
(1)避免重新下载最近已经下载过的数据
(2)指定存储的位置和方式
ImagesPipeline还提供了额外的特性:
(1)将所有下载的图片转换成通用的格式(JPG)和模式(RGB)
(2)缩略图生成
(3)检查图像的宽/高,确保它们满足最小限制
MediaPipeline去重的方式:
(1)MediaPipeline会为当前安排好的要下载的图片保留一个内部队列,并将那些到达的包含相同图片的项目连接到该队列中,避免多次下载几个item共享的同一图片
1、使用FilesPipeline的工作流程
1、在一个爬虫里,抓取一个item,把其中文件的url放入file_urls组内
2、Item从爬虫内返回,进入item pipeline
3、当item进入filespipeline,file_urls组内的url将被Scrapy的调度器和下载器安排下载。Item会在这个特定的管道阶段保持"Locker"的状态,直到完成文件的下载(或者未完成下载而失败)
4、当文件下载完成后,另一个字段(files)将被更新到结构中。这个组将包含一个字典列表,其中包括下载文件的信息,如下载路径、源抓取地址和文件的校验码(checksum)。files列表文件顺序将和源file_urls组保持一致。当某个文件下载失败,将会记录下错误信息,文件将不会出现在files组内。
2、使用FilesPipeline
1、在settings.py文件的ITEM_PIPELINES中添加一条“scrapy.pipelines.files.FilesPipeline”:1
2、在item中添加两个字段:
(1)file_urls = scrapy.Field()
(2)files = scrapy.Field()
3、在settings.py文件中添加:
(1)FILES_STORE = "./files" # 下载路径
(2)FILES_FILES_FIELD = ‘file_urls‘ # 文件url所在的item字段
(3)FILES_RESULT_FIELD = "files" # 文件结果信息所在的item字段
4、在settings.py中的可选设置
(1)FILES_EXPIRES = 30 # 30天过期
(2)IMAGES_THUMBS = {
"small":(50,70),
"big":(270,270),
}
IMAGES_THUMBS能制作缩略图,并设置缩略图尺寸大小。
IMAGES_EXPIRES = 30 设置文件过期时间
IMAGES_MIN_HEIGHT和IMAGES_MIN_WIDTH来设置图片的最小高和宽
3、示例
1、目录结构
图片名称是图片下载链接由scrapy自行按经过SHA1哈希后的值
image_urls存在目标url,images存放返回结果
2、items.py代码
import scrapy class TiantangtupianItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() href = scrapy.Field() image_urls = scrapy.Field() images = scrapy.Field()
3、settings.py代码
BOT_NAME = ‘tiantangtupian‘ SPIDER_MODULES = [‘tiantangtupian.spiders‘] NEWSPIDER_MODULE = ‘tiantangtupian.spiders‘ LOG_LEVEL = "WARNING" # Obey robots.txt rules ROBOTSTXT_OBEY = False # Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { ‘tiantangtupian.pipelines.TiantangtupianPipeline‘: 300, ‘scrapy.pipelines.images.ImagesPipeline‘:1, } IMAGES_STORE = ‘./Images‘ IMAGES_URLS_FIELD = ‘image_urls‘ IMAGES_RESULT_FIELD = ‘images‘
4、pipelines.py
class TiantangtupianPipeline: def process_item(self, item, spider): print(item) return item
5、main.py
class TttpSpider(scrapy.Spider): name = ‘tttp‘ allowed_domains = [‘ivsky.com‘] start_urls = [‘https://www.ivsky.com/tupian/ziranfengguang/index_1.html‘] def parse(self, response): li_list = response.xpath(‘//ul[@class="ali"]/li‘) for li in li_list: item = TiantangtupianItem() href = li.xpath(‘.//img/@src‘).extract() item[‘href‘] = parse.urljoin(base=response.url,url=href[0] if href else None) item[‘image_urls‘] = [parse.urljoin(base=response.url,url=href[0] if href else None)] yield item
4、定制自己的FilesPipeline
需要继承FilesPipeline或者ImagesPipeline,重写get_media_requests和item_completed()方法。
1、get_media_requests(item,info)方法
需要重写get_media_requests()方法,并对各个图片url返回一个Request
```
def get_media_requests(self,item,info):
for image_url in item[‘image_urls‘]:
yield scrapy.Request(image_url)
```
当这些请求由管道处理完成后,结果results将以2-元素的元组列表形式传送到item_completed()方法中
success是一个布尔值,当图片成功下载时为True,失败时为False
url是图片下载的url
path是图片存储的路径
checksum是图片内容MD5 hash
2、item_completed(results,items,info)方法
当一个单独项目中的所有图片请求完成时,ImagesPipeline.item_completed()方法将被调用。
results参数是get_media_requests下载完成之后返回的结果。
item_completed()方法需要返回一个输出(可以按需要返回或丢弃项目),该item将被送到随后的ItemPipelines。
```
from scrapy.exceptions import DropItem
def item_completed(self,results,item,info):
image_paths = [x[‘path‘] for ok,x in results if ok] # 遍历取url
if not image_paths:
raise DropItem("Item contains no images")
item[‘image_urls‘] = image_paths #去掉没有完成下载的图片链接
return item
```
3、示例
注意:重写的itempipeline需要在settings中注册才能使用。
from scrapy.pipelines.images import ImagesPipeline from scrapy.exceptions import DropItem import scrapy from pprint import pprint class TiantangtupianPipeline: def process_item(self, item, spider): print(item) return item class MyImagesPipeline(ImagesPipeline): def get_media_requests(self,item,info): for image_url in item[‘image_urls‘]: yield scrapy.Request(url=image_url) def item_completed(self, results, item, info): print("*"*50) pprint(results) print("*" * 50) print("-"*20) image_path = [x[‘path‘] for ok,x in results if ok] if not image_path: raise DropItem("Item contains no images") item[‘image_urls‘] = image_path return item