前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Scrapy之FilesPipeline和ImagesPipline文件与图片下载

Scrapy之FilesPipeline和ImagesPipline文件与图片下载

作者头像
菲宇
发布2019-06-13 11:45:39
3K0
发布2019-06-13 11:45:39
举报
文章被收录于专栏:菲宇菲宇

Media Pipeline

Scrapy为下载item中包含的文件(比如在爬取到产品时,同时也想保存对应的图片)提供了一个可重用的 item pipelines . 这些pipeline有些共同的方法和结构(称之为media pipeline)。我们可以使用FilesPipeline和Images Pipeline来保存文件和图片,他们有以下的一些特点:

  • Files Pipeline
    • 避免重新下载最近已经下载过的数据
    • 指定存储路径

    FilesPipeline的典型工作流程如下:

    1. 在一个爬虫里,你抓取一个项目,把其中图片的URL放入 file_urls 组内。
    2. 项目从爬虫内返回,进入项目管道。
    3. 当项目进入 FilesPipeline,file_urls 组内的URLs将被Scrapy的调度器和下载器(这意味着调度器和下载器的中间件可以复用)安排下载,当优先级更高,会在其他页面被抓取前处理。项目会在这个特定的管道阶段保持“locker”的状态,直到完成文件的下载(或者由于某些原因未完成下载)。
    4. 当文件下载完后,另一个字段(files)将被更新到结构中。这个组将包含一个字典列表,其中包括下载文件的信息,比如下载路径、源抓取地址(从 file_urls 组获得)和图片的校验码(checksum)。 files 列表中的文件顺序将和源 file_urls 组保持一致。如果某个图片下载失败,将会记录下错误信息,图片也不会出现在 files 组中。
  • Images Pipeline
    • 避免重新下载最近已经下载过的数据
    • 指定存储路径
    • 将所有下载的图片转换成通用的格式(JPG)和模式(RGB)
    • 缩略图生成
    • 检测图像的宽/高,确保它们满足最小限制

    和FilesPipeline类似,除了默认的字段名不同,image_urls保存图片URL地址,images保存下载后的图片信息。当然,它还提供了一些拓展功能,比如图片的缩略图,过滤图片的尺寸。 注意:你需要安装Pillow 库来实现以上的拓展功能。

启用Media Pipeline

要想使用media pipeline,你需要在设置添加一些必要的信息。

代码语言:javascript
复制
# 同时启用图片和文件管道
ITEM_PIPELINES = {
                  'scrapy.pipelines.images.ImagesPipeline': 1,
                  'scrapy.pipelines.files.FilesPipeline': 2,
                 }
FILES_STORE = 'D:'  # 文件存储路径
IMAGES_STORE = 'D' # 图片存储路径

# 避免下载最近90天已经下载过的文件内容
FILES_EXPIRES = 90
# 避免下载最近90天已经下载过的图像内容
IMAGES_EXPIRES = 30

# 设置图片缩略图
IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (250, 250),
}
# 图片过滤器,最小高度和宽度,低于此尺寸不下载
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

你可能会好奇文件的命名,在当你启用media pipeline以后, 它的默认命名方式是这样的,文件以它们URL的 SHA1 hash 作为文件名。 例如, 对下面的图片URL:http://www.example.com/image.jpg, 其SHA1 hash 值为:3afec3b4765f8f0a07b78f98c07b83f013567a0a 将被下载并存为下面的文件:<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg 其中, 是定义在 IMAGES_STORE 设置里的文件夹,我们设置的是D盘,full 是用来区分图片和缩略图(如果使用的话)的一个子文件夹,这个文件夹scrapy会自动生成。

扩展Media Pipeline

