Python3爬取英雄聯盟英雄皮膚大圖

gxcuizy發表於2018-11-14

前言

上篇文章,說到了,爬取LOL英雄皮膚的高清圖片,最近有事,也沒怎麼去研究,所以,現在才去看了下,並且寫了Python指令碼來抓取皮膚圖片。需要說明一下,這個指令碼有部分英雄沒有抓取到,但是具體原因,我目前還沒搞懂,我是相當納悶的。大家有興趣的,可以看看後面遺留問題,一起研究下。

爬蟲思路

初步嘗試

我先檢視了network,並沒有發現有可用的API;然後又用bs4去分析英雄列表頁,但是請求到html裡面,並沒有英雄列表,在英雄列表的節點上,只有“正在載入中”這樣的字樣;同樣的方法,分析英雄詳情也是這種情況,所以我猜測,這些資料應該是Javascript負責載入的。

step-1

繼續嘗試

然後我就檢視了英雄列表的原始碼,檢視外部引入的js檔案,以及行內的js指令碼,大概在368行,發現了有處理英雄列表的js註釋,然後繼續往下讀這些程式碼,發現了第一個彩蛋,也就是他引入了一個champion.js的檔案,我猜測,這個應該就是英雄列表大全了,然後我開啟了這個連結的js,一眼看過去,黑麻麻一片,然後格式化了一下壓縮的js,確定這就是英雄列表的js資料檔案了。

step-2

接著嘗試

前面通過檢視列表的原始碼,找到了英雄列表的js資料檔案,那麼,我繼續隨機點開了一個英雄的詳情,然後檢視英雄詳情原始碼,然後大概在568行看到有一個showSkin的js方法,通過這裡,發現了第二個彩蛋,也就是皮膚圖片的URL地址拼接方法。

step-3

最後嘗試

上面找到了皮膚圖片URL的拼接方法,並且發現了一行很關鍵的程式碼var skin =LOLherojs.champion[heroid].data.skins,也就是,這個skin變數,就是英雄皮膚的所有圖片陣列,但是這個檔案內,並沒有LOLherojs這個變數,也就是外部引入的,所以,需要繼續檢視下面的原始碼,找到引入這個變數的位置,果不其然,在757行,發現了最後一個彩蛋,也就是,英雄皮膚的js檔案,通過這裡可以知道,每個英雄都有一個單獨的js檔案,並且知道了這個js檔案的URL拼接方法。

step-4

思路總結

通過上面的分析,我們就得到了爬取LOL皮膚圖片的所有資料準備了,也就是,直接,只需要提取js中的英雄列表以及英雄詳情資料,就可實現我們的需求了。下面是執行後抓取到的圖片……

step-5

執行環境

Python執行環境:python3.6
用到的模組:requests、json、urllib、os
未安裝的模組,請使用pip instatll進行安裝,例如:pip install requests

完整程式碼

其他啥的廢話就不多說了,直接上完整程式碼,有問題,直接留言給我就行,另外,程式碼已上傳GitHub。再說明一下,那些有問題的英雄詳情的js檔案,大家有時間也可以琢磨下,或者有其他的更加快捷的爬取這些圖片的方法,也可以拿出來交流和討論,謝謝。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
抓取英雄聯盟英雄全皮膚
author: gxcuizy
date: 2018-11-13
"""

import requests
import json
from urllib import parse
import os


class GetLolSkin(object):
    """抓取LOL英雄皮膚"""

    def __init__(self):
        """初始化變數"""
        self.hero_url = `https://lol.qq.com/biz/hero/champion.js`
        self.hero_detail_url = `http://lol.qq.com/biz/hero/`
        self.skin_folder = `skin`
        self.skin_url = `https://ossweb-img.qq.com/images/lol/web201310/skin/big`

    @staticmethod
    def get_html(url):
        """下載html"""
        request = requests.get(url)
        request.encoding = `gbk`
        if request.status_code == 200:
            return request.text
        else:
            return "{}"

    def get_hero_list(self):
        """獲取英雄的完整資訊列表"""
        hero_js = self.get_html(self.hero_url)
        # 刪除左右的多餘資訊,得到json資料
        out_left = "if(!LOLherojs)var LOLherojs={};LOLherojs.champion="
        out_right = `;`
        hero_list = hero_js.replace(out_left, ``).rstrip(out_right)
        return json.loads(hero_list)

    def get_hero_info(self, hero_id):
        """獲取英雄的詳細資訊"""
        # 獲取js詳情
        detail_url = parse.urljoin(self.hero_detail_url, hero_id + `.js`)
        detail_js = self.get_html(detail_url)
        # 刪除左右的多餘資訊,得到json資料
        out_left = "if(!herojs)var herojs={champion:{}};herojs[`champion`][%s]=" % hero_id
        out_right = `;`
        hero_info = detail_js.replace(out_left, ``).rstrip(out_right)
        return json.loads(hero_info)

    def download_skin_list(self, skin_list, hero_name):
        """下載皮膚列表"""
        # 迴圈下載皮膚
        for skin_info in skin_list:
            # 拼接圖片名字
            if skin_info[`name`] == `default`:
                skin_name = `預設皮膚`
            else:
                if ` ` in skin_info[`name`]:
                    name_info = skin_info[`name`].split(` `)
                    skin_name = name_info[0]
                else:
                    skin_name = skin_info[`name`]
            hero_skin_name = hero_name + `-` + skin_name + `.jpg`
            self.download_skin(skin_info[`id`], hero_skin_name)

    def download_skin(self, skin_id, skin_name):
        """下載皮膚圖片"""
        # 下載圖片
        img_url = self.skin_url + skin_id + `.jpg`
        request = requests.get(img_url)
        if request.status_code == 200:
            print(`downloading……%s` % skin_name)
            img_path = os.path.join(self.skin_folder, skin_name)
            with open(img_path, `wb`) as img:
                img.write(request.content)
        else:
            print(`img error!`)

    def make_folder(self):
        """初始化,建立圖片資料夾"""
        if not os.path.exists(self.skin_folder):
            os.mkdir(self.skin_folder)

    def run(self):
        # 獲取英雄列表資訊
        hero_json = self.get_hero_list()
        hero_keys = hero_json[`keys`]
        # 迴圈遍歷英雄
        for hero_id, hero_code in hero_keys.items():
            hero_name = hero_json[`data`][hero_code][`name`]
            hero_info = self.get_hero_info(hero_id)
            if hero_info:
                skin_list = hero_info[`result`][hero_id][`skins`]
                # 下載皮膚
                self.download_skin_list(skin_list, hero_name)
            else:
                print(`英雄【%s】的皮膚獲取有問題……` % hero_name)


# 程式執行入口
if __name__ == `__main__`:
    lol = GetLolSkin()
    # 建立圖片儲存檔案
    lol.make_folder()
    # 執行指令碼
    lol.run()

相關文章