第九章 下载文件和图片
FilesPipeline和ImagesPipeline
Scrapy框架内部提供了两个Item Pipeline,专门用于下载文件和图片:
FilesPipeline
ImagesPipeline
FilesPipeline使用说明:
- 在配置文件
settings.py
中启用FilesPipeline,通常将其置于其他Item Pipeline
之前:
ITEM_PIPELINES={'scrapy.pipelines.files.FilesPipeline':1}
- 在配置文件
settings.py
中,使用FILES_STORE
指定文件下载目录,如:
FILES_STORE = 'download_images'
- 在Spider解析一个包含文件下载链接的页面时,将所有需要下载文件的url地址收集到一个列表,赋给item的file_urls字段(
item['file_urls']
)。FilesPipeline在处理每一项item时,会读取item['file_urls']
,对其中每一个url进行下载。 - 当FilesPipeline下载完
item['file_urls']
中所有文件后,会将各文件的下载结果信息收集到另一个列表,赋给item的files字段(item['files']
)。下载结果信息包括以下内容:Path
:文件下载到本地的路径(相对于FILES_STORE
的相对路径)Checksum
:文件的校验和url
:文件的url
地址
ImagesPipeline使用说明:
ImagesPipeline和FilesPipeline大同小异,只存在一些细微差别,如下图所示:
区别 | FilesPipeline | ImagesPipiline |
---|---|---|
导入路径 | scrapy.pipelines.files.FilesPipeline | scrapy.pipelines.images.ImagesPipeline |
Item字段 | file_urls,files | image_urls,images |
下载目录 | FILE_STORE | IMAGES_STORE |
ImagesPipeline特有的功能:
- 为图片生成缩略图
在配置文件中settings.py中设置IMAGES_THUMBS
,它是一个字典,每一项的值是缩略图的尺寸:
开启该功能后,下载一张图片,本地会出现三张图片。IMAGES_THUMBS = { 'small':(50,50), 'big':(270,270), }
- 过滤掉尺寸过小的图片
在配置文件中settings.py中设置IMAGES_MIN_WIDTH
和IMAGES_MIN_HEIGHT
,它们分别指定图片最小的宽和高:
IMAGES_MIN_WIDTH = 110
IMAGES_MIN_HEIGHT = 110
下载文件
项目需求
下载http://matplotlib.org网站中所有例子的源码文件到本地
编码实现
我们按以下4步完成该项目:
- 创建Scrapy项目,创建Spider
- 在配置文件中启用FilePipeline,并指定文件下载目录
- 实现ExampleItem
- 实现ExamplesSpider
步骤1:
创建Scrapy项目,取名为matplotlib_examples
,在Spider目录里创建examples.py
。
步骤2:
在配置文件中settings.py中启用FilesPipeline,并指定文件下载目录,代码如下:
ITEM_PIPELINES = {
'scrapy.pipelines.files.FilesPipeline':1
}
FILES_STORE = 'examples_src'
将ROBOTSTXT_OBEY = True
改为False
,因为这个网站现在在robot.txt
,里面禁止爬虫了,所以要改成不按照那个爬取协议来,否则最后会出错。
步骤3:
实现ExampleItem,需定义file_urls和files两个字段,在images.py中完成如下代码:
class ExampleItem(scrapy.Item):
file_urls = scrapy.Field()
files = scrapy.Field()
步骤4:
在examples.py
里创建并实现ExamplesSpider:
import scrapy
from scrapy.linkextractors import LinkExtractor
from ..items import ExampleItem
class ExamplesSpider(scrapy.Spider):
name = 'examples'
allowed_domains = ['matplotlib.org'] #过滤爬取的域名
start_urls = ['https://matplotlib.org/examples/index.html']
#提取每个例子页面的链接
def parse(self, response):
le = LinkExtractor(restrict_css='div.toctree-wrapper.compound',deny='/index.html$')
print(len(le.extract_links(response)))
for link in le.extract_links(response):
yield scrapy.Request(link.url,callback=self.parse_example)
#获取源码文件的url,将其放入一个列表,赋给ExampleItem的file_urls字段
def parse_example(self,response):
href = response.css('a.reference.external::attr(href)').extract_first()
url = response.urljoin(href)
example = ExampleItem()
example['file_urls'] = [url]
return example
运行爬虫scrapy crawl examples -o examples.json
运行结果显示下载文件的url为它的shal散列值,比如d9b551310a6668ccf43871e896f2fe6e0228567d
,这种文件名很不直观,我们期望把这些例子文件按照类别下载到不同目录下,所以我们可以通过修改FilesPipeline为文件命名的规则。在pipelines.py
实现MyFilesPipeline
,代码如下:
from scrapy.pipelines.files import FilesPipeline
from urllib.parse import urlparse
from os.path import basename,dirname,join
class MyFilesPipeline(FilesPipeline):
def file_path(self, request, response=None, info=None):
path = urlparse(request.url).path
return join(basename(dirname(path)),basename(path))
修改配置文件,使用MyFilesPipeline替代 FilesPipeline:
ITEM_PIPELINES = {
# 'scrapy.pipelines.files.FilesPipeline':1
'matplotlib_examples.pipelines.MyFilesPipeline':1,
}
删除之前的文件,重新运行,examples_src
目录如下:
到此,文件下载的项目完成。
下载图片
项目需求
下载360图片网站http://image.so.com中艺术分类下的所有图片到本地
编码实现
首先通过在该网站页面中找到艺术分类下的jQuery发送的请求,其响应结果是一个json串。
http://image.so.com/zj?ch=art&sn=%s&listtype=new&temp=1
然后我们按以下3步完成该项目:
- 创建Scrapy项目,创建Spider
- 在配置文件中启用ImagesPipeline,并指定图片下载目录
- 实现ImagesSpider
步骤1:
创建Scrapy项目,取名为so_image,在Spider目录里创建images.py
。
步骤2:
在配置文件settings.py中启用ImagesPipeline,并指定图片下载目录,代码如下:
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline':1,
}
IMAGES_STORE = 'download_images'
和上个项目一样,将ROBOTSTXT_OBEY = True
改为False
步骤3:
在images.py
里创建并实现ImagesSpider:
import scrapy
from scrapy import Request
import json
class ImageSpider(scrapy.Spider):
BASE_URL = 'http://image.so.com/zj?ch=art&sn=%s&listtype=new&temp=1'
start_index = 0
#限制最大下载量,防止磁盘用量过大
MAX_DOWNLOAD_NUM = 1000
name = 'images'
start_urls = [BASE_URL % 0]
def parse(self, response):
#使用json模块解析响应结果
infos = json.loads(response.body.decode('utf-8'))
#提取所有图片下载url到一个列表,赋给item的'image_urls'字段
yield {'image_urls':[info['qhimg_url'] for info in infos['list']]}
#如count字段大于0,并且下载数量不足 MAX_DOWNLOAD_NUM,继续下载下一页图片信息
self.start_index += infos['count']
if infos['count'] > 0 and self.start_index < self.MAX_DOWNLOAD_NUM:
yield Request(self.BASE_URL % self.start_index)
运行爬虫scrapy crawl images
,然后查看图片下载目录download_images
,如图所示:
到此,图片下载的项目完成。