爬虫 | 王者荣耀高清壁纸-多线程

# CY3761 | 2021-11-04 18:23
import json
import os
import queue
import time
import urllib

import requests

from urllib import parse
import threading
from queue import Queue


# 王者荣耀-官网 https://pvp.qq.com
# 王者荣耀-高清壁纸 https://pvp.qq.com/web201605/wallpaper.shtml | 游戏资料-游戏壁纸

# 多线程下载王者荣耀-高清壁纸 使用 queue
# 生产者线程 page_queue.get img_url_queue.put
# 请求json数据获得图片链接, 并对图片链接进行处理成可下载的图片链接
# 消费者线程 img_url_queue.get
# 进行读取图片链接,并进行下载图片

# -------------------------------------------------------------
# 创建文件夹
def makeDirPlus(_dirPath):
    if not os.path.exists(_dirPath):
        print('创建目录: %s' % _dirPath)
        os.makedirs(_dirPath)


# -------------------------------------------------------------
# 请求数据
def request(_url, _encoding=None):
    print(f'请求数据: {_url}')
    resp = requests.get(_url, headers=headers)
    resp.encoding = _encoding if _encoding else encoding
    
    if _url.endswith(imgRepNew):  # 属于图片
        return resp.content  # 图片返回二进制
    else:
        return resp.text  # 返回字符串文本


# -------------------------------------------------------------

# 保存文件
def saveData(_data, saveFilePath):
    # 不存在才进行保存
    if not os.path.exists(saveFilePath):
        # print(type(_data))
        if isinstance(_data, bytes):  # 保存图片
            w = open(saveFilePath, 'wb')
        else:
            w = open(saveFilePath, 'w', encoding=encoding)
        
        w.write(_data)
        w.close()
        print(f'写入数据: {saveFilePath}')


# -------------------------------------------------------------
# 读取数据
def getData(saveFilePath):
    # 存在才进行读取
    if os.path.exists(saveFilePath):
        r = open(saveFilePath, 'r', encoding=encoding)
        # 读取 json 数据
        if saveFilePath.endswith('json'):
            _data = json.load(r)
        else:
            _data = r.read()
        r.close()
        print(f'读取数据: {saveFilePath}')
        
        return _data


# -------------------------------------------------------------
# 获取请求数据
def get_request(requestUrl, saveFilePath='', requestEncoding=None):
    if not os.path.exists(saveFilePath):
        saveData(request(requestUrl, requestEncoding), saveFilePath)
    
    # 图片不需要读取
    if saveFilePath.endswith('jpg'):
        return
    
    return getData(saveFilePath)


# -------------------------------------------------------------
# 获取json数据
def get_json(_page):
    global cQs
    
    cQs['page'] = _page
    
    dEncode = urllib.parse.urlencode(cQs)
    dUrl = '?'.join([bUrl, dEncode])
    # print(dUrl)  # 这才是请求的地址
    
    jsonFilePath = f'{jsonDirPath}%d.json'
    
    return get_request(
        dUrl,
        jsonFilePath % _page
    )


# -------------------------------------------------------------
# 处理图片链接
def get_image_urls(_datas):
    # 图片数据
    dItems = _datas.get('List')
    # 1 ~ 8
    dImgItems = []
    for k, _ in enumerate(dItems):
        dImgItems.append([])  # 初始化图片列表
        
        # 图片名字 sProdName
        sProdName = _.get('sProdName')
        sProdName = urllib.parse.unquote(sProdName)
        sProdName = sProdName.strip()  # 清除空格
        
        # 图片id iProdId
        _iProdId = _.get('iProdId')
        
        for i in range(1, imgSize + 1):
            dImgItem = _.get(f'sProdImgNo_{i}')
            dImgItem = urllib.parse.unquote(dImgItem)
            dImgItem = dImgItem.replace(imgRepOld, imgRepNew)
            
            if dImgItem:
                dImgItems[k].append((sProdName, _iProdId, i, dImgItem))
    
    return dImgItems


# -------------------------------------------------------------

