zoukankan      html  css  js  c++  java
  • 使用Scrapy框架对图片按文件夹分类下载

    应室友需求,这里我打算使用python的scrapy框架去下载“优美图库”上的美图,话不多说这就开始行动。

    一、工欲善其事,必先利其器

    第一步,配置scrapy爬虫环境,先将pip换到国内阿里云的源。在用户目录下新建pip/pip.ini:

    1 [global]
    2 index-url = https://mirrors.aliyun.com/pypi/simple
    3 trusted-host = mirrors.aliyun.com

    我这里是Windows环境,要手动下载安装Twisted包,否则会提示需要VC++14.0:

    下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

    找到和自己python对应版本的包,我这里是python3.7-32(学校机房都是32位系统...),所以应选择:

    然后安装scrapy:$ pip install scrapy

    此外,由于我们是爬取图片,需要pillow包,使用scrapy shell则要安装win32api,于是这里一并装上:$ pip install pillow pypiwin32

    ***非常重要***

    Python采用缩进来控制代码块,请不要混用tap和空格,否则会报语法错误。我这里使用notepad++编辑器,可在设置>首选项>语言>制表符设置替换为空格:

    准备工作完毕以后,现在来开始我们的爬虫之旅。

    二、Scrapy基础

    首先新建一个项目:$ scrapy startproject girls

    其中girls为项目名称,可以看到将会创建一个girls文件夹,目录结构大致如下:

    这些文件分别是:

    scrapy.cfg:项目的配置文件

    girls/items.py:项目的目标文件

    girls/pipelines.py:项目的管道文件

    girls/settings.py:项目的设置文件

    girls/spiders/:存储爬虫代码目录

    然后需要明确目标:我们要抓取https://www.umei.cc/tags/meinv.htm网站里的所有美图。打开girls目录下的items.py文件,编辑GirlsItem类:

    1 class GirlsItem(scrapy.Item):
    2     # define the fields for your item here like:
    3     name = scrapy.Field() #图片名
    4     image_url = scrapy.Field() #图片链接
    5     pass

    然后进入到girls目录下输入如下命令创建一个girl爬虫:

    $ scrapy genspider girl 'umei.cc'

    会看到spiders目录下新增了girl.py文件,主要是编写这个爬虫文件。首先将起始网页修改为:

    1 start_urls = ['http://www.umei.cc/tags/meinv.htm']

    分析网页的程序在parse方法里编写。

    三、下载图片

    接下来分析一下目标网页,用谷歌浏览器打开网页,也在控制台用scrapy shell打开网址:

    $ scrapy shell http://www.umei.cc/tags/meinv.htm

    在浏览器端使用右键检查图片元素可以看到每一页都是一个TypeList列表,找到TypeList下的超链接:

    >>> list = response.xpath('//div[@class="TypeList"]/ul/li/a/@href').extract()

    可以输出list看看:

    随便进入一个超链接:

    $ scapy shell https://www.umei.cc/meinvtupian/meinvxiezhen/194087.htm

    会看到下面有关于这个主题的一系列图片,我们的目标是把这些图片按标题文件夹分类存放,图片名称则按顺序编号。

    于是我们先找到图片:

    >>> img = response.xpath('//div[@class="ImageBody"]/p/a/img')

    可以看到img的alt属性就是我们要的文件夹名,而src就是要下载的图片,可以把图片地址显示出来看看:

    找到当前图片的索引,按顺序编号存放:

    >>> index = response.xpath('//li[@class="thisclass"]/a/text()').extract_first()

    不排除图片有png或其他格式的,为了写出更健壮的程序,所以应该取出url最后的文件扩展名:

    1 # 新建一个图片对象
    2 item = GirlsItem()
    3 item['image_url'] = img.xpath('@src').extract_first()
    4 image_type = item['image_url'].split('.')[-1] # 获取图片扩展名
    5 item['name'] = img.xpath('@alt').extract_first() + '/' + index + '.' + image_type
    6 yield item

    接下来便是获取下一张图片,找到底部分页索引:

    提取出下一页的链接:

    >>> next_page = response.xpath('//div[@class="NewPages"]/ul/li/a/@href')[-1].extract()

    需要注意这是一个相对地址,我们要的是绝对地址,还好scrapy已经帮我们解决了问题:

    >>> response.urljoin(next_page)

    最前面的首页下面也有一个分页索引,也就是TypeList的分页索引,当然也不能放过:

    需要回到上一页的地址,然后:

    >>> next_url = response.xpath('//div[@class="NewPages"]/ul/li')[-2].xpath('a/@href').extract_first()

    取出“下一页”的链接。

    接下来只需要按照上面的流程递归的爬取每一张图片即可,完整的girl.py文件内容如下:

     1 # -*- coding: utf-8 -*-
     2 import scrapy
     3 from girls.items import GirlsItem
     4 
     5 class GirlSpider(scrapy.Spider):
     6     name = 'girl'
     7     allowed_domains = ['umei.cc']
     8     start_urls = ['http://www.umei.cc/tags/meinv.htm']
     9 
    10     def parse(self, response):
    11         list = response.xpath('//div[@class="TypeList"]/ul/li/a/@href').extract()
    12         for url in list:
    13             yield scrapy.Request(response.urljoin(url), callback = self.myParse)
    14         
    15         next_url = response.xpath('//div[@class="NewPages"]/ul/li')[-2].xpath('a/@href').extract_first()
    16         if next_url:
    17             yield scrapy.Request(response.urljoin(next_url), callback = self.parse)
    18     
    19     # 下载每一个主题内的图片
    20     def myParse(self, response):
    21         img = response.xpath('//div[@class="ImageBody"]/p/a/img')
    22         index = response.xpath('//li[@class="thisclass"]/a/text()').extract_first()
    23         
    24         item = GirlsItem()
    25         item['image_url'] = img.xpath('@src').extract_first()
    26         image_type = item['image_url'].split('.')[-1]
    27         item['name'] = img.xpath('@alt').extract_first() + '/' + index + '.' + image_type
    28         yield item
    29         
    30         next_page = response.xpath('//div[@class="NewPages"]/ul/li/a/@href')[-1].extract()
    31         if next_page != '#':
    32             yield scrapy.Request(response.urljoin(next_page), callback = self.myParse)

    重要的是管道文件(pipelines.py)的编写,我们要继承scrapy的ImagesPipeline,先重载get_media_requests方法将文件名封装到元数据(meta)里,然后重载路径函数file_path获取文件名,pipelines.py修改如下:

    1 from scrapy.pipelines.images import ImagesPipeline
    2 from scrapy import Request
    3 
    4 class GirlsPipeline(ImagesPipeline):
    5     def get_media_requests(self, item, info):
    6         yield Request(item['image_url'], meta = {'name': item['name']})
    7     
    8     def file_path(self, request, response = None, info = None):
    9         return request.meta['name']

    然后在配置文件(settings.py)里启用我们的管道文件:

    由于只有一个管道,后面的优先级可任意,这里默认的是300。

    最后,运行爬虫:$ scrapy crawl girl。无奈学校机房电脑只能用https协议,拿室友电脑跑了一下,将网速限制在10M左右,做一个有素质的爬虫。见下图:

     源码下载:https://files.cnblogs.com/files/viewts/girls.7z

  • 相关阅读:
    javascript 调试代码
    简洁的js拖拽代码
    搭个小窝
    FastDFS随笔
    JDK6和JDK7中String的substring()方法及其差异
    杂笔
    JVM内存随笔
    java中的final和volatile详解
    关于java的Synchronized,你可能需要知道这些(下)
    关于java的Synchronized,你可能需要知道这些(上)
  • 原文地址:https://www.cnblogs.com/viewts/p/10832403.html
Copyright © 2011-2022 走看看