使用Scrapy框架对图片按文件夹分类下载

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

1、工欲善其事,必先利其器shell

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

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位系统...),因此应选择:dom

而后安装scrapy:$ pip install scrapyscrapy

此外,因为咱们是爬取图片,须要pillow包,使用scrapy shell则要安装win32api,因而这里一并装上:$ pip install pillow pypiwin32编辑器

***很是重要***ide

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

准备工做完毕之后,如今来开始咱们的爬虫之旅。

 

2、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方法里编写。

 

3、下载图片

接下来分析一下目标网页,用谷歌浏览器打开网页,也在控制台用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

相关文章
相关标签/搜索