前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Scrapy爬取汽车之家的网站图片就是爽

用Scrapy爬取汽车之家的网站图片就是爽

作者头像
码农飞哥
发布2022-03-31 20:08:08
1K0
发布2022-03-31 20:08:08
举报
文章被收录于专栏:好好学习

前言

本文将介绍如何使用scrapy框架来快速爬取某网站汽车的图片,并将爬取到的图片保存到本地。

爬取的网址

https://car.autohome.com.cn/pic/series/66.html#pvareaid=2042194

创建scrapy项目

创建scrapy项目的命令在此不在赘述了,如果不清楚的小伙伴可以看下:Scrapy框架快速入门,以糗事百科为例进行说明【python爬虫入门进阶】(16) 通过如下命令创建了一个名为bba_img_demo的scrapy项目,并创建一个名为bba3的spider。

代码语言:javascript
复制
scrapy startproject bba_img_demo
cd bba_img_demo
scrapy genspider bba3 "car.autohome.com.cn"

爬取图片bba3Spider

这里还是采用xpath来爬取页面元素。在bba3Spider中爬取页面元素得到imgDemoItem,并返回给Pipelines。

代码语言:javascript
复制
class bba3Spider(scrapy.Spider):
    name = 'bba3'
    allowed_domains = ['car.autohome.com.cn']
    start_urls = ['https://car.autohome.com.cn/pic/series/66.html#pvareaid=2042214']

    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()
            # 获取图片链接
            org_urls = uibox.xpath(".//ul/li/a/img/@src").getall()
            urls = []
            for url in org_urls:
                # 将图片地址拼接上域名
                url = response.urljoin(url)
                urls.append(url)
            imgDemoItem = bbaImgDemoItem(category=category, urls=urls)
            yield imgDemoItem

这里需要说明的是org_urls获取到的结果是:

代码语言:javascript
复制
['//car2.autoimg.cn/cardfs/product/g27/M0B/8C/16/480x360_0_q95_c42_autohomecar__ChxkmWGegTyACi-kAC3FbBHMbU0705.jpg', '//car3.autoimg.cn/cardfs/product/g28/M04/89/E4/480x360_0_q95_c42_autohomecar__ChxkmmGegTuAMP6mABxWnAoC-D4144.jpg', '//car3.autoimg.cn/cardfs/product/g28/M03/89/E4/480x360_0_q95_c42_autohomecar__ChxkmmGegTmAcgvzABhIjSB7-9g758.jpg', '//car3.autoimg.cn/cardfs/product/g28/M00/89/E4/480x360_0_q95_c42_autohomecar__ChxkmmGegTmAERL1ACm2BVXLZ6Y692.jpg', '//car3.autoimg.cn/cardfs/product/g27/M04/8C/16/480x360_0_q95_c42_autohomecar__ChxkmWGegTiAXmudADZcJBeHRXE488.jpg', '//car3.autoimg.cn/cardfs/product/g27/M0A/AE/14/480x360_0_q95_c42_autohomecar__ChwFkWGegTeAU296ACGsSse0_UY335.jpg', '//car3.autoimg.cn/cardfs/product/g27/M01/8C/16/480x360_0_q95_c42_autohomecar__ChxkmWGegTaAJjHFACpeMVzBTpk896.jpg', '//car2.autoimg.cn/cardfs/product/g27/M01/AE/14/480x360_0_q95_c42_autohomecar__ChwFkWGegTWAGp1RACY4LcxwCrk373.jpg']

所以需要在爬取的图片地址前面拼接:https: ,这里既可以使用 url='https:'+url ,也可以使用response.urljoin(url)

保存图片bbaImgDemoPipeline

在bbaImgDemoPipeline中通过接收bba3Spider返回的imgDemoItem。并将图片保存到 bba_img_demo项目的images目录下。并且在images目录下以类别来保存各个类别下的图片数据。

代码语言:javascript
复制
import os
from urllib import request
import ssl
from urllib.request import HTTPSHandler

context = ssl._create_unverified_context()
https_handler = HTTPSHandler(context=context)
opener = request.build_opener(https_handler)
request.install_opener(opener)

class bbaImgDemoPipeline:
    def __init__(self):
        self.path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'image')
        if not os.path.exists(self.path):
            os.mkdir(self.path)

    def process_item(self, item, spider):
        category = item['category']
        urls = item['urls']
        category_path = os.path.join(self.path, category)
        if not os.path.exists(category_path):
            os.mkdir(category_path)
        for url in urls:
            # 通过__来分割得到文件名
            image_name = url.split('__')[-1]
            request.urlretrieve(url, os.path.join(category_path, image_name))
        return item

