记录一次极简壁纸的图片爬取过程【https://bz.zzzmh.cn/】-Python交流社区-开发交流-WordPress主题模板-zibll子比主题

记录一次极简壁纸的图片爬取过程【https://bz.zzzmh.cn/】

引言

首先观察网站结构

20240820202126202-QQ_1724156473491

每张图片右上角都有保存的功能,检查源码发现这个保存是一个url

20240820202343415-QQ_1724156611882

那不就简单了,随便使用python模拟访问一下

import requests

response = requests.get('https://api.zzzmh.cn/v2/bz/v3/getUrl/286484bdc06b47dab2f5cdb9a3ca86e229')
print(response.text)
# 确保响应的内容是二进制数据
if response.status_code == 200:
    with open(f'{i}.jpg', 'wb') as file:
        file.write(response.content)
    print("图片已成功保存。")
else:
    print(f"请求失败,状态码: {response.status_code}")

图片被成功的下载了下来,而且还不需要验证直接get就可以得到。
现在就是观察这个 url 的时候了,这儿列举几个

https://api.zzzmh.cn/v2/bz/v3/getUrl/37aa5dc36d88444786b6b543845963ba29

https://api.zzzmh.cn/v2/bz/v3/getUrl/1b87e2c5880511ebb6edd017c2d2eca229

https://api.zzzmh.cn/v2/bz/v3/getUrl/ba3dee63249d43e88b3d5c5e7c75ef2329

现在就是小学生的作业了,找规律

https://api.zzzmh.cn/v2/bz/v3/getUrl/这个是一个相同点前缀。

只有后面的东西不同,还有一个就是最后都有一个29

所以前面的部分例如37aa5dc36d88444786b6b543845963ba

这个好像是服务器给每一张图片生成的,不知道是不是随机的,写法咋们也看不到,所以暂时先不管,我想这个网站作者也是一个大佬,首页图片的数据应该不是直接写入到页面中的,这个网站是vue与html等等构成的。

遇事不决便刷新观察,同时为了观察清楚,将页面缓存禁用,同时限制加载速度

20240820203810123-QQ_1724157415934

点击刷新观察

20240820203919276-QQ_1724157541678

他优先出来的是 ui 这说明这个过程加载了一个josn数据用来构建主体支撑

思路明确找加载的数据,在网络中查看,勾选XHR,剔除掉媒体文件

20240820204346278-QQ_1724157778076

第一个就是标准的数据传输文件,data这个类文件最为常用

点开查看响应数据是否加密,很明显直接明文传输

20240820204520470-QQ_1724157905166

其中【i】值不就是我们在连接中找到的不同的地方吗。所以差不多了开始写代码

上代码

先理清思路,批量获取【i】值,构建链接批量下载
首先写一个获取【i】值的代码,继续观察这个getData数据传输的过程,看看负载

20240820205150994-QQ_1724158297066

不难看出current为页码,size为加载数据的多少。翻到最下面,共计1733页

20240820205624792-QQ_1724158568105

现在就是将这个getdata模拟到python上,这个访问请求到新建标签页时候会显示网络错误,500

所以这儿给你们推荐一个网站【Convert curl commands to code (curlconverter.com)】用来将curl处理为各种语言的请求。

右键这个请求,复制,复制curl(bash)

粘贴到处理curl的网站,粘贴到红色的框中

20240820210117740-QQ_1724158864255

 

下面会自动显示python的模拟请求

import requests

headers = {
    'accept': 'application/json, text/plain, */*',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
    'content-type': 'application/json;charset=UTF-8',
    'origin': 'https://bz.zzzmh.cn',
    'priority': 'u=1, i',
    'referer': 'https://bz.zzzmh.cn/',
    'sec-ch-ua': '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0',
}

json_data = {
    'size': 24,
    'current': 2,
    'sort': 0,
    'category': 0,
    'resolution': 0,
    'color': 0,
    'categoryId': 0,
    'ratio': 0,
}

response = requests.post('https://api.zzzmh.cn/v2/bz/v3/getData', headers=headers, json=json_data)

# Note: json_data will not be serialized by requests
# exactly as it was in the original request.
#data = '{"size":24,"current":2,"sort":0,"category":0,"resolution":0,"color":0,"categoryId":0,"ratio":0}'
#response = requests.post('https://api.zzzmh.cn/v2/bz/v3/getData', headers=headers, data=data)

我们现在需要获取全部的数据,就需要写循环处理

import requests

headers = {
    'accept': 'application/json, text/plain, */*',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
    'cache-control': 'no-cache',
    'content-type': 'application/json;charset=UTF-8',
    'origin': 'https://bz.zzzmh.cn',
    'pragma': 'no-cache',
    'priority': 'u=1, i',
    'referer': 'https://bz.zzzmh.cn/',
    'sec-ch-ua': '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0',
}

