利用Python多线程爬取王者荣耀高清壁纸

Python大数据分析

共 3716字,需浏览 8分钟

 ·

2022-02-13 11:40

本文将手把手教你用Python多线程爬取王者荣耀高清壁纸,以下是运行代码后的结果:



01

需求分析



爬取王者荣耀高清壁纸,其实用传统方法也可以爬,但是多线程可以提高效率。

目标网址:

url = "https://pvp.qq.com/web201605/wallpaper.shtml"

壁纸位置:


本文主要是演示爬取的过程,因此只爬取10页,上面的一张图片其实是一套图,有八张图片(大小不一)。初步分析发现图片url不在网页源码里,所以需要在network里找到该资源,发现壁纸放在了worklist的json数据里:




02

解析数据



打开json.cn,得到如下数据。(第一步得到的json数据的请求url发起请求得到的json数据)
  1. response 复制到了json.cn网站数据是错误的 jsoncallback=Jquery的数据删掉
  2. 每一个Object就是一组图片 sProdImgNo_1 是封面小图 ()




03

编写代码



在创建队列之前需要对图片url进行预处理。

# 通过编号来获取不同规格的图片 必须把 200 --> 0
# 发现图片的url做了编码了 parse.unquote 进行了一个解码
def extract_images(data):
    image_urls = []
    for x in range(1,9):
        image_url = parse.unquote(data['sProdImgNo_%d'%x]).replace('200''0')
        image_urls.append(image_url)
    return image_urls

我们需要定义生产者和消费者两个类,生产者负责获取图片url链接,消费者负责下载图片。所以还需要定义两个队列,一个储存page_url(我们要爬取10页),一个储存img_url。由于多线程相较于函数式编程复杂了点,就不一点点展示了,直接上代码。

生产者:

class Producer(threading.Thread):
    def __init__(self,page_ueue,image_ueue,*args,**kwargs):
        super(Producer,self).__init__(*args,**kwargs) # 初始化父类的init方法属性,父类也有__init__方法,如果不初始化,会报错.
        self.page_ueue = page_ueue
        self. image_ueue = image_ueue

    def run(self) -> None:
        while not self.page_ueue.empty():
            page_url = self.page_ueue.get()
            res = requests.get(page_url, headers=headers)
            result = res.json() # response.json() 是requests第三方库提供的 是将json类型的数据转换成python字典的
方法

            datas = result['List']
            for data in datas:
                # extract_images()定义的全局函数函数将图片url的200改成0,并且解码图片url(因为8张图片大小不一样,就是由这个字符串控制,因为看到图片url中有特殊字符%13%aab...)
                image_urls = extract_images(data) 
                name = parse.unquote(data['sProdName'])
                dirpath = os.path.join('image', name) # 动态的取添加路径 os.path.join()
                if not os.path.exists(dirpath):
                    os.mkdir(dirpath)
                # 把图片的url放到队列当中
                for index,image_url in enumerate(image_urls): #  为图片命名 enumerate()来解决图片名字的问题 1.jpg 2.jpg 3.jpg
                    self.image_ueue.put({'image_url':image_url,'image_path':os.path.join(dirpath,'%d.jpg'%(index+1))})

消费者:

class Comsumer(threading.Thread):
    def __init__(self, image_ueue,*args,**kwargs):
        super(Comsumer,self).__init__(*args,**kwargs)
        self.image_ueue = image_ueue

    def run(self) -> None:
        while True:
            try:
                image_obj = self.image_ueue.get(timeout=10)
                image_url = image_obj.get('image_url')
                image_path = image_obj.get('image_path')
                try:
                    request.urlretrieve(image_url,image_path)
                    print("%s下载成功!"%image_path)
                except:
                    print('下载失败')
            except:
                break

主函数,定义队列,开启线程:

# 创建了3个生产者线程 5个消费者线程 (因为消费者做的事情比较多 发起请求 保存图片)
def main():

    # 创建页面url队列一
    page_ueue = Queue(10)

    # 创建图片url队列
    image_ueue = Queue(3000)


    for i in range(10): # 咱们就爬取10页
        img_url = "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={}&iOrder=0&iSortNumClose=1&171003449092155893818_1620870158277&iAMSActivityId=51991&_everyRead=true&iTypeId=2&iFlowId=267733&iActId=2735&iModuleId=2735&_=1620870158575".format(i)

        page_ueue.put(img_url)

    # 创建3个生产者线程
    for i in range(3):
        pt = Producer(page_ueue,image_ueue)
        pt.start()

    # 创建5个消费者线程
    for i in range(5):
        ct = Comsumer(image_ueue)
        ct.start()



04

程序运行



if __name__ == '__main__':
    main()


感兴趣的小伙伴可以去试一下。

加入知识星球【我们谈论数据科学】

500+小伙伴一起学习!








· 推荐阅读 ·

地图可视化:geopandas绘制拓扑着色地图

盘点2021最佳数据可视化作品

「Python实用秘技04」pdf文件批量添加文字水印


浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报