多线程爬取wallhaven

前言

最近整理自己的项目时,发现之前自己写的爬取wallhaven网站顿时有来的兴趣决定再写一遍来回顾自己以前学的知识
网站地址:"https://wallhaven.cc/"

10年积累的网站设计、成都做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有爱民免费网站建设让你可以放心的选择与我们合作。

1. url参数结构

从url地址我们发现为"https://wallhaven.cc/search?categories=111&purity=110&sorting=date_added&order=desc&page=2",这段url里面代用几个参数分别为,categories,purit,sorting。而这几个参数分别对应界面的这几个按钮

2. 查看网页结构

按f12键查找到要爬取的图片目录

通过查看代码发现该图片只是一张预览图片,分辨率仅为300*200,明显不符合要求。
观察下方a标签

发现这个class为preview的标签中存在个href地址链接到该高清图片的正确地址,点击该地址跳转


通过查看代码发现这才是我们需要的图片,找到了我们要的图片链接,在img标签中。

通过以上观察我们可以知道,我们的代码编写思路

  1. 获取到这一页所有图片的url地址
  2. 通过解析获取到详情页网页的图片
  3. 请求到详情页网页图片进行下载

3. 开始爬取图片

3.1 初始化

import requests
url = "https://wallhaven.cc/search?categories=011&purity=010&sorting=date_added&order=desc&page=2"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.56',
}

class Spider:
	# 初始化数据
	def __init__(self, url, header, cookie):
		self.user_url = url
		self.header = header
		self.img_list_url = [] # 存放一页的所有图片地址
		self.num = 0  # 计数爬取了多少张图片

3.2 获取到这一页所有图片的url地址

我们在Spider类中定义一个res方法来爬取这一页的所有url地址,并且利用列表推导式将所有的url地址存放到list_img列表中以便我们后续使用

# 获取某一页要下载的图片所有链接
def res(self):
	response = requests.get(self.user_url, headers=self.header).text
	soup = BeautifulSoup(response, "html.parser")
	img_href = soup.select(".thumb-listing-page > ul > li  a[href]")
	list_img = [i.get("href") for i in img_href]  # 所有的图片的href
	print(list_img)
	print("一共", len(list_img), "图片")
	return list_img

3.3 通过解析获取到详情页网页的图片

通过res方法我们得到了一页的所有图片链接,这时我们获取详情页的图片地址并把它存放到img_list_url列表中以便我们后续开启多进程进行下载。但是我们这些书写会出现拿不到详细页的图片地址从而导致下标越界从而报错,所以可以优化一下代码

# 获取详情页图片地址
def detail_page(self, list_img):
	# 遍历获取详情页图片地址
	for item in list_img:
		detail_page = requests.get(item, headers=self.header, cookies=self.cookie).text
		soup = BeautifulSoup(detail_page, "html.parser")
		img_url = soup.select("#showcase > div.scrollbox > img[src]")[0]["src"]
		self.img_list_url.append(img_url)
		print(img_url, "添加成功")
		self.error_img_url(item)
		return self.img_list_url

可以写出这样

# 获取详情页图片地址
def detail_page(self, list_img):
# 遍历获取详情页图片地址
for item in list_img:
	detail_page = requests.get(item, headers=self.header, cookies=self.cookie).text
	soup = BeautifulSoup(detail_page, "html.parser")
	try:  # 访问过快会爬取不到详情页图片地址,则出现一个空列表从而导致下标越界
		img_url = soup.select("#showcase > div.scrollbox > img[src]")[0]["src"]
		self.img_list_url.append(img_url)
		print(img_url, "添加成功")
	except IndexError:  # 出现下标越界调用error_img_url方法
		self.error_img_url(item)
		return self.img_list_url

# 处理获取失败的图片地址
def error_img_url(self, url):
	print(url, "没有获取到现在重新获取")
	# 休眠2秒重新发送请求
	time.sleep(2)
	detail_page = requests.get(url, headers=self.header, cookies=self.cookie).text
	soup = BeautifulSoup(detail_page, "html.parser")
	try:
		img_url = soup.select("#showcase > div.scrollbox > img[src]")[0]["src"]
		self.img_list_url.append(img_url)
		print(img_url, "获取成功")
	except IndexError:  # 如果还没有获取到图片地址那么递归调用
		self.error_img_url(url)

3.4 请求到详情页网页图片进行下载

通过前面我们已经获取下载图片地址,我们发现地址为"https://w.wallhaven.cc/full/zy/wallhaven-zyg5kw.jpg "完全可以使用split('/')进行切片获取最后一段用来命名图片名称

