1. 程式人生 > 實用技巧 >JasperReports入門教程(五):分組列印

JasperReports入門教程(五):分組列印

此文轉載自:https://blog.csdn.net/qq_42453117/article/details/109961751

距離上次寫爬蟲文章已經過了許久了,之前寫過一篇20行Python程式碼爬取王者榮耀全英雄面板
,反響強烈,其中有很多同學希望我再寫一篇針對英雄聯盟官網的面板爬取,但苦於事情繁多,便一拖再拖,一直拖到了現在,那麼本篇文章我們就一起來學習一下如何爬取英雄聯盟全英雄面板。

爬取程式碼非常簡單,從上到下可能只需要寫30行左右就能完成,但重要的是分析過程,在此之前,我們先來了解一下本篇文章需要用到的模組。

requests模組介紹

首先是requests模組,這是一個模擬瀏覽器請求的模組,我們可以通過它來獲取網頁上的資訊,比如百度:

import requests

url = 'http://www.baidu.com'
response = requests.get(url)
print(response.text)

執行結果:

通過get函式就能夠向指定引數的url傳送請求,得到的response物件中封裝了很多響應的資訊,其中的text即為響應內容,注意到獲取的內容裡有亂碼,這是編解碼不一致造成的,只需先獲取二進位制資料,然後重新解碼即可:

import requests

url = 'http://www.baidu.com'
response = requests.get(url)
print(response.
content.decode())

執行結果:

json模組

json模組可以對json字串和Python資料型別進行相互轉換,比如將json轉換為Python物件:

import json

json_str = '{"name":"zhangsan","age":"20"}'
rs = json.loads(json_str)
print(type(rs))
print(rs)

使用loads函式即可將json字串轉為字典型別,執行結果:

<class 'dict'>
{'name': 'zhangsan', 'age': '20'}

而若是想將Python資料轉為json字串,也非常簡單:

import json

str_dict = {'name': 'zhangsan', 'age': '20'}
json_str = json.dumps(str_dict)
print(type(json_str))
print(json_str)

通過dumps函式即可將Python資料轉為json字串,執行結果:

<class 'str'>
{"name": "zhangsan", "age": "20"}

準備工作

前面介紹了兩個模組,通過這兩個模組我們就能夠完成這個程式了。
在正式開始編寫程式碼之前,我們首先需要分析資料來源,來到官網:
https://lol.qq.com/main.shtml,往下拉找到英雄列表:

我們隨意點選一個英雄進去檢視:

在面板圖片上右鍵點選檢查:
這樣就找到了這個面板的url,我們再選擇第二個面板,看看它的url:

我們將安妮所有面板的url全部拿出來看看:

https://game.gtimg.cn/images/lol/act/img/skin/big1000.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1001.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1002.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1003.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1004.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1005.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1006.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1007.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1008.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1009.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1010.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1011.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big1012.jpg

從這些url中能發現什麼規律呢?其實規律非常明顯,url前面的內容都是一樣的,唯一不同的是big1000.jpg,而每個面板圖片就是在該url的基礎上加1。

那麼問題來了,它是如何區分這張圖片所屬的英雄的呢?我們觀察瀏覽器上方的地址:

地址上有一個屬性值id為1,那麼我們可以猜測一下,面板圖片url中的big1000.jpg是不是由英雄id和面板id共同組成的呢?

要想證明我們的猜想,就必須再去看看其它英雄面板是不是也滿足這一條件:

開啟燼的詳情頁面,其id為202,由此,燼的面板圖片url最後部分應為:big ' + 202 + ' 面板編號.jpg,所以其url應為:

https://game.gtimg.cn/images/lol/act/img/skin/big202000.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big202001.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big202002.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big202003.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big202004.jpg
https://game.gtimg.cn/images/lol/act/img/skin/big202005.jpg

事實是不是如此呢?檢查一下便知:

規律已經找到,但是我們還面臨著諸多問題,比如每個英雄對應的id是多少呢?每個英雄又分別有多少個面板呢?

查詢英雄id

先來解決第一個問題,每個英雄對應的id是多少?我們只能從官網首頁中找找線索,在首頁位置開啟網路除錯臺:

點選Network,並選中XHR,XHR是瀏覽器與伺服器請求資料所依賴的物件,所以通過它便能篩選出一些伺服器的響應資料。
此時我們重新整理頁面,在篩選出的內容發現了這麼一個東西:

hero_list,英雄列表?這裡面會不會儲存著所有英雄的資訊呢?

點選右側的Response,得到了一串json字串,我們將其解析一下:

這些資料裡果然儲存的是英雄的資訊,包括名字、id、介紹等等,那麼接下來我們的任務就是將英雄名字和id單獨提取出來,過濾掉其它資訊。

忘了告訴你們了,這個檔案的url在這裡可以找到:

接下來開始寫程式碼:

import json
import requests

# 定義一個列表,用於存放英雄名稱和對應的id
hero_id = []
url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js?v=20'
response = requests.get(url)
text = response.text
# 將json字串轉為列表
hero_list = json.loads(text)['hero']
# 遍歷列表
for hero in hero_list:
    # 定義一個字典
    hero_dict = {'name': hero['name'], 'id': hero['heroId']}
    # 將列表加入字典
    hero_id.append(hero_dict)

