本系列文档用于对Python爬虫技术的学习进行简单的教程讲解,巩固自己技术知识的同时,万一一不小心又正好对你有用那就更好了。
Python 版本是3.7.4
本篇文章主要pipeline模块下载文件和图片的使用。
Scrapy为下载item中包含的文件(比如再爬取到产品时,同时也想保存到对应图片)提供了一个可重用的item pipeline
。这些pipeline
有些共同的方法和结构(我们称之为media Pipeline
),一般来说我们会使用到File Pipeline
和Images Pipeline
。
我们为什么要选择用Scrapy
内置的下载文件的防范:
File Pipeline
当使用File Pipeline
下载文件的时候,按照以下步骤来完成:
Item
,然后再这个Item
中定义两个属性,分别为file_urls
以及files
。file_urls
是用来存储需要下载的文件的url链接,需要给一个列表。item
的files
属性中。比如下载路径、下载的url和文件的校验码等。setting.py
中配置FILES_STORE
,这个配置时用来设置文件下载下来的路径。pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.files.FilesPipeline:1
。Images Pipeline
当使用Images Pipeline
下载文件的时候,按照以下步骤来完成:
Item
,然后再这个Item
中定义两个属性,分别为image_urls
以及images
。image_urls
是用来存储需要下载的图片的url链接,需要给一个列表。item
的images
属性中。比如下载路径、下载的url和图片的校验码等。setting.py
中配置IMAGES_STORE
,这个配置时用来设置文件下载下来的路径。pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.images.ImagesPipeline:1
。我们就以爬取汽车之家中的图片为例。
传统的下载方式在这里就简单说下步骤:
setting.py
文件配置信息;items.py
代码;spider
模块下代码(爬取网页中需要下载的图片链接);pipelines.py
代码(进行数据处理,图片的保存等)。pipelines.py
代码如下:
import os
import urllib.request
class BmwPipeline(object):
def __init__(self):
# 创建文件保存文件夹
self.image_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
if not os.path.exists(self.image_path):
os.mkdir(self.image_path)
def process_item(self, item, spider):
category = item['category']
img_urls = item['img_urls']
# 创建图片类别文件夹
category_path = os.path.join(self.image_path, category)
if not os.path.exists(category_path):
os.mkdir(category_path)
# 根据图片url进行下载保存图片
for url in img_urls:
img_name = url.split('_')[-1]
urllib.request.urlretrieve(url=url, filename=os.path.join(category_path, img_name))
print(img_name)
return item
通过上述代码我们就可以将爬取的图片url进行处理保存图片到本地。但是这种处理方式一个很大的缺点就是图片只能一个一个下载不能进行异步下载。下面我们使用Scrapy
框架中自带的图片下载进行下载图片。
Scrapy
框架自带ImagesPipeline
进行下载图片按照上述的步骤进行编写相应的代码:
Item
,然后再这个Item
中定义两个属性,分别为image_urls
以及images
。编写items.py
代码如下:import scrapy
class BmwItem(scrapy.Item):
category = scrapy.Field()
image_urls = scrapy.Field()
images = scrapy.Field()
setting.py
中配置IMAGES_STORE
。增加代码如下:# 图片下载路径,供image pipeline使用
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.images.ImagesPipeline:1
。修改代码如下:ITEM_PIPELINES = {
# 'bmw.pipelines.BmwPipeline': 300,
'scrapy.pipelines.images.ImagesPipeline': 1
}
spider
模块代码如下:import scrapy
from bmw.items import BmwItem
class Bmw5Spider(scrapy.Spider):
name = 'bmw5'
allowed_domains = ['car.autohome.com.cn']
start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
def parse(self, response):
uiboxs = response.xpath('//div[@class="uibox"]')[1:]
for uibox in uiboxs:
category = uibox.xpath('.//div[@class="uibox-title"]/a/text()').get()
img_urls_tmp = uibox.xpath('.//ul/li/a/img/@src').getall()
img_urls = list(map(lambda url:response.urljoin(url),img_urls_tmp))
item = BmwItem(category=category,image_urls=img_urls)
yield item
运行代码你会发现其能很快的将图片下载完成。下载完成后你会在你定义的图片保存文件夹images
下多了一个full
文件夹,这个里面就是所下载下来的所有图片。有的想问如果我还想要根据分类继续保存图片需要怎么办呢?这就需要我们去重写ImagesPipeline
类中的一些方法了。通过读ImagesPipeline
类的代码我们可以知道,类中有两个方法分别是get_media_requests
和file_path
。
get_media_requests
: 这个方法是在发送下载请求之前调用,其实这个方法本身就是去发送下载请求的。file_path
: 这个方式是在图片将要被存储时候调用,来获取这个图片的存储路径。优化方案如下:
pipelines.py
中重写声明定义一个类,继承ImagesPipeline
;import os
import urllib.request
from scrapy.pipelines.images import ImagesPipeline
from bmw import settings
class BmwImagesPipeline(ImagesPipeline):
# 重写get_media_requests方法
def get_media_requests(self, item, info):
# 这个方法是在发送下载请求之前调用
# 其实这个方法本身就是去发送下载请求的
request_objs = super(BmwImagesPipeline, self).get_media_requests(item, info)
# 将item数据加入到请求中
for requests_obj in request_objs:
requests_obj.item = item
return request_objs
# 重写file_path方法
def file_path(self, request, response=None, info=None):
# 这个方式是在图片将要被存储时候调用,来获取这个图片的存储路径
# 获取父类返回的保存地址
path = super(BmwImagesPipeline, self).file_path(request, response, info)
category = request.item.get('category')
images_store = settings.IMAGES_STORE
# 创建图片分类文件夹
category_path = os.path.join(images_store, category)
if not os.path.exists(category_path):
os.mkdir(category_path)
# 重新返回新的图片保存路径
image_name = path.replace('full/', '')
image_path = os.path.join(category_path, image_name)
return image_path
setting.py
中的ITEM_PIPELINES
配置如下:ITEM_PIPELINES = {
# 'bmw.pipelines.BmwPipeline': 300,
# 'scrapy.pipelines.images.ImagesPipeline': 1
'bmw.pipelines.BmwImagesPipeline': 300,
}
之后运行代码即可。