• Scrapy爬虫——壁纸爬虫


    说在前面:

    今天把之前写过的壁纸爬虫拿出来用了一下,发现是很久以前写过的,用的还是python基本库urllib去做网络请求的,正好最近在学scrapy,于是就用scrapy框架重新写了一遍。


    环境要求:

    python:3.6
    Scrapy:1.51


    正式开始:

    感谢壁纸来源Wallhaven

    网页分析
    这里写图片描述
    通过分析可以得知,只需要给https://alpha.wallhaven.cc/latest?page=后加上数字即可获取到该页数的图片的url,并且可以知道图片的url是由https://alpha.wallhaven.cc/wallpaper/加数字组成
    这里写图片描述
    进入到图片url后,通过网页源码可以定位到图片的信息,通过scrapy的xpath选择器可以轻松的获取图片的真实url
    到这里,对网页分析完毕,开始代码实现。

    实现爬虫
    首先创建scrapy项目,在items文件中添加本次项目需要的item,其中type是照片的格式,在存储的时候会用到

    class WallhavenSpdierItem(scrapy.Item):
        # define the fields for your item here like:
        name = scrapy.Field()
        url = scrapy.Field()
        type = scrapy.Field()

    在spiders文件中新建spider.py文件。
    scrapy框架中有Spider类、CrawlSpider类以及其他的爬虫类可以使用,我这里使用的是CrawlSpider,通过与Rule配合使用,可以实现全站的爬取。

    class Spider(CrawlSpider):
        name = 'wallhaven'
        allowed_domains = ['alpha.wallhaven.cc']
        bash_url = 'https://alpha.wallhaven.cc/latest?page='
    
        def start_requests(self):
            for i in range(1, PAGE_NUMBER+1):
                yield Request(self.bash_url + str(i))

    PAGE_NUMBER在新增的config配置文件中,代表着爬取的页数。
    start_requests使用爬取页数构造了所有需要爬取页码url,然后通过Request发出请求。

    rules = (
            Rule(LinkExtractor(allow=('https://alpha.wallhaven.cc/wallpaper/d{1,6}',)), callback='parse_item'),
        )

    不清楚Rule和LinkExtractor,请移步官方文档查看。
    上一步网页分析时说过,图片的url的组成,所以使用在基础url后加上d{1,6}$来提取出图片的url,之后传给回调函数做进一步处理。

        def parse_item(self, response):
            item = WallhavenSpdierItem()
            tags = response.xpath('//*[@id="tags"]/child::li/a/text()').extract()
            ban_tags = BAN_TAGS
            for tag in ban_tags:
                if tag in tags:
                    return
            item['url'] = str('https:' + response.xpath('//*[@id="wallpaper"]/@src').extract_first(default=0))
            item['name'] = item['url'].split('/')[-1].split('.')[0]
            item['type'] = item['url'].split('/')[-1].split('.')[1]
            yield item

    对xpath不清楚,请移步w3cschool
    创建item对象,然后通过xpath选择器选出你需要的部分,加入到item中,最后返回item给下载器即可。
    注意
    我还提取了图片的标签,就是tags。因为我发现爬下的很多壁纸都是人物写真等不适合当作壁纸的图片,所以我选择过滤了那些带有标签的图片。BAN_TAGS同样在config配置文件中。

    实现下载管道
    scrapy可以通过ImagesPipeline类来实现图片下载管道,官网中也有简单的例子。例子中使用了item_completed和get_media_requests两个方法来实现下载。
    item_completed方法是检测item中是否存在图片url
    get_media_requests方法是提取出item中url再进行下载

    class WallhavenSpdierPipeline(ImagesPipeline):
        def get_media_requests(self, item, info):
            """
            :param item: spider.py中返回的item
            :param info:
            :return:
            """
            img_url = item['url']
            if img_url == 0:
                return 
            yield Request(img_url, meta={'item': item})

    在这里,我直接使用了get_media_requests方法,在方法内部对url进行检验,然后对图片真实的url发出了最后请求。

        def file_path(self, request, response=None, info=None):
            item = request.meta['item']
            folder = item['name']
            type = item['type']
            filename = u'full/{0}.{1}'.format(folder, type)
            return filename

    同时,还使用了file_path方法,使用item中的name和type构建了文件名,完成下载。
    注意:
    如果只是写好了这些,就去运行爬虫,并不能下载图片到本地,还需要在settings文件中做最后的配置。

    IMAGES_EXPIRES = 30  # 30天内不下载重复文件 如果不设置默认90天
    IMAGES_STORE = 'E:p\'  # 图片保存的路径
    IMAGES_THUMBS = {    #  图片缩略图
        'small': (50, 50),
        'big': (270, 270)
    }

    如果设置了缩略图,那么会在图片路径下,生成thumbs文件夹,其中有big和small两个文件夹。

    修改:
    经过检查,我发现直接使用了item_completed和get_media_requests两个方法来下载图片,会压缩图片的质量,比如之前大小为2.6mb大小的壁纸,直接压缩到160kb,这样极其影响当作壁纸的使用体验,所以我有下面两个想法进行改进:

    • 使用自定义管道
    • 重写父类方法

    上面两种方法我都进行了实现,第一种自定义管道如下:

    class WallhavenSpdierPipeline(object):
        def process_item(self, item, spider):
            url = item['url']
            name = item['name']
            type = item['type']
            filepath = IMAGES_STORE + '/full/{0}.{1}'.format(name, type)
            agent = random.choice(agents)
            headers = {
                'User-Agent': agent
            }
            fp = open(filepath, 'wb')
            image = requests.get(url, headers=headers).content
            fp.write(image)
            fp.close()

    直接使用requests请求url,然后直接写入文件。但是因为请求url需要等待一个响应的完成,又加上对于网址的连接本身效率就比较低,于是我又研究了下ImagesPipeline的源码,进行了第二次修改:

        def get_images(self, response, request, info):
            path = IMAGES_STORE + self.file_path(request, response=response, info=info)
            orig_image = response.body
            fp = open(path, 'wb')
            fp.write(orig_image)
            fp.close()
            return None, None, None

    因为scrapy本身是异步爬虫,而且在方法中已经对图片的url请求好,返回了response,那么使用起来,效率最高。就是在父类中,有其他方法调用该方法,所以会报错误,但是在父类中处理下异常即可。

    可能会有些更好的方法,希望大家可以评论在下面。


    最后

    源码放在Github上了,希望喜欢或者觉得有用的朋友点个star或者follow。
    有任何问题可以在下面评论或者通过私信或联系方式找我。
    联系方式
    QQ:791034063
    Wechat:liuyuhang791034063
    CSDN:https://blog.csdn.net/Sun_White_Boy
    Github:https://github.com/liuyuhang791034063

  • 相关阅读:
    git之clone
    gulp之sass 监听文件,自动编译
    cat命令
    centos安装yum源
    centos下wget: command not found的解决方法
    centos安装
    为什么很多公司招聘前端开发要求有 Linux / Unix 下的开发经验?
    ASP.NET MVC HtmlHelper用法集锦
    Html.Listbox的用法(实例)
    JSON入门实例
  • 原文地址:https://www.cnblogs.com/GF66/p/9785474.html
Copyright © 2020-2023  润新知