# 创建或输出文件
with open('output.txt', 'w', encoding='utf-8') as file:
    file.write("")  # 清空文件内容

for current_page in range(1, 1735):  # 从第 1 页到第 1734 页
    json_data = {
        'size': 24,
        'current': current_page,
        'sort': 0,
        'category': 0,
        'resolution': 0,
        'color': 0,
        'categoryId': 0,
        'ratio': 0,
    }

    response = requests.post('https://api.zzzmh.cn/v2/bz/v3/getData', headers=headers, json=json_data)

    # 确保响应的内容是 JSON 数据
    if response.status_code == 200:
        response_json = response.json()
        # 检查响应中是否包含 "data" 和 "list"
        if 'data' in response_json and 'list' in response_json['data']:
            items = response_json['data']['list']
            i_values = [item['i'] for item in items]

            # 将 'i' 值追加到 txt 文件中
            with open('output.txt', 'a', encoding='utf-8') as file:
                for value in i_values:
                    #一行一个数据
                    file.write(f"{value}\n")

            print(f"第 {current_page} 页的数据已写入 'output.txt' 文件中。")
        else:
            print(f"第 {current_page} 页的响应中没有 'data' 或 'list'。")
    else:
        print(f"第 {current_page} 页请求失败,状态码: {response.status_code}")

print("所有页的数据已写入 'output.txt' 文件中。")

好了现在所有的【i】值都写入到txt文件中了

紧接着就可以下载图片了

import requests
import os

# 确保图片保存目录存在
image_folder = 'image'
os.makedirs(image_folder, exist_ok=True)

# 读取 'output.txt' 文件中的所有 'i' 值
with open('output.txt', 'r', encoding='utf-8') as file:
    i_values = file.readlines()

for i in i_values:
    i = i.strip()  # 去除可能存在的空白字符
    if i:
        url = f'https://api.zzzmh.cn/v2/bz/v3/getUrl/{i}29'
        response = requests.get(url)

        # 确保响应的内容是二进制数据
        if response.status_code == 200:
            file_path = os.path.join(image_folder, f'{i}.jpg')
            with open(file_path, 'wb') as file:
                file.write(response.content)
            print(f"图片 {i}.jpg 已成功保存。")
        else:
            print(f"请求失败,状态码: {response.status_code},图片 {i}.jpg 未能保存。")

这个代码在运行的时候

发现有个别的会出现状态码错误,一般和url有关系,再次看了网页下载结构发现有的后缀为【29】有的为【19】而且所有的图片并不是jpg结尾的

所以这里使用相应内容中【Content-Type】来确定图片的后缀

所以再增加两个 if 来解决这两个问题,以及在测试过程中出现了这个东西,所以再增加一个time限制,以避免被封ip。

20240820211337707-QQ_1724159605389

import requests
import os
import time

# 确保图片保存目录存在
image_folder = 'image'
os.makedirs(image_folder, exist_ok=True)

# 读取 'output.txt' 文件中的所有 'i' 值
with open('output.txt', 'r', encoding='utf-8') as file:
    i_values = file.readlines()

for i in i_values:
    i = i.strip()  # 去除可能存在的空白字符
    if i:
        # 尝试第一个 URL
        url = f'https://api.zzzmh.cn/v2/bz/v3/getUrl/{i}29'
        response = requests.get(url)

        # 如果请求状态是 403,则尝试使用备用 URL
        if response.status_code == 403:
            url = f'https://api.zzzmh.cn/v2/bz/v3/getUrl/{i}19'
            response = requests.get(url)

        # 确保响应的内容是二进制数据
        if response.status_code == 200:
            # 获取内容类型以确定图片格式
            content_type = response.headers.get('Content-Type', '')
            if 'image/jpeg' in content_type:
                extension = 'jpg'
            elif 'image/png' in content_type:
                extension = 'png'
            elif 'image/webp' in content_type:
                extension = 'webp'
            else:
                extension = 'bin'  # 如果无法确定格式,使用通用扩展名

            file_path = os.path.join(image_folder, f'{i}.{extension}')
            with open(file_path, 'wb') as file:
                file.write(response.content)
            print(f"图片 {i}.{extension} 已成功保存。")
        else:
            print(f"请求失败,状态码: {response.status_code},图片 {i} 未能保存。")

        # 每处理一行等待 2 秒
        print(f"等待 5 秒...")
        time.sleep(5)

其实有细心的小伙伴应该发现了,在getdata文件中【t】值就是处理这个问题的关键。但是这个她只有两个不同的值,所以就不需要再写入一个excel来判定是否用哪个url了,直接在源码中 if 这样应该快点。

所上最终代码

 

 

请登录后发表评论