├── requirements.txt ├── welcome.gif ├── welcome1.gif ├── tk_window_Spier.cpython-37.pyc ├── README.md ├── img2char.py └── wallhaven_cc.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests~=2.26.0 2 | Pillow~=8.3.2 3 | -------------------------------------------------------------------------------- /welcome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfhzzdx/wallhaven/HEAD/welcome.gif -------------------------------------------------------------------------------- /welcome1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfhzzdx/wallhaven/HEAD/welcome1.gif -------------------------------------------------------------------------------- /tk_window_Spier.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfhzzdx/wallhaven/HEAD/tk_window_Spier.cpython-37.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wallhaven 2 | 壁纸网站爬虫 3 | 4 | ##### 环境 python3.7.x 5 | 1. 安装依赖 6 | ```shell 7 | pip install -r requirements.txt 8 | ``` 9 | 2. wallhaven_cc.py 10 | > 为wallhaven的工具类,参考 [WallhavenApi](https://github.com/Goblenus/WallhavenApi) 11 | 3. __pychache__/tk_window_Spier.cpython-37.pyc 12 | > 为可执行的二进制文件,直接python {path_to_pyc}执行该文件 13 | > 需要将welcome.gif和该二进制文件及wallhaven工具类(wallhaven_cc)放在同一文件夹下 14 | ![image](https://user-images.githubusercontent.com/44967393/163918247-93cf59ff-7149-403f-b64b-618446468cbc.png) 15 | 16 | 4. 图形化界面如下: 17 | 18 | ![image](https://user-images.githubusercontent.com/44967393/163918298-72e9ded4-45a4-44bc-996a-f0f294b7e290.png) 19 | 20 | 21 | 点击查看apiKey,浏览器自动跳转wallhaven登录页面 22 | 23 | ![image](https://user-images.githubusercontent.com/44967393/163918409-fbfa2c88-1a41-4563-a4dc-07e5cbb137b1.png) 24 | 25 | 26 | 介绍: 27 | 28 | ![image](https://user-images.githubusercontent.com/44967393/163918573-9663db0d-a4f1-49ae-b358-281a7515751c.png) 29 | 30 | 31 | 32 | 33 | 34 | 等鄙人有闲暇时间了,再进行优化 35 | 36 | 待优化项目: 37 | 38 | > 1. 加入搜索框 39 | > 2. 更改搜索类别和级别为复选框 40 | > 3. 下载图片日志输出到UI界面 41 | 42 | 43 | -------------------------------------------------------------------------------- /img2char.py: -------------------------------------------------------------------------------- 1 | # -*- encoding : utf-8 -*- 2 | """ 3 | @Time : 2022年04月15日 17:09 4 | @Contact : hfhzzdx@hotmail.com 5 | @File : img2char.py 6 | @SoftWare : PyCharm 7 | @Modify Time @Author @Version @Desciption 8 | ------------ ------- -------- ----------- 9 | 2022-04-15 17:09 hfh 1.0 None 10 | """ 11 | 12 | from PIL import Image as img 13 | import os 14 | ascii_char = list(".$@B%8&2M#*oahkbdpqwmzO0QLCJUYXvZcunxrjft/\|()1{}[]?-_+~<>i!1I;:,\"^`'") 15 | 16 | 17 | def get_char(r, g, b, alpha=256): 18 | if alpha == 0: 19 | return ' ' 20 | length = len(ascii_char) 21 | gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) 22 | unit = (256.0 + 1) / length 23 | return ascii_char[int(gray / unit)] 24 | 25 | 26 | if __name__ == '__main__': 27 | file_list = [] 28 | rootpath = '' 29 | img_path_list = [] 30 | file_path = input("请输入要预览的图片文件夹路径(图片文件的上一级目录)\r\n") 31 | for root, dirs, files in os.walk(file_path): 32 | file_list = files 33 | rootpath = root 34 | for filepath in file_list: 35 | img_path_list.append(rootpath+'/'+filepath) 36 | for file in img_path_list: 37 | # img_path = input("请输入图片地址\r\n") 38 | # WIDTH = 150 39 | # HEIGHT = 40 40 | im = img.open(file) 41 | print(im.size) 42 | print("图片的宽为{}px,高为{}px".format(str(im.size[0]), str(im.size[1]))) 43 | HEIGHT = int(input("请输入要输出的像素高度\r\n")) 44 | WIDTH = int(HEIGHT * (im.size[0] / im.size[1]) * 3) 45 | im = im.resize((WIDTH,HEIGHT), img.NEAREST) 46 | text = '' 47 | for i in range(HEIGHT): 48 | for j in range(WIDTH): 49 | text += get_char(*im.getpixel((j, i))) 50 | text += '\n' 51 | print(text) 52 | -------------------------------------------------------------------------------- /wallhaven_cc.py: -------------------------------------------------------------------------------- 1 | # -*- encoding : utf-8 -*- 2 | import logging 3 | import requests 4 | import os 5 | from enum import Enum 6 | import random 7 | import string 8 | import time 9 | 10 | 11 | class Purity(Enum): 12 | sfw = "sfw" 13 | sketchy = "sketchy" 14 | nsfw = "nsfw" 15 | 16 | 17 | class Category(Enum): 18 | general = "general" 19 | anime = "anime" 20 | people = "people" 21 | 22 | 23 | class Sorting(Enum): 24 | date_added = "date_added" 25 | relevance = "relevance" 26 | random = "random" 27 | views = "views" 28 | favorites = "favorites" 29 | toplist = "toplist" 30 | hot = "hot" 31 | 32 | 33 | class Order(Enum): 34 | # desc used by default 35 | desc = "desc" 36 | asc = "asc" 37 | 38 | 39 | class TopRange(Enum): 40 | one_day = "1d" 41 | three_days = "3d" 42 | one_week = "1w" 43 | one_month = "1M" 44 | three_months = "3M" 45 | six_months = "6M" 46 | one_year = "1y" 47 | 48 | 49 | class Color(Enum): 50 | # Color names from http://chir.ag/projects/name-that-color 51 | lonestar = "660000" 52 | red_berry = "990000" 53 | guardsman_red = "cc0000" 54 | persian_red = "cc3333" 55 | french_rose = "ea4c88" 56 | plum = "993399" 57 | royal_purple = "663399" 58 | sapphire = "333399" 59 | science_blue = "0066cc" 60 | pacific_blue = "0099cc" 61 | downy = "66cccc" 62 | atlantis = "77cc33" 63 | limeade = "669900" 64 | verdun_green = "336600" 65 | verdun_green_2 = "666600" 66 | olive = "999900" 67 | earls_green = "cccc33" 68 | yellow = "ffff00" 69 | sunglow = "ffcc33" 70 | orange_peel = "ff9900" 71 | blaze_orange = "ff6600" 72 | tuscany = "cc6633" 73 | potters_clay = "996633" 74 | nutmeg_wood_finish = "663300" 75 | black = "000000" 76 | dusty_gray = "999999" 77 | silver = "cccccc" 78 | white = "ffffff" 79 | gun_powder = "424153" 80 | 81 | 82 | class Type(Enum): 83 | jpeg = "jpeg" 84 | jpg = "jpg" # the same as jpeg 85 | png = "png" 86 | 87 | 88 | class Seed(object): 89 | @staticmethod 90 | def generate(): 91 | # [a-zA-Z0-9]{6} 92 | return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(6)) 93 | 94 | 95 | class RequestsLimitError(Exception): 96 | def __init__(self): 97 | super().__init__("You have exceeded requests limit. Please try later.") 98 | 99 | 100 | class ApiKeyError(Exception): 101 | def __init__(self): 102 | super().__init__("Bad api key. Check it please.") 103 | 104 | 105 | class UnhandledException(Exception): 106 | def __init__(self): 107 | super().__init__("Somthing went wrong. Please submit this issue to " 108 | "https://github.com/Goblenus/WallhavenApi/issues.") 109 | 110 | 111 | class NoWallpaperError(Exception): 112 | def __init__(self, wallpaper_id): 113 | super().__init__("No wallpaper with id {}".format(wallpaper_id)) 114 | 115 | 116 | class WallhavenApiV1: 117 | def __init__(self, api_key=None, verify_connection=True, base_url="https://wallhaven.cc/api/v1", 118 | timeout=(2,5), requestslimit_timeout=None, proxies={}): 119 | self.verify_connection = verify_connection 120 | self.api_key = api_key 121 | self.base_url = base_url 122 | self.timeout = timeout 123 | self.requestslimit_timeout = requestslimit_timeout 124 | self.proxies = proxies 125 | 126 | def _request(self, to_json, **kwargs): 127 | for i in range(self.requestslimit_timeout[0] if self.requestslimit_timeout is not None else 1): 128 | if self.api_key is not None: 129 | if "params" in kwargs: 130 | kwargs["params"]["apikey"] = self.api_key 131 | else: 132 | kwargs["params"] = {"apikey": self.api_key} 133 | 134 | if "timeout" not in kwargs: 135 | kwargs["timeout"] = self.timeout 136 | 137 | if "verify" not in kwargs: 138 | kwargs["verify"] = self.verify_connection 139 | 140 | if "proxies" not in kwargs: 141 | kwargs["proxies"] = self.proxies 142 | 143 | response = requests.request(**kwargs) 144 | 145 | if response.status_code == 429: 146 | if self.requestslimit_timeout is None \ 147 | or i == (self.requestslimit_timeout[0] - 1) if self.requestslimit_timeout is not None else 0: 148 | raise RequestsLimitError 149 | 150 | time.sleep(self.requestslimit_timeout[1]) 151 | continue 152 | 153 | if response.status_code == 401: 154 | raise ApiKeyError 155 | 156 | # if response.status_code != 200: 157 | # raise UnhandledException 158 | 159 | if to_json: 160 | try: 161 | return response.json() 162 | except: 163 | raise UnhandledException 164 | print("wallhaven_cc _request",end='\t') 165 | print(response) 166 | return response 167 | 168 | def _url_format(self, *args): 169 | url = self.base_url 170 | url += "/" if not url.endswith("/") else "" 171 | return url + "/".join((str(x) for x in args)) 172 | 173 | @staticmethod 174 | def _category_(general=True, anime=True, people=False): 175 | return "{}{}{}".format(int(general), int(anime), int(people)) 176 | 177 | @staticmethod 178 | def _category(categories): 179 | if categories == 'general': 180 | return '100' 181 | elif categories == 'anime': 182 | return '010' 183 | elif categories == 'people': 184 | return '001' 185 | else: 186 | return '111' 187 | 188 | @staticmethod 189 | def _purity_(sfw=True, sketchy=True, nsfw=False): 190 | 191 | return "{}{}{}".format(int(sfw), int(sketchy), int(nsfw)) 192 | 193 | @staticmethod 194 | def _purity(purities): 195 | purities_list = str(purities).split(".") 196 | if purities_list[0] == 'Purity': 197 | if purities_list[1] == 'sfw': 198 | return '100' 199 | elif purities_list[1] == 'sketchy': 200 | return '010' 201 | elif purities_list[1] == 'nsfw': 202 | return '001' 203 | else: 204 | return '000' 205 | else: 206 | return '000' 207 | 208 | def search(self, q=None, categories=None, purities=None, sorting=None, order=None, top_range=None, atleast=None, 209 | resolutions=None, ratios=None, colors=None, page=None, seed=None): 210 | params = {} 211 | if q is not None: 212 | params["q"] = q 213 | if categories is not None: 214 | # categories = categories if type(categories) is list else [categories] 215 | # params["categories"] = self._category(Category.general in categories, Category.anime in categories, 216 | # Category.people in categories) 217 | params["categories"] = self._category(categories) 218 | 219 | if purities is not None: 220 | # purities = purities if type(purities) is list else [purities] 221 | # params["purity"] = self._purity(Purity.sfw in purities, Purity.sketchy in purities, 222 | # Purity.nsfw in purities) 223 | params["purity"] = self._purity(purities) 224 | 225 | if sorting is not None: 226 | params["sorting"] = sorting.value 227 | 228 | if order is not None: 229 | params["order"] = order.value 230 | 231 | if top_range is not None: 232 | params["topRange"] = top_range.value 233 | 234 | if atleast is not None: 235 | params["atleast"] = "{}x{}".format(atleast[0], atleast[1]) 236 | 237 | if resolutions is not None: 238 | params["resolutions"] = ",".join(["{}x{}".format(x[0], x[1]) \ 239 | for x in (resolutions if type(resolutions) is list else [resolutions])]) 240 | 241 | if ratios is not None: 242 | params["ratios"] = ",".join(["{}x{}".format(x[0], x[1]) \ 243 | for x in (ratios if type(ratios) is list else [ratios])]) 244 | 245 | if colors is not None: 246 | params["colors"] = colors.value 247 | 248 | if page is not None: 249 | params["page"] = str(page) 250 | 251 | if seed is not None: 252 | params["seed"] = seed 253 | return self._request(True, method="get", url=self._url_format("search"), params=params) 254 | 255 | def wallpaper(self, wallpaper_id): 256 | return self._request(True, method="get", url=self._url_format("w", wallpaper_id)) 257 | 258 | def is_walpaper_exists(self, wallpaper_id): 259 | return "error" not in self.wallpaper(wallpaper_id) 260 | 261 | def download_walpaper(self, *args, **kwargs): 262 | logging.warning('Please use "download_wallpaper" method instead "download_walpaper"') 263 | return self.download_wallpaper(*args, **kwargs) 264 | 265 | def download_wallpaper(self, wallpaper_id, file_path, chunk_size=4096): 266 | wallpaper_data = self.wallpaper(wallpaper_id) 267 | 268 | if "error" in wallpaper_data: 269 | raise NoWallpaperError(wallpaper_id) 270 | 271 | wallpaper = requests.get(wallpaper_data["data"]["path"], stream=True, timeout=self.timeout, 272 | verify=self.verify_connection) 273 | 274 | # if wallpaper.status_code != 200: 275 | # raise UnhandledException 276 | 277 | if file_path is not None: 278 | save_path = os.path.abspath(file_path) 279 | save_directory_path = os.path.dirname(save_path) 280 | 281 | if not os.path.exists(save_directory_path): 282 | os.makedirs(save_directory_path) 283 | 284 | with open(save_path, "wb") as image_file: 285 | for chunk in wallpaper.iter_content(chunk_size): 286 | image_file.write(chunk) 287 | 288 | return save_path 289 | 290 | return wallpaper.content 291 | 292 | def tag(self, tag_id): 293 | return self._request(True, method="get", url=self._url_format("tag", tag_id)) 294 | 295 | def settings(self): 296 | return None if self.api_key is None else self._request(True, method="get", url=self._url_format("settings")) 297 | 298 | def collections(self, user_name): 299 | return self._request(True, method="get", url=self._url_format(f"collections/{user_name}")) 300 | 301 | def collection_wallpapers(self, user_name, collection_id, page=None): 302 | return self._request(True, method="get", url=self._url_format(f"collections/{user_name}/{collection_id}"), 303 | params={"page": str(page)} if page is not None else {}) 304 | 305 | def my_collections(self): 306 | return None if self.api_key is None else self._request(True, method="get", url=self._url_format(f"collections")) 307 | 308 | 309 | if __name__ == '__main__': 310 | pass 311 | --------------------------------------------------------------------------------