# 图片下载
def download_images(self, img_url):
	if img_url is not None:
		img_name = img_url.split("/")[-1]  # 图片名称
		img = requests.get(img_url, headers=self.header).content
		with open(img_path + "/" + img_name, "wb") as fp:
			fp.write(img)
			print(img_name, "download over~")
			self.num += 1

3.5 main方法

这时候我们编写主函数就可以下载了

if __name__ == '__main__':
    img_path = "./wall"
    if not os.path.exists(img_path):
        os.mkdir(img_path)
    # 开始爬取
    start = time.time()
    spider = Spider(url, headers, cookie)
    list_img = spider.res()

    img_list_url = spider.detail_page(list_img)

    # 开启5个进程下载图片
    pool = Pool(5)
    pool.map(spider.download_images, img_list_url)

    end = time.time()
    print("下载图片数量:", Spider.images_count)
    print(end - start)

4. 完整代码

import os
import time
from multiprocessing.dummy import Pool
import requests
from bs4 import BeautifulSoup

url = "https://wallhaven.cc/search?categories=011&purity=010&sorting=date_added&order=desc&page=2"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.56',
}


class Spider:
    # 初始化数据
    def __init__(self, url, header, cookie):
        self.user_url = url
        self.header = header
        self.cookie = cookie
        self.img_list_url = []  # 存放一页的所有图片地址
        self.num = 0  # 计数爬取了多少张图片

    # 获取某一页要下载的图片所有链接
    def res(self):
        response = requests.get(self.user_url, headers=self.header).text
        soup = BeautifulSoup(response, "html.parser")
        img_href = soup.select(".thumb-listing-page > ul > li  a[href]")
        list_img = [i.get("href") for i in img_href]  # 所有的图片的href
        print(list_img)
        print("一共", len(list_img), "图片")
        return list_img

    # 获取详情页图片地址
    def detail_page(self, list_img):
        # 遍历获取详情页图片地址
        for item in list_img:
            detail_page = requests.get(item, headers=self.header).text
            soup = BeautifulSoup(detail_page, "html.parser")
            try:  # 访问过快会爬取不到详情页图片地址,则出现一个空列表从而导致下标越界
                img_url = soup.select("#showcase > div.scrollbox > img[src]")[0]["src"]
                self.img_list_url.append(img_url)
                print(img_url, "添加成功")
            except IndexError:  # 出现下标越界调用error_img_url方法
                self.error_img_url(item)
        return self.img_list_url

    # 处理获取失败的图片地址
    def error_img_url(self, url):
        print(url, "没有获取到现在重新获取")
        # 休眠2秒重新发送请求
        time.sleep(2)
        detail_page = requests.get(url, headers=self.header).text
        soup = BeautifulSoup(detail_page, "html.parser")
        try:
            img_url = soup.select("#showcase > div.scrollbox > img[src]")[0]["src"]
            self.img_list_url.append(img_url)
            print(img_url, "获取成功")
        except IndexError:  # 如果还没有获取到图片地址那么递归调用
            self.error_img_url(url)

    # 图片下载
    def download_images(self, img_url):
        if img_url is not None:
            img_name = img_url.split("/")[-1]  # 图片名字
            img = requests.get(img_url, headers=self.header).content
            with open(img_path + "/" + img_name, "wb") as fp:
                fp.write(img)
            print(img_name, "download over~")
            self.num += 1

    def images_count(self):
        print(self.num)


if __name__ == '__main__':
    img_path = "./wall"
    if not os.path.exists(img_path):
        os.mkdir(img_path)
    # 开始爬取
    start = time.time()
    spider = Spider(url, headers, cookie)
    list_img = spider.res()

    img_list_url = spider.detail_page(list_img)

    # 开启5个进程下载图片
    pool = Pool(5)
    pool.map(spider.download_images, img_list_url)

    end = time.time()
    print("下载图片数量:", Spider.images_count)
    print(end - start)

5. 添加Cookie

如果有wallhaven用户的想爬取用户能爬取的图片需要添加Cookie

# 用户cookie
cookie = {
    "_pk_id.1.01b8": "xxxx",
    "_pk_ses.1.01b8": "xxxx",
    "remember_web_59ba36addc2b2ff014c7f58ea4ed": "xxx",
    "XSRF-TOKEN": "xxxx",
    "wallhaven_session": "xxxx"
}

请求的时候带上cookie即可


新闻标题:多线程爬取wallhaven
文章起源:http://ybzwz.com/article/dsogscp.html