# 生产者线程
class P(threading.Thread):
    def __init__(self, j_queue, i_queue):
        super(P, self).__init__()
        self.j_queue = j_queue  # 页面数
        self.i_queue = i_queue  # 图片数
    
    # 进行数据请求获取JSON数据, 从JSON数据获取图片链接并处理成可下载的链接
    def run(self) -> None:
        global imgItems
        # 队列不为空 执行以下代码段
        while not self.j_queue.empty():
            # 获取取URL并发送请求 / 获取JSON数据
            # 获取JSON数据后, 从JSON数据获取图片链接并处理成可下载的链接
            # sProdName, _iProdId, i, dImgItem
            datas = get_image_urls(get_json(self.j_queue.get()))
            
            for k1, v1 in enumerate(datas):
                k1 = str(k1)
                for k2, v2 in enumerate(v1):
                    fileName = v2[0]
                    iProdId = v2[1].zfill(5)
                    i = v2[2]
                    fileSrc = v2[3]
                    
                    # 图片路径
                    imgItemDirPath = f'{imgDirPath}%s/' % iProdId
                    
                    makeDirPlus(imgItemDirPath)
                    
                    # 创建图片名字文件
                    saveData('', f'{imgItemDirPath}{fileName}.txt')
                    
                    self.i_queue.put((fileSrc, f'{imgItemDirPath}%s.jpg' % i))
                    
                    imgItems.setdefault(iProdId, fileName)
            pass
            
            # for in
            # 创建文件夹与对应各图片名字文件 | 一页的数据 20个
            # 生成图片路径与本地路径数据 并且进行
        
        # 最后需要 这里进行图片名字组装并写入一个文件
        
        pass


# -------------------------------------------------------------
# 消费者线程
class C(threading.Thread):
    def __init__(self, j_queue, i_queue):
        super(C, self).__init__()
        self.j_queue = j_queue  # 页面数
        self.i_queue = i_queue  # 图片数
    
    # 获取图片链接并下载图片
    def run(self) -> None:
        while True:
            try:
                get = self.i_queue.get(timeout=32)  # 获取图片并下载 | 超时结束
                get_request(get[0], get[1])
            except queue.Empty as e:  # 没有数据就停止循环
                break
        pass


# -------------------------------------------------------------
# 全局数据
dirPath = '10.王者荣耀/'
jsonDirPath = dirPath + 'json/'
imgDirPath = dirPath + 'img/'

imgRepOld = '/200'
imgRepNew = '/0'
imgSize = 8
imgItems = {}

makeDirItems = [
    jsonDirPath,
    imgDirPath
]

headers = {
    'referer': 'https://pvp.qq.com/',
    'user-agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                   'Chrome/95.0.4638.69 Safari/537.36')
}

encoding = 'utf-8'

# -------------------------------------------------------------
# 数据链接分析与解析
aUrl = ('https://apps.game.qq.com/cgi-bin/ams/module/ishow/V1.0/query/workList_inc.cgi?activityId=2735&sVerifyCode=ABCD'
        '&sDataType=JSON&iListNum=20&totalpage=0&page=1&iOrder=0&iSortNumClose=1&jsoncallback'
        '=jQuery1710035807958553548014_1635991723290&iAMSActivityId=51991&_everyRead=true&iTypeId=2&iFlowId=267733'
        '&iActId=2735&iModuleId=2735&_=1635992383307')
aSplit = aUrl.split('?')
bUrl, bQuery = aSplit[0], aSplit[1]  # 将链接与请求数据进行拆分

# print(bUrl, bQuery, '?'.join([bUrl, bQuery]), sep='\n')

cQs = urllib.parse.parse_qsl(bQuery)
cQs = {_[0]: _[1] for _ in cQs}
del cQs['jsoncallback']  # 删除请求响应前的字符串


# 改变请求页码 | 默认 0 | 第一页

# -------------------------------------------------------------
def main():
    s = time.time()
    for _ in makeDirItems:
        makeDirPlus(_)
    
    # 获取总页面 总图片数
    data = get_json(0)
    totalPages = int(data.get('iTotalPages'))
    totalLines = int(data.get('iTotalLines'))
    
    # 设置2个队列
    j_queue = Queue(totalPages)  # 页面数
    i_queue = Queue(totalLines * imgSize)  # 图片数
    
    for page in range(0, totalPages):
        # 传入链接页面 page
        j_queue.put(page)
        pass
    
    # 创建生产者线程对象 同时进行
    ts = []
    for _ in range(5):
        t = P(j_queue, i_queue)  # 存入2个队列对象
        ts.append(t)
        t.start()
    
    # 创建消费者线程对象 同时进行
    for _ in range(10):
        t = C(j_queue, i_queue)
        ts.append(t)
        t.start()
    
    for _ in ts:
        _.join()
    
    with open(f'{imgDirPath}目录对应名称.txt', 'w', encoding=encoding) as w:
        for _ in imgItems:
            k = _
            v = imgItems[_]
            w.write('|'.join([k, v]) + '\n')
        
        w.write(f'共{len(imgItems)}个目录\n')
    
    e = time.time()
    print('耗时 %.8f' % (e - s))


# -------------------------------------------------------------
if __name__ == '__main__':
    main()

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CY3761

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值