运行后的结果是:

这个方法可以实现我们想要的效果。但是,还不够优雅,没有用到多线程,不够简洁。 scrapy为下载item包含的文件(比如在爬取到产品时,同时也想保存对应的图片)提供了一个可重用的item pipelines。这些pipeline有共同的方法和结构(我们称之为media pipelines)。一般来说你会使用Files Pipeline或者Images Pipeline

使用scrapy内置的下载文件的方法有如下好处:

  1. 避免重新下载最近已经下载过的数据
  2. 可以方便的指定文件存储的路径。
  3. 可以将下载的图片转换成通用的格式,比如png或jpg。
  4. 可以方便的生成缩略图。
  5. 可以方便的检测图片的宽和高,确保他们满足最小限制。
  6. 异步下载,效率非常高。

Files Pipeline的使用步骤:

  1. 定义好一个Item,然后在这个item中定义两个属性,分别为file_url以及files。file_urls 是用来存储需要下载的文件的url链接的,需要的是一个列表。
  2. 当文件下载完成后,会把文件下载的相关信息存储到items中的files属性中,比如下载路径,下载的url和文件的校验码等。
  3. 在配置文件settings.py中配置FILES_STORE,这个配置是用来设置文件下载下来的路径。
  4. 启动pipeline:在ITEM_PIPLINES中设置'scrapy.pipelines.files.FilePipeline':1

Images Pipeline的使用步骤:

当使用Image Pipeline下载文件的时候,按照以下步骤来完成:

  1. 定义好一个Item,然后在这个item中定义两个属性,分别为image_urls以及imagesimage_url 是用来存储需要下载的图片的url链接,需要给一个列表。
  2. 当图片下载完成之后,会把图片下载的相关信息存储到item的imags属性中,比如下载路径,下载的url和图片的校验码等。
  3. 当配置文件settings.py 中配置IMAGES_STORE,这个配置是用来设置图片下载下来的路径的。
  4. 启动pipeline:在ITEM_PIPELINES中设置'scrapy.pipelines.images.ImagesPipeline':1

下面我们就使用Image Pipeline来实现下这个功能,详细的步骤如下:

1. 修改bbaImgDemoItem类
代码语言:javascript
复制
class bbaImgDemoItem(scrapy.Item):
    category = scrapy.Field()
    image_urls = scrapy.Field()
    images = scrapy.Field()

在bbaImgDemoItem类中定义image_urls和images两个属性。

2. 修改bba3Spider类,将下载的图片路径放到image_urls中。
代码语言:javascript
复制
class bba3Spider(scrapy.Spider):
    name = 'bba3'
    allowed_domains = ['car.autohome.com.cn']
    start_urls = ['https://car.autohome.com.cn/pic/series/66.html#pvareaid=2042214']

    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()
            # 获取图片链接
            org_urls = uibox.xpath(".//ul/li/a/img/@src").getall()
            urls = list(map(lambda url: response.urljoin(url), org_urls))
            imgDemoItem = bbaImgDemoItem(category=category, image_urls=urls)
            yield imgDemoItem
3. 修改settings.py

在settings.py文件配置IMAGES_STORE。指定图片的保存路径。

代码语言:javascript
复制
import os
IMAGES_STORE= os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
4. 指定启动的pipeline

将启动的pipeline指定为scrapy.pipelines.images.ImagesPipeline

代码语言:javascript
复制
ITEM_PIPELINES = {
   # 'bba_img_demo.pipelines.bbaImgDemoPipeline': 300,
    'scrapy.pipelines.images.ImagesPipeline':1
}

这样设置之后,原先的bbaImgDemoPipeline则不会被启动。因为只有一个pipeline,所以优先级可以随意设置。 经过这四步设置基本上差不多了。但是,如果此时你直接运行的话,大概率得不到想要的效果。这是因为ImagesPipeline类在初始化时需要引入PIL包。如果没有安装的话则会直接报错。

5. 安装Pillow库
代码语言:javascript
复制
pip install Pillow

经过上面五步之后就可以正确的运行了。运行之后的结果是:

