天天看点

【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学习者