print(hero_id)

首先通過requests模組請求該url,就能夠獲取到一個json字串,然後使用json模組將該字串轉為Python中的列表,最後迴圈取出每個英雄的name和heroid屬性,放入新定義的列表中,這個程式就完成了英雄id的提取。

查詢面板個數

接下來解決第二個問題,如何知曉某個英雄究竟有多少個面板,按照剛才的思路,我們可以猜測一下,對於面板也應該會有一個檔案儲存著面板資訊,在某個英雄的面板頁面開啟網路除錯臺,並選中XHR,重新整理頁面,找找線索:

找來找去確實找不到有哪個檔案是跟面板有關係的,但是這裡發現了一個31.js檔案,而當前英雄的id也為31,這真的是巧合嗎?我們將右邊的json字串解析一下:

該json資料中有一個skins屬性,該屬性值即為當前英雄的面板資訊,既然找到了資料,那接下來就好辦了,開始寫程式碼:

import json
import requests

url = 'https://game.gtimg.cn/images/lol/act/img/js/hero/31.js'
response = requests.get(url)
text = response.text
# 將json字串轉為列表
skins_list = json.loads(text)['skins']
skin_num = len(skins_list)

源程式實現

準備工作已經完成了我們所有的前置任務,接下來就是在此基礎上編寫程式碼了:

import requests
import json
import os
import traceback
from tqdm import tqdm


def spider_lol():
    # 定義一個列表,用於存放英雄名稱和對應的id
    hero_id = []
    skins = []
    url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js?v=20'
    response = requests.get(url)
    text = response.text
    # 將json字串轉為列表
    hero_list = json.loads(text)['hero']
    # 遍歷列表
    for hero in hero_list:
        # 定義一個字典
        hero_dict = {'name': hero['name'], 'id': hero['heroId']}
        # 將列表加入字典
        hero_id.append(hero_dict)
    # 得到每個英雄對應的id後,即可獲得英雄對應面板的url
    # 英雄id + 001
    # 遍歷列表
    for hero in hero_id:
        # 得到英雄名字
        hero_name = hero['name']
        # 得到英雄id
        hero_id = hero['id']
        # 建立資料夾
        os.mkdir('C:/Users/Administrator/Desktop/lol/' + hero_name)
        # 進入資料夾
        os.chdir('C:/Users/Administrator/Desktop/lol/' + hero_name)
        # 得到id後即可拼接儲存該英雄資訊的url
        hero_info_url = 'https://game.gtimg.cn/images/lol/act/img/js/hero/' + hero_id + '.js'
        # 通過訪問該url獲取英雄的面板數量
        text = requests.get(hero_info_url).text
        info_list = json.loads(text)
        # 得到面板名稱
        skin_info_list = info_list['skins']
        skins.clear()
        for skin in skin_info_list:
            skins.append(skin['name'])
        # 獲得面板數量
        skins_num = len(skin_info_list)
        # 獲得面板數量後,即可拼接面板的url,如:安妮的面板url為:
        # https://game.gtimg.cn/images/lol/act/img/skin/big1000.jpg ~ https://game.gtimg.cn/images/lol/act/img/skin/big1012
        s = ''
        for i in tqdm(range(skins_num), '正在爬取' + hero_name + '的面板'):
            if len(str(i)) == 1:
                s = '00' + str(i)
            elif len(str(i)) == 2:
                s = '0' + str(i)
            elif len(str(i)) == 3:
                pass
            try:
                # 拼接面板url
                skin_url = 'https://game.gtimg.cn/images/lol/act/img/skin/big' + hero_id + '' + s + '.jpg'
                # 訪問當前面板url
                im = requests.get(skin_url)
            except:
                # 某些英雄的炫彩面板沒有url,所以直接終止當前url的爬取,進入下一個
                continue
            # 儲存圖片
            if im.status_code == 200:
                # 判斷圖片名稱中是否帶有'/'、'\'
                if '/' in skins[i] or '\\' in skins[i]:
                    skins[i] = skins[i].replace('/', '')
                    skins[i] = skins[i].replace('\\', '')
                with open(skins[i] + '.jpg', 'wb') as f:
                    f.write(im.content)


def main():
    try:
        spider_lol()
    except Exception as  e:
        # 列印異常資訊
        print(e)


if __name__ == '__main__':
    main()

執行效果:

執行之前記得在桌面上建立一個lol資料夾,如果想改動的話也可以修改程式:

程式中還考慮到了一些其它情況,比如在爬取這個面板的時候會出現問題:

因為圖片路徑是以面板名字命名的,然而這個面板的名字中竟然有個/,它是會影響到我們的圖片儲存操作的,所以在儲存前將斜槓替換成空字元即可。

還有一個問題就是即使是第一個面板,其編號也應該為000而不是0,所以還需要對其進行一個轉化,讓其始終是三位數。

最後

本篇文章同樣繼承了上篇文章精簡的特點,拋去註釋的話總共30行程式碼左右,程式當然還有一些其它地方可以進一步優化,這就交給大家自由發揮了。

文章程式碼已上傳至GitHub:https://github.com/blizzawang/Spider_LOL,有什麼好的建議,歡迎Issues。