(1), 前言
Scrapy 框架为文件和图片的下载专门提供了两个 Item Pipeline 它们分别是:
- FilePipeline
- ImagesPipeline
(2), 使用 Scrapy 内置的下载方法的好处
1, 可以有效避免重复下载
2, 方便指定下载路径
3, 方便格式转换, 例如可以有效的将图片转换为 png 或 jpg
4, 方便生成缩略图
5, 方便调整图片大小
6, 异步下载, 高效率
(3), 较为传统的 Scrapy 框架图片下载方式
1, 创建项目: scrapy startproject baoma---cd baoma -- 创建爬虫 scrapy genspider spider car.autohome.com.cn
2, 使用 pycharm 打开项目
改写 settings.py
不遵守 robots 协议
设置请求头
开启 pipelines.py
改写 spider.py
- # -*- coding: utf-8 -*-
- import scrapy
- from ..items import BaomaItem
- class SpiderSpider(scrapy.Spider):
- name = 'spider'
- allowed_domains = ['car.autohome.com.cn']
- start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
- def parse(self, response):
- #SelecorList 类型
- uiboxs = response.xpath('//div[@class ="uibox"]')[1:] #第一个我们不需要
- for uibox in uiboxs:
- catagory = uibox.xpath('.//div[@class ="uibox-title"]/a/text()').get()
- urls = uibox.xpath('.//ul/li/a/img/@src').getall()
- #遍历列表, 并将列表中的某一项执行函数操作, 再将函数的返回值以列表的形式返回
- #map()
- # for url in urls:
- # # url = 'https:' + url
- # # print(url)
- # #方法二:
- # url = response.urljoin(url)
- # print(url)
- #方法三:
- #将列表中的每一项进行遍历传递给 lambda 表达式, 并执行函数中的代码, 再以返回值以列表形式进行返回, 结果是 map 对象, 接着使用 list 转换为列表
- urls = list(map(lambda url:response.urljoin(url),urls))
- item = BaomaItem(catagory = catagory,urls = urls)
- yield item
改写 pipelines.py
- # -*- 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
- import os
- from urllib import request
- class BaomaPipeline(object):
- def __init__(self):
- self.path = os.path.join(os.path.dirname(__file__), 'images') #os.path.dirname() 获取当前文件的路径, os.path.join() 获取当前目录并拼接成新目录
- if not os.path.exists(self.path): # 判断路径是否存在
- os.mkdir(self.path)
- def process_item(self, item, spider):
- #分类存储
- catagory = item['catagory']
- urls = item['urls']
- catagory_path = os.path.join(self.path,catagory)
- if not os.path.exists(catagory_path): #如果没有该路径即创建一个
- os.mkdir(catagory_path)
- for url in urls:
- image_name = url.split('_')[-1] #以_进行切割并取最后一个单元
- request.urlretrieve(url,os.path.join(catagory_path,image_name))
- return item
新建测试 py(main.py)
- #author: "xian"
- #date: 2018/6/14
- from scrapy import cmdline
- cmdline.execute('scrapy crawl spider'.split())
运行结果:(我们成功获取了以 catagory 分类并以图片地址_后的参数作为图片名的图片)
Scrapy 框架提供了两个中间件 1, 下载文件的 Files pipeline 和下载图片的 Image pipeline
下载文件的 Files pipeline
使用步骤:
1, 定义好一个 item, 然后定义两个属性 file_urls 和 files . file_urls 是用来存储需要下载的文件的 url 链接, 列表类型
2, 当文件下载完成后, 会把文件下载的相关信息存储到 item 的 files 属性中. 例如: 下载路径, 下载 url 和文件的效验码
3, 再配置文件 settings.py 中配置 FILES_STORE, 指定文件下载路径
4, 启动 pipeline, 在 ITEM_PIPELINES 中设置 scrapy.pipelines.files.FilesPipeline :1
下载图片的 Images Pipeline
使用步骤:
1, 定义好一个 item, 然后定义两个属性 image_urls 和 images. image_urls 是用来存储需要下载的文件的 url 链接, 列表类型
2, 当文件下载完成后, 会把文件下载的相关信息存储到 item 的 images 属性中. 例如: 下载路径, 下载 url 和文件的效验码
3, 再配置文件 settings.py 中配置 FILES_STORE, 指定文件下载路径
4, 启动 pipeline, 在 ITEM_PIPELINES 中设置 scrapy.pipelines.images.ImagesPipeline :1
(4), 使用 Images_pipeline 进行图片下载 (还是以汽车之家图片为例)
改写 settings.py
开启自己定义的中间件
改写 pipelines,py
- # -*- 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
- import os
- from urllib import request
- from scrapy.pipelines.images import ImagesPipeline
- import settings
- class BaomaPipeline(object):
- def __init__(self):
- self.path = os.path.join(os.path.dirname(__file__), 'images') #os.path.dirname() 获取当前文件的路径, os.path.join() 获取当前目录并拼接成新目录
- if not os.path.exists(self.path): # 判断路径是否存在
- os.mkdir(self.path)
- def process_item(self, item, spider):
- #分类存储
- catagory = item['catagory']
- urls = item['urls']
- catagory_path = os.path.join(self.path,catagory)
- if not os.path.exists(catagory_path): #如果没有该路径即创建一个
- os.mkdir(catagory_path)
- for url in urls:
- image_name = url.split('_')[-1] #以_进行切割并取最后一个单元
- request.urlretrieve(url,os.path.join(catagory_path,image_name))
- return item
- class BMWImagesPipeline(ImagesPipeline): # 继承 ImagesPipeline
- # 该方法在发送下载请求前调用, 本身就是发送下载请求的
- def get_media_requests(self, item, info):
- request_objects = super(BMWImagesPipeline, self).get_media_requests(item, info) # super() 直接调用父类对象
- for request_object in request_objects:
- request_object.item = item
- return request_objects
- def file_path(self, request, response=None, info=None):
- path = super(BMWImagesPipeline, self).file_path(request, response, info)
- # 该方法是在图片将要被存储时调用, 用于获取图片存储的路径
- catagory = request.item.get('catagory')
- images_stores = settings.IMAGES_STORE #拿到 IMAGES_STORE
- catagory_path = os.path.join(images_stores,catagory)
- if not os.path.exists(catagory_path): #判断文件名是否存在, 如果不存在创建文件
- os.mkdir(catagory_path)
- image_name = path.replace('full/','')
- image_path = os.path.join(catagory_path,image_name)
- return image_path
运行结果展示:
通过对比我们可以直观感受到下载速度明显提高.
下面我们对图片进行优化, 获取高清图片
通过分析缩略图和高清图的 url, 我们发现缩略图只是多了 t_罢了
缩略图地址: https://car3.autoimg.cn/cardfs/product/g24/M08/2F/9E/t_autohomecar__wKgHIVpogfqAIlTbAAUzcUgKoGY701.jpg
高清图地址: https://car3.autoimg.cn/cardfs/product/g24/M08/2F/9E/autohomecar__wKgHIVpogfqAIlTbAAUzcUgKoGY701.jpg
(5), 下面我们获取所有的高清图片
传统思路如下: 找到更多获取接口的 url, 进入详情页 -- 找分页接口 (显然这种情况会大大提高我们的工作量, 下面我们使用 Scrapy 框架中的 CrawlSpider 进行爬取, 因为 CrawlSpider 只要指定响应的规则, 爬虫会自动进行爬取, 省事省力!)
我们首先分析下 url 的规律:
- https://car.autohome.com.cn/pic/series/65-1.html(更多的第一页 url)
- https://car.autohome.com.cn/pic/series/65-1-p2.html(更多的第二页 url)
改写 spider.py
- # -*- coding: utf-8 -*-
- import scrapy
- from scrapy.spiders import CrawlSpider ,Rule# 导入 CrawlSpider 模块 需改写原来的 def parse(self,response) 方法
- from scrapy.linkextractor import LinkExtractor #导入链接提取模块
- from ..items import BaomaItem
- class SpiderSpider(CrawlSpider):
- name = 'spider'
- allowed_domains = ['car.autohome.com.cn']
- start_urls = ['https://car.autohome.com.cn/pic/series/65.html']
- rules = {
- Rule(LinkExtractor(allow=r'https://car.autohome.com.cn/pic/series/65.+'),callback= 'parse_page',follow=True),
- } #如需要进行页面解释则使用 callback 回调函数 因为有下一页, 所以我们需要跟进, 这里使用 follow 令其为 True
- def parse_page(self, response): #页面解析函数
- catagory = response.xpath('//div[@class ="uibox"]/div/text()').get()
- srcs = response.xpath('//div[contains(@class,"uibox-con")]/ul/li//img/@src').getall()
- srcs = list(map(lambda x:x.replace('t_',''),srcs)) #map(函数, 参数二), 将参数二中的每个都进行函数计算并返回一个列表
- # urls = {}
- # for src in srcs:
- # url = response.url.join(src)
- # urls.append(url)
- srcs = list(map(lambda x:response.urljoin(x),srcs))
- yield BaomaItem(catagory=catagory,image_urls = srcs)
运行结果 (展示):(我们成功获取了高清图片到本地)
来源: https://www.cnblogs.com/518894-lu/p/9181448.html