下面我们以ImagesPipeline为例来自定义ImagesPipeline,需要重写以下两个方法:

  • get_media_requests(item, info) 在工作流程中可以看到,管道会得到图片的URL并从项目中下载。为了这么做,你需要重写 get_media_requests() 方法,并对各个图片URL返回一个Request: def get_media_requests(self, item, info): for image_url in item['image_urls']: yield scrapy.Request(image_url) 这些请求将被管道处理,当它们完成下载后,结果将以2元素的元组列表形式传送到 item_completed() 方法: 每个元组包含 (success, file_info_or_error):
    • success 是一个布尔值,当图片成功下载时为 True ,因为某个原因下载失败为False
    • file_info_or_error 是一个包含下列关键字的字典(如果成功为 True )或者出问题时为 Twisted Failure 。 url - 文件下载的url。这是从 get_media_requests() 方法返回请求的url。 path - 图片存储的路径(类似 IMAGES_STORE) checksum - 图片内容的 MD5 hash item_completed() 接收的元组列表需要保证与 get_media_requests() 方法返回请求的顺序相一致。下面是 results 参数的一个典型值: [(True, {'checksum': '2b00042f7481c7b056c4b410d28f33cf', 'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg', 'url': 'http://www.example.com/files/product1.jpg'}), (False, Failure(...))] 该方法 必须返回每一个图片的URL。
  • item_completed(results, items, info) 当一个单独项目中的所有图片请求完成时,例如,item里面一共有10个URL,那么当这10个URL全部下载完成以后,ImagesPipeline.item_completed() 方法将被调用。默认情况下, item_completed() 方法返回item。

可以改写自己的文件名的保存方式

class MyFilesPipeline(FilesPipeline): def file_path(self, request, response=None, info=None): split_url = str(request.url).split('/') kind_name = split_url[-2] file_name = split_url[-1] return '%s/%s'%(kind_name,file_name) # path = urlparse(request.url).path # return join(basename(dirname(path)),basename(path))

在settings中设置

ITEM_PIPELINES = { #'scrapy.pipelines.files.FilesPipeline':1 'download_files.pipelines.MyFilesPipeline':1 # 'download_files.pipelines.DownloadFilesPipeline': 300, }

也可以编写自己的文本格式来保存:

class ExcelItemExporter(BaseItemExporter): def __init__(self,file,**kwargs): self._configure(kwargs) self.file = file self.row = 0 self.wb = xlwt.Workbook() self.sheet = self.wb.add_sheet('books') def finish_exporting(self): self.wb.save(self.file) def export_item(self, item): items = self._get_serialized_fields(item) for col,v in enumerate(x for _,x in items): self.sheet.write(self.row,col,v) self.row +=1

然后在settings中配置:

FEED_EXPORTERS = {'excel':'douban_books.my_exporter.ExcelItemExporter'} FEED_EXPORT_FIELDS = ['url','ISBN','name','author','publish','publish_time','subtitle','price','pages','series','about_author','content']

FEED_EXPORT_FIELDS 可以设置保存数据字段的顺序

使用ImagesPipeline下载图片

下面我们用上面学习到的知识来下载一些图片。 我们以http://jandan.net/ooxx为例,把页面上的图片下载下来,并产生缩略图 我们新建一个项目,名为jiandan,各个文件内容如下。

  • item.py
代码语言:javascript
复制
import scrapy

class JiandanItem(scrapy.Item):
    image_urls = scrapy.Field()#图片的链接
    images = scrapy.Field()
  • jiandan_spider.py
代码语言:javascript
复制
import scrapy
from jiandan.items import JiandanItem

class jiandanSpider(scrapy.Spider):
    name = 'jiandan'
    start_urls = ["http://jandan.net/ooxx"]

    def parse(self, response):

        item = JiandanItem()
        item['image_urls'] = response.xpath('//img//@src').extract() #提取图片链接
        yield item
  • settings.py
代码语言:javascript
复制
BOT_NAME = 'jiandan'

SPIDER_MODULES = ['jiandan.spiders']
NEWSPIDER_MODULE = 'jiandan.spiders'

DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
}

ITEM_PIPELINES = {
   'jiandan.pipelines.JiandanPipeline':1,
}
IMAGES_STORE='H:\\jiandan'
IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (200, 200),
}
  • pipelinse.py
代码语言:javascript
复制
import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline   #内置的图片管道

class JiandanPipeline(ImagesPipeline):#继承ImagesPipeline这个类

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            image_url = "http://" + image_url
            yield scrapy.Request(image_url)



    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        return item

运行这个spider,你会发现,图片已经下载好了,如下图所示。

方法一:

item.py文件:定义item字段

import scrapy class PicPhotoPeopleItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # 存放url的下载地址 image_urls = scrapy.Field() # 图片下载路径、url和校验码等信息(图片全部下载完成后将信息保存在images中) images = scrapy.Field() # 图片的本地保存地址 image_paths = scrapy.Field()

spider.py文件:编写爬虫文件,解析源码,得到图片的url下载路径

