ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【爬虫】【教程向】爬取壁纸网站Wallhaven收藏夹中的壁纸

2021-07-26 15:34:03  阅读:370  来源: 互联网

标签:self Wallhaven 收藏夹 path 壁纸 data id 下载


开头:

Wallhaven是一个壁纸网站,注册账号后可以将喜欢的壁纸添加至收藏夹中。

里面有许多质量非常高的壁纸,为了方便下载,于是花了两天多时间写了一个爬壁纸的脚本(蒟蒻落泪)


需求:

首先明确自己的需求:

代码的功能应包含:

  • 访问各个收藏夹,按收藏夹的分类下载图片
  • 下载的图片保存至对应收藏夹名的文件夹中
  • 下载图片应按照网站中给出的编号来命名
  • 含有记录下载的功能,避免未全部下载完后关闭程序,第二次打开时从头开始下载的悲剧
  • 下载过程中有适当的提示,例如:正在下载xx收藏夹中的图片;第x张图片已下载完成等等

准备工作:

接着做一些准备工作:

最开始抱着试一试的心态找找看有没有api,居然发现这个网站提供了详细的api说明:api说明页面

太良心了,感动

在api说明页面的最后面我们可以了解到:

通过这个链接:https://wallhaven.cc/api/v1/collections?apikey=<API KEY>(下文中称为1号链接)可以访问自己的所有收藏夹信息

再通过这个链接:https://wallhaven.cc/api/v1/collections/USERNAME/ID(下文中称为2号链接)可以访问某个收藏夹中所收藏的全部壁纸的详细信息

注:若某个收藏夹被你设置为私密,访问详情时只需要照猫画虎的在后面加上?apikey=<API KEY>便可以了。API KEY可以通过查看个人信息的设置页面找到。


思路:

以上是爬图片前做的准备工作,下面便开始构思思路了:

1号链接:

打开1号链接可以看见:

{

"data":

[

{"id":9xxxx8,"label":"Nxx","views":0,"public":0,"count":0},

{"id":9xxxx4,"label":"Xxxxx","views":4,"public":0,"count":126},

{"id":9xxxx7,"label":"Xxxxxxng","views":0,"public":0,"count":22},

{"id":9xxxx6,"label":"Xixxxxxxon","views":0,"public":0,"count":9},

{"id":9xxxx8,"label":"Nxxxxo","views":0,"public":1,"count":10},

{"id":9xxxx9,"label":"Nxxxxxian","views":0,"public":1,"count":33},

{"id":9xxxx7,"label":"Nxxxxx","views":2,"public":1,"count":47},

{"id":9xxxx0,"label":"Sxxxxn","views":2,"public":1,"count":24},

{"id":9xxxx2,"label":"Sxxxxxe","views":0,"public":1,"count":7}

]

}

注:直接访问得到的内容是没有换行了,为了方便展示我加了换行符,下面也是一样

得到的是一个只有一个键值对的字典,键名是data,值是一个列表,列表里又套了多个字典,字典中包含了各个收藏夹的详细信息,包括:收藏夹的id、名称等等,重要的就是id和名称这两项内容了。

2号链接:

接下来根据id访问2号链接:

{

"data":

[

{"id":"9mz31w",

--snip--

"path":"https:\/\/w.wallhaven.cc\/full\/9m\/wallhaven-9mz31w.png",

--snip--

}},

--snip--

{"id":"lqzp62",

--snip--

"path":"https:\/\/w.wallhaven.cc\/full\/lq\/wallhaven-lqzp62.jpg",

}}

],

"meta":{"current_page":1,"last_page":2,"per_page":24,"total":47}

}

得到的同样也是一个字典,有两个键,一个是data,另一个是meta,主要用的是data。第一个键data的值是一个列表,列表中又套了多个字典,分别包含了各张图片的详细信息,我们只需要里面path键的值就可以了。

 

看完了上面两个页面,再根据下载的需求便有了一个大致的思路:

首先访问1号链接,得到各个收藏夹的id与名称即label,接着根据得到的id,分别访问2号链接,得到图片的链接即path,访问path即可得到原图


代码详情:

有了思路便开始正式写代码:

首先导入所需要的运行库:

import requests
import os
import json
import re
from time import sleep

再定义一个Wallhaven_master类,再定义若干个属性:

def __init__(self):
        super(Wallhaven_master, self).__init__()
        self.url_user = 'https://wallhaven.cc/api/v1/collections?apikey=<API KEY>'#用的时候需要填写自己的api key
        self.headers = {
            "User-Agent": "",#内容可通过自己的浏览器查看,这里我删了
        }
        self.name = []  # 列表,储存每个收藏夹的label
        self.colle_id = []  # 列表,储存每个收藏夹的id
        self.pic_id = {}  # 字典,键名为收藏夹id,键值为列表,储存每个收藏夹下所有图片path
        self.DLed = []  # 列表,储存此次运行过程中已下载的图片path
        self.DLread = []  # 列表,储存从json文件从读取的已下载的图片path,用于排重
        self.DLtmp = []#列表,储存从json中读取的已下载链接

接下来写若干个函数(方法):

def get_data(self, a):
        '''
        打开api ,返回 data 字典
        '''
        r = self.get(a, headers=self.headers)
        req = r.json()
        data = req['data']
        return data
      
#备注:通过访问前面的两个链接可以发现有个巧合:都是个字典,且都有键data,里面都保存了我们需要的信息
#	   因为这个巧合,为了方面我们的访问,减少代码量便可以写这个函数


    def get_colle_name(self):
        '''
        获得每个收藏夹的名字 label
        '''
        data = self.get_data(self.url_user)
        for x in data:
            name = x['label']
            self.name.append(name)
            