可以看出所有的图片都被保存到了images目录下的full文件夹下了。这显然也不是我们期望的结果。所以我们还是需要继承 ImagesPipeline类,然后,重写其保存逻辑。查看ImagesPipeline源代码可以得知将图片保存到full文件夹的方法是file_path。所以我们只需要重写这个方法返回我们想要的路径即可。

指定我们需要保存的路径

在pipelines.py 文件中自定义一个名为bbaImagesPipeline的类,让该类继承自ImagesPipeline。

  1. 重写get_media_requests方法 get_media_requests方法在发送下载请求之前调用,该方法主要的作用是拿到image_urls中的图片链接,并拼接成下载请求。 ImagesPipeline类的get_media_requests方法,可以看出返回的是一个Request对象的列表。
代码语言:javascript
复制
    def get_media_requests(self, item, info):
        urls = ItemAdapter(item).get(self.images_urls_field, [])
        return [Request(u) for u in urls]

重写后的get_media_requests方法。首先调用父类的image_urls方法,然后将item设置到request_obj中。

代码语言:javascript
复制
 def get_media_requests(self, item, info):
        request_objs = super(bbaImagesPipeline, self).get_media_requests(item, info)
        for request_obj in request_objs:
            request_obj.item = item
        return request_objs

2. 重写file_path方法 父类的file_path方法主要就两步,第一步是将图片链接的地址做hash运算得到图片的名称,接着返回图片存储的相对路径 full/{image_guid}.jpg

代码语言:javascript
复制
    def file_path(self, request, response=None, info=None, *, item=None):
        image_guid = hashlib.sha1(to_bytes(request.url)).hexdigest()
        return f'full/{image_guid}.jpg'

所以重写file_path方法只需要将返回的相对路径替换成我们期望的相对路径。

代码语言:javascript
复制
    def file_path(self, request, response=None, info=None, *, item=None):
        path = super(bbaImagesPipeline, self).file_path(request, response, info)
        category = request.item.get('category')
        img_name = path.replace('full/', '')
        image_path = os.path.join(category, img_name)
        return image_path

调用父类的file_path方法得到返回的full/{image_guid}.jpg。接着获取item中的category属性。 然后就是将full/ 替换掉就得到了图片名称。最后就是将分类和图片名称拼接成一个相对路径返回。

爬取高清图片(多个网页同时爬取)

1. 分析链接特点

  1. 车身外观的地址: https://car.autohome.com.cn/pic/series/66-1-p2.html
  2. 中控方向盘的地址:https://car.autohome.com.cn/pic/series/66-10.html#pvareaid=2042223 简单分析下可以得出链接中 https://car.autohome.com.cn/pic/series/66 这部分是完全一样的,后面的部分的可以匹配任意字符。 所以匹配链接地址的正则表达式是https://car.autohome.com.cn/pic/series/66.+

2. 编写爬虫代码

这里自定义了一个名为bba3Spider类,该类继承自CrawlSpider。

代码语言:javascript
复制
   # 定义好爬取策略
    rules = (
        Rule(LinkExtractor(allow=r"https://car.autohome.com.cn/pic/series/66.+"), callback="parse_page", follow=True),
    )

callback指定调用的回调函数是parse_page方法。当点击下一页时则继续往下走。 回调方法parse_page,该方法爬取分类和图片的地址。

代码语言:javascript
复制
  def parse_page(self, response):
        category = response.xpath("//div[@class='uibox']/div/text()").get()
        srcs = response.xpath('//div[contains(@class,"uibox-con")]/ul/li/a/img/@src').getall()
        urls = list(map(lambda x: response.urljoin(x), srcs))
        yield bbaImgDemoItem(category=category, image_urls=urls)

总结

本文通过以某网站为例说明了如何利用scrapy框架来高效的爬取网站中的图片。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农飞哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 爬取的网址
  • https://car.autohome.com.cn/pic/series/66.html#pvareaid=2042194
  • 创建scrapy项目
  • 爬取图片bba3Spider
  • 保存图片bbaImgDemoPipeline
    • 使用scrapy内置的下载文件的方法有如下好处:
      • Files Pipeline的使用步骤:
        • Images Pipeline的使用步骤:
          • 1. 修改bbaImgDemoItem类
          • 2. 修改bba3Spider类,将下载的图片路径放到image_urls中。
          • 3. 修改settings.py
          • 4. 指定启动的pipeline
          • 5. 安装Pillow库
        • 指定我们需要保存的路径
        • 爬取高清图片(多个网页同时爬取)
          • 1. 分析链接特点
            • 2. 编写爬虫代码
            • 总结
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档