# -*- coding: utf-8 -*- import scrapy from ..items import PicPhotoPeopleItem class PeopleSpider(scrapy.Spider): name = 'people' allowed_domains = ['699pic.com'] start_urls = ['http://699pic.com/people.html'] base_url = 'http://699pic.com/' def parse(self, response): # 获取全部的图片地址 list_imgs = response.xpath('//div[@class="swipeboxEx"]/div[@class="list"]//img/@data-original').extract() if list_imgs: item = PicPhotoPeopleItem() item['image_urls'] = list_imgs # 保存到item的image_urls里 yield item # 返回item给pipeline文件处理 # 获取下一页url next_url = response.xpath('//a[@class="downPage"]/@href').extract()[0] \ if len(response.xpath('//a[@class="downPage"]/@href')) else None if next_url: yield scrapy.Request(url=self.base_url + next_url,callback=self.parse)

pipelines.py文件:对item里的数据进行处理,这里是调用调度器和下载器下载图片。重写一些ImagesPileline类的方法。

需要重写的方法有:get_media_requests(item, info)和item_completed(results, items, info)

from scrapy.pipelines.images import ImagesPipeline from scrapy.exceptions import DropItem from scrapy.http import Request class PicPhotoPeoplePipeline(ImagesPipeline): def file_path(self, request, response=None, info=None): """ 重写ImagesPipeline类的file_path方法 实现:下载下来的图片命名是以校验码来命名的,该方法实现保持原有图片命名 :return: 图片路径 """ image_guid = request.url.split('/')[-1] # 取原url的图片命名 return 'full/%s' % (image_guid) def get_media_requests(self, item, info): """ 遍历image_urls里的每一个url,调用调度器和下载器,下载图片 :return: Request对象 图片下载完毕后,处理结果会以二元组的方式返回给item_completed()函数 """ for image_url in item['image_urls']: yield Request(image_url) def item_completed(self, results, item, info): """ 将图片的本地路径赋值给item['image_paths'] :param results:下载结果,二元组定义如下:(success, image_info_or_failure)。 第一个元素表示图片是否下载成功;第二个元素是一个字典。 如果success=true,image_info_or_error词典包含以下键值对。失败则包含一些出错信息。 字典内包含* url:原始URL * path:本地存储路径 * checksum:校验码 :param item: :param info: :return: """ image_paths = [x['path'] for ok, x in results if ok] if not image_paths: raise DropItem("Item contains no images") # 如果没有路径则抛出异常 item['image_paths'] = image_paths return item

settings.py文件:配置setting文件。

import os # 配置数据保存路径,为当前工程目录下的 images 目录中 project_dir = os.path.abspath(os.path.dirname(__file__)) IMAGES_STORE = os.path.join(project_dir, 'images') # 过期天数 IMAGES_EXPIRES = 90 # 90天内抓取的都不会被重抓 # IMAGES_MIN_HEIGHT = 100 # 图片的最小高度 # IMAGES_MIN_WIDTH = 100 # 图片的最小宽度

结果:

方法二:

item.py和spider.py同上;pipeline.py此处不需要自己编写,直接引用现成的ImagePileline;

修改settings.py文件即可,代码如下:

import os IMAGES_URLS_FIELD = 'image_urls' # 要保存的字段,即在 Item 类中的字段名为 image_urls # 配置数据保存路径,为当前工程目录下的 images 目录中 project_dir = os.path.abspath(os.path.dirname(__file__)) IMAGES_STORE = os.path.join(project_dir, 'images') # 过期天数 IMAGES_EXPIRES = 90 # 90天内抓取的都不会被重抓 # IMAGES_MIN_HEIGHT = 100 # 图片的最小高度 # IMAGES_MIN_WIDTH = 100 # 图片的最小宽度 ITEM_PIPELINES = { 'scrapy.pipelines.images.ImagesPipeline': 1, }

结果同上。

注意点:在写爬虫文件解析response时,获取图片的下载地址,一开始写的xpath是@src,但是爬取到的image_urls里面全部是http://static.699pic.com/images/blank.png

后来排查代码,观察网页源代码,发现源代码中@src的值是http://static.699pic.com/images/blank.png。刚才element中的src是经过渲染之后的值,所以最后采用的@data-original。这也就证实了爬虫获取到的response是网页的源码,爬取之前需要先确认源码和element中的元素和值是否一致,只有一致了才可以直接使用element中的元素和值。

转自:https://blog.csdn.net/loner_fang/article/details/81111879

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年05月06日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Media Pipeline
  • 启用Media Pipeline
  • 扩展Media Pipeline
  • 使用ImagesPipeline下载图片
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档