前言
既然想要爬取图片,那咱们就要爬一(成)张(百)两(上)张(千)的了(`・ω・´)。既然这么多的图片如果用传统的方式一张张进行下载,那得等到猴年马月👿。毫无意外Scrapy框架可以帮助实现,让我们事半功倍,下面就让我们实现吧!
分析
网上其他文章爬取的要么是美女、要么是猛男,咱们就没那么庸俗了。我们什么类型都要ヽ(°▽、°)ノ。我们要爬取的地址是美桌一个壁纸图片网站。如下图所示:
我们按照红框标题进行分类,用青色框的名字作为图片名。嗯······大概就是这个亚子:
接着我们来看一哈那个网页分析
各类详情
如图所示每一个分类都是一个div,每个div里面都有ul>li,我们要的信息毫无意外的就保存在这里(废话)。然后我们要提取的信息是<h2></h2>
中的分类标题;<img>
中的图片地址和<p></p>
中的图片标题。对于下面这种220 × 147 的小图片不是我们的目标。
我们要爬就爬大的!!
1920 × 1200 才是我们的需要,看上去护眼(手动滑稽)
代码实现
我们分析完了就开撸!
创建一个scrapy项目:scrapy startproject Photo
接着创建一个爬虫:scrapy genspider photo_spider win4000.com
项目已经创建,我们来到items.py来进行字段的定义。这里定义了三个:imgtitle、imgname、imgurl。然后我们来到刚刚新建的photo_spider.py爬虫文件中编写逻辑
import scrapy
from Photo.items import PhotoItem
class PhotoSpider(scrapy.Spider):
name = 'photo_spider'
allowed_domains = ['win4000.com/']
start_urls = ['http://www.win4000.com/wallpaper.html']
def parse(self, response):
div = response.css('div.list_cont')
for info in div:
title = info.css('div.tit h2::text').extract_first()
for li in info.css('ul li'):
name = li.css('p::text').extract_first()
next_url = li.css('a::attr(href)').extract_first()
yield scrapy.Request(next_url, callback=self.con_url, dont_filter=True,
meta={'title': title, 'name': name})
def con_url(self, response):
item = PhotoItem()
item['imgtitle'] = response.meta['title']
item['imgname'] = response.meta['name']
item['imgurl'] = response.css('.pic-meinv img::attr(src)').extract_first()
# print(item)
yield item
由于我们获取的是大图,所以爬虫是在两个页面之间来回穿梭的,我们保存的信息可以在第一个页面通过Request中meta传参的方式传递到下一个页面的回调函数。回调函数通过response.meta[key]
获取信息后,填充到item对象中,在信息都填充完毕之后再yield item
。回调函数如果不能够生效记得在Request对象中添加dont_filter=True参数,即url去重。
图片的下载还是要在管道中进行的,首先我们来讲讲概念。图片的下载在Scrapy内部是ImagesPipeline类控制的,而我们现在就是要继承ImagesPipeline类。虽然ImagesPipeline类是也是继承了FilesPipeline类,但是ImagesPipeline有自己的特色:过滤掉不符合自定义图片尺寸的(settings中设置),图片转换为RGB格式,默认存储格式是jpg…
那么可以在settings中设置的图片属性有:
IMAGES_STORE = 'D:\Image' # 图片保存地址,默认是IMAGES_STORE\full\名字.jpg
MIN_WIDTH = 220 # 图片的最小宽度
MIN_HEIGHT = 147 # 图片的最小高度
查看源码可以发现有两个函数是我们需要重写的:
# 第一个,通过获取的url进行下载
def get_media_requests(self, item, info): #
return [Request(x) for x in item.get(self.images_urls_field, [])]
# 第二个,设置下载路径,图片名字默认是哈希值(字母+数字=毫无逻辑(。_ 。))
def file_path(self, request, response=None, info=None):
image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
return 'full/%s.jpg' % (image_guid)
具体代码如下
from scrapy.pipelines.images import ImagesPipeline,FilesPipeline
from scrapy import Request
class PhotoPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
# Request meta传参:图片信息
yield Request(url=item['imgurl'],meta={'name':item['imgname'],'title':item['imgtitle']})
# 这里注意,如果item['imgurl']的value是字符串直接yild即可,列表需要循环!
def file_path(self, request, response=None, info=None):
title = request.meta['title'] # 获取图片的分类标题
name = request.meta['name'] # 获取图片的名字
filename = u'{0}/{1}.jpg'.format(title, name) # 根据源码,拼接地址
return filename
这里敲完之后去settings.py将设置IMAGES_STORE=保存地址,将ITEM_PIPELINES取消注释,后面的数值越小越优先。
执行 scrapy crawl photo_spider 就可以看到图片乖乖在我们定义的路径里了🤗