scrapy自带文件下载器,实现多层级目录结构的存储

概scrapy既然是一款强大的爬虫框架,自然也实现了图片和文件的下载,FilesPipeline、ImagesPipeline分别是图片和文件的下载器,image也是文件的一种为什么还要单独提供一个image下载器?这是因为图片下载器还提供了一些额外方法:缩略图生成、图片过滤;今天就来介绍这两款特殊的下载器。

使用方法

1.常规的使用方法是首先在item中定义关键字,images和files关键字段如下:

image

当然其中字段还可以增加可以加title、Word等自定义字段

2.在setting.py中启用下载管道

ITEM_PIPELINES = {    'scrapy.pipelines.files.FilesPipeline': 1,    'scrapy.pipelines.images.ImagesPipeline': 2}

上面启用的是内部的下载器,如果实现了自定义需要将路径替换为自定义的下载器

3.在爬虫主程序中传递url,yield item{'file_urls':[url1, url2,`````````]}

下面将从源码和案例来分析具体如何使用

注:file下载和image下载的原理是一样的,只是images下载图片可以配置,所以我们分析file的源码,讲image的实例

FilesPipeline源码

FilesPipeline文件位于:

scrapy.pipelines.files

源码有点多,我们只需要关注两个地方,一个是下载的实现一个是下载的文件命名

class FilesPipeline(MediaPipeline):
    """Abstract pipeline that implement the file downloading

    This pipeline tries to minimize network transfers and file processing,
    doing stat of the files and determining if file is new, uptodate or
    expired.

    `new` files are those that pipeline never processed and needs to be
        downloaded from supplier site the first time.

    `uptodate` files are the ones that the pipeline processed and are still
        valid files.

    `expired` files are those that pipeline already processed but the last
        modification was made long time ago, so a reprocessing is recommended to
        refresh it in case of change.

    """

    ### Overridable Interface
    def get_media_requests(self, item, info):
        return [Request(x) for x in item.get(self.files_urls_field, [])]

    def file_path(self, request, response=None, info=None):
        ## start of deprecation warning block (can be removed in the future)
        def _warn():
            from scrapy.exceptions import ScrapyDeprecationWarning
            import warnings
            warnings.warn('FilesPipeline.file_key(url) method is deprecated, please use '
                          'file_path(request, response=None, info=None) instead',
                          category=ScrapyDeprecationWarning, stacklevel=1)

        # check if called from file_key with url as first argument
        if not isinstance(request, Request):
            _warn()
            url = request
        else:
            url = request.url

        # detect if file_key() method has been overridden
        if not hasattr(self.file_key, '_base'):
            _warn()
            return self.file_key(url)
        ## end of deprecation warning block

        media_guid = hashlib.sha1(to_bytes(url)).hexdigest()  # change to request.url after deprecation
        media_ext = os.path.splitext(url)[1]  # change to request.url after deprecation
        return 'full/%s%s' % (media_guid, media_ext)

其中get_media_requests用于解析item中的file_urls字段里的url列表,并构造成请求

file_path是给文件命名,使用的url的sha1散列值也我们只需要让return返回我们想要的文件名即可。

图片下载的关键源码同上。

image自定义下载路径及文件名实例

目标:下载图片按照原网站的文件目录组织结构、文件名按照原网站命名,本地生成缩略图,小于200*200以下的文件。

目标明确就开工,先创建一个项目模板,然后开始做我们的案例。

案例图片是类似于妹子图的组图网站,不过内容不宜展示,并且网站也被封了,就不在其中展示敏感内容,这是以前的案例。

items.py 定义字段

import scrapy

class RgspoiderItem(scrapy.Item):
    image_urls = scrapy.Field()
    images = scrapy.Field()
    title = scrapy.Field()
    word = scrapy.Field()

title是分类名字,Word是组图的专题名字, 组图下载后将按照分类-专题-组图的目录结构存储,是三层结构。

Spider.py 爬虫主程序

# -*- coding: utf-8 -*-
import scrapy
from ..items import RgspoiderItem

class PicturnSpider(scrapy.Spider):

    name = 'picturn'
    allowed_domains = ['xxxxxx.com']
    start_urls = ['http://xxxxxx/index.html']

    def parse(self, response):
        select = response.xpath('//*[@id="body"]/div[2]/ul//li')
        for i in select:
            URL = i.xpath('a/@href').extract()[0]
            title = i.xpath('a/text()').extract()[0] # 获取分类名字并专递给解析函数
            yield scrapy.Request(
                response.urljoin(URL), callback=self.parse_ml, meta={'title': title})

    def parse_ml(self, response):
        mulu = response.xpath('//li[@class="zxsyt"]/a')
        title = response.meta['title']
        for i in mulu:
            urls = i.xpath('@href').extract()[0]
            word = i.xpath('text()').extract()[0]
            yield scrapy.Request(
                response.urljoin(urls), callback=self.parse_pict,
                meta={'word': word, 'title': title}) # 将分类名字及其组图标题在传递给解析函数

        next_url = response.xpath('//font[@class="PageCss"]/..//a/@href').extract()
        for nuel in next_url:
            title = response.meta['title']
            yield scrapy.Request(response.urljoin(nuel), callback=self.parse_ml,
                                 meta={'title': title})  

    def parse_pict(self, response):
        items = RgspoiderItem()
        items['title'] = response.meta['title']
        items['word'] = response.meta['word']
        pict = response.xpath('//div[@class="temp23"]//a/@href').extract()
        items['image_urls'] = pict  # 构造item数据传递给下载器,item包含了单张图片,并附带该图片所在分类和组图标题,后面自动归类
        yield items

主程序实现了三层解析,第一层获取所有分类连接及其分类名字,第二层获取所有分类下的组图连接及其组图标题,第三层获取组图下的所有图片连接并将连接、分类、标题传递给下载器

pipeline.py 复写imagespipeline

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy.pipelines.images import ImagesPipeline
import scrapy

class RgspoiderPipeline(ImagesPipeline):

    def get_media_requests(self, items, info):
        print(items)
        title = items['title']
        word = items['word']
        for image_url in items['image_urls']:
            yield scrapy.Request(image_url, meta={'title': title, 'word': word}) # 继续传递分类、标题

    def file_path(self, request, response=None, info=None):
        # 按照full\分类\标题\图片合集命名
        filename = r'full\%s\%s\%s' % (request.meta['title'], request.meta['word'], request.url[-6:])
        return filename

现在get_media_requests狗仔request请求,然后在file_path中解析构造的request请求中的meta字段来构建文件名字,并取url的后几个字符串作为组图中单张图片的名字。

setting.py

ITEM_PIPELNES={
    'rgSpoider.pipelines.RgspoiderPipeline': 1,
}

IMAGES_STORE = 'D://A' # 问价存放的根目录
IMAGES_THUMBS = {
'thumbnail': (50, 50) # 生成缩略图标签及其尺寸
}
IMAGES_MIN_WIDTH = 200 # 过滤宽度小于200
IMAGES_MIN_HEIGHT = 300 # 过滤高度小于300

image
image

—END—

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容