天天看點

【Python爬蟲】爬取數位寶貝digimon資料庫

寫在最前面,本文完全基于 【網絡爬蟲】爬取神奇寶貝Pokemon圖鑒圖檔大全 編寫

前言

最近在學習Vue.js和微信小程式,作為實踐,決定用uni-app以vue.js的方式寫微信小程式,加上最近在switch上玩“數位寶貝網絡偵探駭客追憶”,發現還沒有和口袋妖怪一樣的圖鑒小程式,決定做一個“數位寶貝圖鑒”小程式,苦于沒有資料也沒有圖檔,決定現場學習python,自己去數位獸資料庫爬資料。

效果圖

【Python爬蟲】爬取數位寶貝digimon資料庫
【Python爬蟲】爬取數位寶貝digimon資料庫
【Python爬蟲】爬取數位寶貝digimon資料庫

準備工作

  1. 學習Python基本文法,學習地點 Python基礎教程
  2. 了解爬蟲方式、庫,直接從【網絡爬蟲】爬取神奇寶貝Pokemon圖鑒圖檔大全中的代碼看着學習,結合 Python爬蟲實戰教程:批量爬取某網站圖檔的視訊來綜合改進
  3. 請教已有的圖鑒類小程式的創作者,特别鳴謝微信小程式“FGO素材規劃”作者,問了才知道資料都是自己去找的,沒有API

代碼

由于是第一次寫Python,第一次寫爬蟲,可能下載下傳慢,可能記憶體爆掉,見諒

# -*- coding:UTF-8 -*-
from bs4 import BeautifulSoup
from xlwt import Workbook
import requests
import os

class downloader(object):

    def __init__(self):
        self.target = r'http://www.digimons.net/digimon/chn.html'  # 首頁
        self.root = 'http://www.digimons.net/digimon/'  # 數位獸們的根目錄
        self.digimon = []  # 存放數位寶貝名和屬性
        self.names = []  # 數位寶貝的名字
        self.urls = []  # 數位寶貝的連結
        self.level = []  # 數位寶貝的等級
        self.divs = []  # 不同等級的class屬性
        self.palces = ['幼年期Ⅰ', '幼年期Ⅱ', '成長期', '成熟期',
                       '完全體', '究極體', '裝甲體', '混合體', '不明', '無(-)']
        self.levelClass = ['c_1','c_2','c_3','c_4','c_5','c_6','c_7','c_8','c_9','c_10']
        self.image = []  # 數位寶貝的圖檔位址
        self.down = []  # 圖檔位址及編号
        self.datas = [] # 數位寶貝資料
        self.tableName = 'digimons'
        self.tableHeader = ['序号', '名字', '圖檔連結', '等級', '類型',
                            '屬性', '所屬', '适應領域', '首次登場', '名字來源', '必殺技1', '必殺技2']

    def first_process(self):
        r = requests.get(self.target)
        r.encoding = r.apparent_encoding
        html = r.text
        div_bf = BeautifulSoup(html, features='html.parser')
        for place in self.palces:
            if place == self.palces[0]:
                name = self.levelClass[0]
            elif place == self.palces[1]:
                name = self.levelClass[1]
            elif place == self.palces[2]:
                name = self.levelClass[2]
            elif place == self.palces[3]:
                name = self.levelClass[3]
            elif place == self.palces[4]:
                name = self.levelClass[4]
            elif place == self.palces[5]:
                name = self.levelClass[5]
            elif place == self.palces[6]:
                name = self.levelClass[6]
            elif place == self.palces[7]:
                name = self.levelClass[7]
            elif place == self.palces[8]:
                name = self.levelClass[8]
            else:
                name = self.levelClass[9]
            self.divs.append(div_bf.find_all('li', class_=name))
        print("等級共有:", len(self.divs))

        self.get_Pokemon()

    def get_digimon_data(self):
        k = 0
        for i, url in enumerate(self.urls):
            try:
                k = k+1
                print('擷取圖檔位址中…… {}%'.format(k*100/len(self.urls)), end='\r')
                r = requests.get(url[0])
                r.encoding = r.apparent_encoding
                html = r.text
                div_bf = BeautifulSoup(html, features='html.parser')
                # 擷取頁面資源完畢,擷取圖檔連結開始
                imageDiv = div_bf.find('div', class_='digimon_img')
                image = imageDiv.find('img')
                if image == None:
                    print(url[1], "沒找到圖檔")

                image_address = url[0][0:url[0].rfind(
                    '/')+1] + image.get('src')
                self.image.append((image_address, url[1]))
                self.down.append((image_address, k-1))
                # 擷取圖檔連結完畢,擷取資料資源開始
                datas = div_bf.find_all('tr')
                tmp_datas = []
                for i in range(len(datas)):
                    tmp_datas.append(datas[i].find('td').get_text())
                self.datas.append(tmp_datas)
            except Exception as e:
                print(e)

        with open('urls.txt', 'w') as f:
            for item in self.down:
                f.write(str(item) + '\n')

    def get_Pokemon(self):
        print("get_Pokemon")
        for index, adiv in enumerate(self.divs):
            l = []
            k = 0
            if(index == 0):
                level = self.palces[0]
            elif(index == 1):
                level = self.palces[1]
            elif(index == 2):
                level = self.palces[2]
            elif(index == 3):
                level = self.palces[3]
            elif(index == 4):
                level = self.palces[4]
            elif(index == 5):
                level = self.palces[5]
            elif(index == 6):
                level = self.palces[6]
            elif(index == 7):
                level = self.palces[7]
            elif(index == 8):
                level = self.palces[8]
            else:
                level = '\"數位獸第六部\"'

            print('擷取 '+level+' 數位寶貝資訊中')

            for li in adiv:
                # print('擷取'+level+'數位寶貝資訊中…… {} %'.format(k*100/len(adiv)), end='\r')
                k = k+1
                tmp = []
                tmp_url = []
                tmp_level = []

                label = li('a')

                tmp.append(label[0].get_text())
                tmp_url.append(label[0].get('href'))
                tmp_level.append(level)

                l.append(tmp)
                try:
                    if tmp[0] not in self.palces and tmp[0] != None:
                        self.urls.append((self.root + tmp_url[0], tmp[0]))
                        self.level.append(index)
                        self.names.append(tmp[0])
                except Exception as e:
                    print(e)

            self.digimon.extend(l)

        print("數位寶貝總數:", len(self.digimon))
        print("url數:", len(self.urls))
        self.get_digimon_data()

        with open('names.txt', 'w') as f:
            for item in self.names:
                f.write(item+'\n')

        print(len(self.image))

    def get_image(self):
        print("擷取圖檔中")
        imageRoot = './image'
        k = 0
        for index, url in enumerate(self.image):
            k = k+1
            address = url[0]
            name = url[1]
            levelPath = imageRoot + '/' + str(self.level[index])
            picture = levelPath + '/' + name + '.jpg'
            try:
                if not os.path.exists(imageRoot):
                    os.mkdir(imageRoot)
                if not os.path.exists(levelPath):
                    os.mkdir(levelPath)
                if not os.path.exists(picture):
                    r = requests.get(address)
                    with open(picture, 'wb') as f:
                        f.write(r.content)
                        print('圖檔儲存成功,{}%'.format(k*100/len(self.image)))
                else:
                    print('檔案已存在')
            except Exception:
                print('爬取失敗')

    def save_as_xlsx(self):
        # TODO 解決“必須目前沒有digimons.xls才可以儲存”的問題
        print("儲存數位寶貝資料中")
        file = Workbook(encoding='utf-8')
        table = file.add_sheet(self.tableName)

        for i, item in enumerate(self.tableHeader):
            table.write(0, i, item)

        for i, data in enumerate(self.datas):
            table.write(i+1, 0, i+1)
            table.write(i+1, 1, self.names[i])
            table.write(i+1, 2, self.down[i][0])
            for j, cell in enumerate(data):
                table.write(i+1, j+3, cell)
        file.save(self.tableName+'.xls')