#备注:将收藏夹的名称添加至列表self.name[]中


    def get_colle_id(self):
        """
            获取每个收藏夹的id
            在列表 pic_id 中添加 列表元素 colle_id
        """
        data = self.get_data(self.url_user)
        for x in data:
            colle = x['id']
            self.colle_id.append(colle)

    def get_pic_id(self):
        '''
        获得每个图片的 path
        将 path 添加至字典 self.pic_id 各个键的值(列表)中
        '''
        for x in self.colle_id:
            url = f"https://wallhaven.cc/api/v1/collections/NAME/{x}?apikey=<API KEY>"
            #备注:NAME是自己的用户名,api key前面有介绍
            #      这里我把自己的信息删了
            data = self.get_data(url)
            self.pic_id[x] = []
            for y in data:
                path = y['path']
                self.pic_id[x].append(path)

    def cret_floder(self):
        '''
        根据收藏夹名称创建文件夹
        '''
        if not os.path.exists('WH-pics'):
            os.mkdir("WH-pics")
        os.chdir('WH-pics')
        for x in self.name:
            if not os.path.exists(x):
                os.mkdir(x)

    def run(self):
        '''
        主程序:下载模块
        '''
        num = 0
        for x in self.pic_id.values():
            print(f'正在下载收藏夹 {self.name[num]} 中的图片')
            rest = len(self.pic_id[self.colle_id[num]])
            if not rest:
                print('此收藏夹中图片已全部下载完成')
            th = 1
            for y in x:
                name = y.split('/')[-1]
                r = self.get(y)
                #备注:此处删除了下载图片的代码
                self.DLed.append(y)
                self.loadjson()
                print(f"第 {th} 张图片已下载完成!")
                th += 1
                sleep(2.5)
            print('\n')
            num += 1
        print('所有收藏夹中的所有图片已全部下载完成!')

    def readjson(self):
        '''
        读取json文件中的已下载链接,若无则创建
        '''
        with open('DLed.json', 'a+') as dl:
            xyz = 0
        try:
            with open('DLed.json', 'r') as dl:
                self.DLread = json.load(dl)
        except ValueError:
            self.DLread = []

    def loadjson(self):
        '''
        将运行过程中下载的图片链接和从json文件中读取的已下载链接合并至一个列表中
        每下载一张图片,更新一次json文件,确保保存了下载记录,防止重复下载
        '''
        self.DLtmp = self.DLed + self.DLread
        with open('DLed.json', 'w+') as dl:
            json.dump(self.DLtmp, dl)

    def delrepeat(self):
        ''''
        删除重复的下载链接,避免重复下载
        '''
        for x in self.pic_id.values():
            for y in self.DLread:
                if y in x:
                    x.remove(y)

 以上便是程序的主要内容了,运行的话只需要创建一个类Wallhaven_master的实例,再依次使用上面写了的函数(方法)便可以了。

运行:

self.get_colle_id()
self.get_colle_name()
self.cret_floder()
self.get_pic_id()
self.readjson()
self.delrepeat()
self.run()
self.loadjson()

排重模块思路:

最后再说一下如何避免重复下载的思路:

创建一个json文件,里面用于存储之前已下载的图片的链接即path,

每次运行程序前读取里面的内容,在已获得的全部下载链接中,遍历删除读取的内容,之后再开始下载,

接着,每下载一张图片,便将该图片的链接添加至列表DLed中,

紧接着,合并列表DLed和列表DLread(即从json文件中读取的列表),接着在json文件中覆盖写入新列表,这样便确保了每下一张图片就可以把下载好的链接写入至json文件中来记录。

特别注意:

从官方的api介绍文档中得知:

API calls are currently limited to 45 per minute. If you do hit this limit, you will receive a 429 - Too many requests error.
Attempting to access a NSFW wallpaper without an API Key or with an invalid one will result in a 401 - Unauthorized error.
Any other attempts to use an invalid API key will result in a 401 - Unauthorized error.

每分钟请求数不得超过45次,所以使用sleep函数每下载一张图片便休息2s左右便可以完全满足需要


写在最后:

代码缺陷:

运行完代码后发现怎么每个收藏夹中下载的图片最多只有24张?!又看了api介绍文档后才发现,访问收藏夹详情只能得到前24张图片的详情。按理说在2号链接上加上?page=x是应该会解决的,但是我尝试了没有用,在网上查资料也没有找到解决办法,这个问题至今未解决。所以只能手动把收藏夹拆分成多个收藏夹,下载完后再手动合并,将就着用吧....蒟蒻落泪

其他:

自己一开始是想在网上找找有没有现成的直接拿来用,但是看了一圈发现都和我想要的没有半毛钱关系.....遂自己动手写了一个。

u1s1,在网上找现成的过程中,发现他们用的都是分析网页的方法,我就没看见一个利用官方api的,迷惑....

说实话写这个是我第一次写的一个“大” 程序,参考了一本python的入门介绍书,然后对着一个爬B站相册的代码照猫画虎的写出来了。

在写的过程中掉进了好多坑,比如说在使用列表元素的值的时候没有加索引,报错提示说:TypeError: unhashable type: 'list',看不懂,花了一晚上时间死活没想通.....蒟蒻落泪

以上

标签:self,Wallhaven,收藏夹,path,壁纸,data,id,下载
来源: https://www.cnblogs.com/ldaiy/p/15061649.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有