之前看到论坛上已经有人发过这个壁纸站的爬虫,甚至还做了用户界面。所以我这个帖就不注重于代码了,而是注重于技术上面。
目标网址:wallhaven.cc
工具需求
第三方库:requests BeautifulSoup
展示操作环境:macOS Big Sur + Chrome + Python3.8
目标剖析 & 过程拆解
壁纸网站的爬虫和平常的爬虫其实没有什么区别,有些地方甚至还更简单。因为只要是你浏览器上显示的东西,爬虫都可以爬下来,壁纸网站的话就更好办了。
老规矩先看看F12,network刷新一下,看见img里面全是图片啊,这不就完事了?但是先观察一下,这些图片画质没有原图那么清晰,还不完整,都被切割成了300x200的缩略图。但是能发现,这些图片的名字有些奇怪,都是六位的字母+数字,回到HTML里面看一下,发现每个子页面的链接都是wallhaven.cc/w/ + 刚才缩略图的六位字母+数字构成的,还是传统的下载站布局。那就好说了。
wallhaven壁纸网站说白了还是个下载站的框架,和之前的套路一样,先从大的展示主页里面获取子页面的链接,再从子页面里面下载。这里下载图片需要用到写入文件的知识。
开始堆代码
观察下网页,我决定从toplist里面爬取壁纸,网站筛选菜单里有几个选项可以选,分别是General/Anime/People,生草翻译一下就是常规/二次元/三次元,看个人喜好自行选择;还有两个就是SFW/Sketchy,第一个就是safe for work,工作看很安全,嗯,那第二个就是工作时看会出事(确信
正常的toplist网址是这样的:https://wallhaven.cc/toplist
修改完几个选项之后就变成了这样:https://wallhaven.cc/search?categories=111&purity=110&topRange=1M&sorting=toplist&order=desc
但是把选项改回去和正常的选项一样,他的网址却回不去了:https://wallhaven.cc/search?categories=110&purity=100&topRange=1M&sorting=toplist&order=desc
这里面categories和purity的值发生了变化,多试几次发现分别和上面的几个选项有关。
选项 |
categories |
purity |
General SFW |
100 |
100 |
General Anime SFW |
110 |
100 |
Anime SFW |
010 |
100 |
Anime People SFW |
011 |
100 |
到这里可以确定categories每位都受一个前面的选项影响,选择就是1,不选择就是0。现在试一下purity。
选项 |
categories |
purity |
General SFW |
100 |
100 |
General Sketchy |
110 |
010 |
General SFW Sketchy |
100 |
110 |
写到这里我已经流鼻血了,网站链接就说这么多了,现在开始爬!
为了方便演示,源码中出现的网址为toplist的标准短链接形式,自定义爬虫时可替换,不会影响代码执行。
先request请求下来看看吧,这里用最普通的get请求就可以了,别忘了做个假的UA放到header里面。
import requests
headers = {'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F'}
# 随便写的
html = "https://wallhaven.cc/toplist"
requests_html = requests.get(html, headers=headers)
print(requests_html.text)
HTML代码已经到手,很简单,现在该考虑解析了。F12里面看下我们需要的东西在哪里,不要被预览图所在的位置所迷惑,找到我们需要的链接在哪里就可以了。 <a class="preview">
拿出我们的老朋友BeautifulSoup,提取<a class="preview">
的href值。
Soup_all = BeautifulSoup(requests_html.text, 'lxml').find_all("a", class_="preview")
for Soup in Soup_all:
print(Soup['href'])
完事,换个方式把这些链接存到list里面,方便一会儿挨个爬取。
url_list = []
for Soup in BeautifulSoup(requests_html.text, 'lxml').find_all("a", class_="preview"):
url_list.append(Soup['href'])
下面来看看单页。
更简单,图片链接直接在<img id="wallpaper">
的src属性里面了,没有任何隐瞒。直接写代码。
num = 0
for link in url_list:
requests_html = requests.get(link, headers=headers)
bs_html = BeautifulSoup(requests_html.text, "lxml")
img = bs_html.find('img', id='wallpaper')
r = requests.get(img['src'])
num += 1
with open("/Users/artcgb/Downloads/壁纸/+ str(num) + ".jpg", 'wb') as f:
f.write(r.content)
用上Python的读写文件操作,直接就可以写入图片,十分方便全程毫无尿点。缺点就是如果文件名一样,新鞋入的图片会覆盖之前的,所以我优化了下代码,让他每天爬的时候都新建一个文件夹存储,这样就不会覆盖掉了。
源代码
import requests
from bs4 import BeautifulSoup
import os
import datetime
now = str(datetime.datetime.today().date())
# 获取当前日期
headers = {'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F'}
num = 0
url_list = []
for page in range(1, 6):
html1 = "https://wallhaven.cc/latest?page=" + str(page)
html2 = "https://wallhaven.cc/hot?page=" + str(page)
html_list = [html1, html2]
for html in html_list:
requests_html = requests.get(html, headers=headers)
bs_html = BeautifulSoup(requests_html.text, "lxml")
for link in bs_html.find_all('a', class_="preview"):
image_link = link['href']
url_list.append(image_link)
num += 1
print("已获取第" + str(num) + "个链接")
a = os.path.exists("/Users/artcgb/Downloads/壁纸/" + now)
if a:
print("文件夹已存在,PASS")
else:
os.mkdir("/Users/artcgb/Downloads/壁纸/" + now)
print("文件夹建立成功")
# 建立文件夹存放图片
num = 0
for link in url_list:
requests_html = requests.get(link, headers=headers)
bs_html = BeautifulSoup(requests_html.text, "lxml")
img = bs_html.find('img', id='wallpaper')
r = requests.get(img['src'])
num += 1
with open("/Users/artcgb/Downloads/壁纸/" + now + "/" + str(num) + ".jpg", 'wb') as f:
f.write(r.content)
print("第" + str(num) + "张写入成功")