if __name__ == "__main__":

    target = downloader()
    target.first_process()
    target.get_image()
    target.save_as_xlsx()

           

這裡使用了beautifulsoup來解析網頁

分析

我決定按照等級(成長期、完全體等)把數位寶貝分類,而不是按照網頁裡的首次登場年份分類

分析網頁

目标網頁

【Python爬蟲】爬取數位寶貝digimon資料庫

可以看到,所有數位寶貝都放在了一個 id="digimon_list"的ul裡,按照年份分成了一個個塊,每一個數位寶貝都在li标簽中,li标簽裡的class屬性儲存了數位獸的等級,a标簽裡儲存了這個數位獸的詳細連結和名字。

是以我們首先要得到id="digimon_list"的ul标簽,在其中找到所有的li标簽,在li标簽中找到數位獸的詳細連結、名字、等級。我們進入到數位獸詳細頁面

【Python爬蟲】爬取數位寶貝digimon資料庫

可以看到,數位獸圖檔連結放在了id="digimon_img"的div裡的img标簽裡,然後所有資訊都放在tr标簽裡,是以我們頁面裡的相應div和tr就可以了。

步驟

  1. 解析首頁,擷取數位寶貝清單裡各自單獨的詳細資料連結
  2. 分别解析數位寶貝圖檔位址,各種資料
  3. 下載下傳圖檔并儲存
  4. 儲存資料和圖檔連結

總結

通過各種百度,玩玩自己想做的東西還挺開心的。

代碼原檔案

參考資料

  1. 【網絡爬蟲】爬取神奇寶貝Pokemon圖鑒圖檔大全 by.Memory逆光
  2. 數位獸資料庫(資料來源)
  3. Python基礎教程
  4. Python爬蟲實戰教程:批量爬取某網站圖檔 UP-python學習者