坦克世界WOT知识图谱三部曲之爬虫篇

news2024/11/19 21:23:14

文章目录

  • 关于坦克世界
  • 1. 爬虫任务
  • 2. 获取坦克列表
  • 3. 获取坦克具体信息
  • 结束语

关于坦克世界

  《坦克世界》(World of Tanks, WOT)是我在本科期间玩过的一款战争网游,由Wargaming公司研发。2010年10月30日在俄罗斯首发,2011年4月12日在北美和欧洲推出,2011年3月15日在中国由空中网代理推出(2020年,国服由360代理)。游戏背景设定在二战时期,玩家会扮演1930到1960年代的战车进行对战,要求战略和合作性,游戏中的战车根据历史高度还原。

  坦克世界官网:https://wotgame.cn/
  坦克世界坦克百科:https://wotgame.cn/zh-cn/tankopedia/#wot&w_m=tanks

在这里插入图片描述

1. 爬虫任务

在这里插入图片描述
  当前的WOT有五种坦克类型,11个系别。我们要构建一个关于坦克百科的知识图谱,接下来就要通过爬虫来获取所有坦克的详细信息,比如坦克的等级、火力、机动性、防护能力、侦察能力等等。以当前的八级霸主中国重型坦克BZ-176为例,坦克的详细信息如下:

在这里插入图片描述
在这里插入图片描述

2. 获取坦克列表

在这里插入图片描述
  常规操作,F12+F5查看一下页面信息,定位到坦克列表的具体请求:

在这里插入图片描述
  是一个POST请求,返回的是一个JSON格式的数据,包含了该类型坦克的一些基本信息:

在这里插入图片描述
  POST请求参数如下:

在这里插入图片描述

  特别说明一下:构建该请求header时,Content-Length参数是必须的。

  代码实现:

# -*- coding: utf-8 -*-
# Author  : xiayouran
# Email   : youran.xia@foxmail.com
# Datetime: 2023/9/29 22:43
# Filename: spider_wot.py
import os
import time
import json
import requests


class WOTSpider:
    def __init__(self):
        self.base_headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/117.0.0.0 Safari/537.36',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.9',
        }
        self.post_headers = {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Content-Length': '135',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        }
        self.from_data = {
            'filter[nation]': '',
            'filter[type]': 'lightTank',
            'filter[role]': '',
            'filter[tier]': '',
            'filter[language]': 'zh-cn',
            'filter[premium]': '0,1'
        }

        self.tank_list_url = 'https://wotgame.cn/wotpbe/tankopedia/api/vehicles/by_filters/'
        self.tank_label = ['lightTank', 'mediumTank', 'heavyTank', 'AT-SPG', 'SPG']
        self.tanks = {}

    def parser_tanklist_html(self, html_text):
        json_data = json.loads(html_text)
        for data in json_data['data']['data']:
            self.tanks[data[0] + '_' + data[4]] = {
                'tank_nation': data[0],
                'tank_type': data[1],
                'tank_rank': data[3],
                'tank_name': data[4],
                'tank_name_s': data[5],
                'tank_url': data[6],
                'tank_id': data[7]
            }
            
    def run(self):
        for label in self.tank_label:
            self.from_data['filter[type]'] = label
            html_text = self.get_html(self.tank_list_url, method='POST', from_data=self.from_data)
            if not html_text:
                print('[{}] error'.format(label))
                continue
            self.parser_tanklist_html(html_text)
            time.sleep(3)
        self.save_json(os.path.join(self.data_path, 'tank_list.json'), self.tanks)


if __name__ == '__main__':
    tank_spider = WOTSpider()
    tank_spider.run()

  上述代码只实现了一些重要的函数及变量声明,完整的代码可以从github上拉取:WOT

3. 获取坦克具体信息

  坦克具体信息的页面就是一个纯HTML页面了,一个GET请求就可以获得。当然啦,具体怎么分析的就不细说了,对爬虫技术感兴趣的同学们可以找找资料,这里就只说一下抓取流程。
  先分析GET请求:https://wotgame.cn/zh-cn/tankopedia/60209-Ch47_BZ_176/,可以分成三部分:
  Part 1:基本的url请求:https://wotgame.cn/zh-cn/tankopedia
  Part 2:坦克的idBZ-176坦克的id60209,每个坦克都是唯一的,这个参数通过上一个步骤的POST请求可以获取到;
  Part 3:坦克的名称:Ch47_BZ_176,这个参数也可以通过上一个步骤的POST请求可以获取到。
  这样就可以为每个坦克构造一个对应的url了,只需解析该url对应的界面即可。解析的时候我分成了两部分,先对坦克的基本信息进行解析,比如坦克系别、等级及价格等等,由BeautifulSoup库实现,坦克的具体信息,比如火力、机动、防护及侦察能力,这些信息是由JavaScript代码动态请求得到的,这里为了简便没有分析具体的js代码,而是先使用selenium库进行网页渲染,然后再使用BeautifulSoup库进行解析。这里不再细说,下面给出页面解析的代码:

# -*- coding: utf-8 -*-
# Author  : xiayouran
# Email   : youran.xia@foxmail.com
# Datetime: 2023/9/29 22:43
# Filename: spider_wot.py
import requests
from tqdm import tqdm
from bs4 import BeautifulSoup, Tag
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

class WOTSpider:
    def __init__(self):
        pass
        
    def is_span_with_value(self, driver):
        try:
            element = driver.find_element(By.XPATH, "//span[@data-bind=\"text: ttc().getFormattedBestParam('maxHealth', 'gt')\"]")
            data = element.text.strip()
            if data:
                return True
        except:
            return False

    def get_html_driver(self, url):
        self.driver.get(url)
        self.wait.until(self.is_span_with_value)
        page_source = self.driver.page_source

        return page_source

    def parser_tankinfo_html(self, html_text):
        tank_info = copy.deepcopy(self.tank_info)
        soup = BeautifulSoup(html_text, 'lxml')
        # tank_name = soup.find(name='h1', attrs={'class': 'garage_title garage_title__inline js-tank-title'}).strip()
        tank_statistic = soup.find_all(name='div', attrs={'class': 'tank-statistic_item'})
        for ts in tank_statistic:
            ts_text = [t for t in ts.get_text().split('\n') if t]
            if len(ts_text) == 5:
                tank_info['价格'] = {
                    '银币': ts_text[-3],
                    '经验': ts_text[-1]
                }
            else:
                tank_info[ts_text[0]] = ts_text[-1]

        tank_property1 = soup.find(name='p', attrs='garage_objection')
        tank_property2 = soup.find(name='p', attrs='garage_objection garage_objection__collector')
        if tank_property1:
            tank_info['性质'] = tank_property1.text
        elif tank_property2:
            tank_info['性质'] = tank_property2.text
        else:
            tank_info['性质'] = '银币坦克'

        tank_desc_tag = soup.find(name='p', attrs='tank-description_notification')
        if tank_desc_tag:
            tank_info['历史背景'] = tank_desc_tag.text

        tank_parameter = soup.find_all(name='div', attrs={'class': 'specification_block'})
        for tp_tag in tank_parameter:
            param_text = tp_tag.find_next(name='h2', attrs={'class': 'specification_title specification_title__sub'}).get_text()
            # spec_param = tp_tag.find_all_next(name='div', attrs={'class': 'specification_item'})
            spec_param = [tag for tag in tp_tag.contents if isinstance(tag, Tag) and tag.attrs['class'] == ['specification_item']]
            spec_info = {}
            for tp in spec_param:
                tp_text = [t for t in tp.get_text().replace(' ', '').split('\n') if t]
                if not tp_text or not tp_text[0][0].isdigit():
                    continue
                spec_info[tp_text[-1]] = ' '.join(tp_text[:-1])
            tank_info[param_text] = spec_info

        return tank_info

    def run(self):
        file_list = [os.path.basename(file)[:-5] for file in glob.glob(os.path.join(self.data_path, '*.json'))]

        for k, item in tqdm(self.tanks.items(), desc='Crawling'):
            file_name = k.replace('"', '').replace('“', '').replace('”', '').replace('/', '-').replace('\\', '').replace('*', '+')
            if file_name in file_list:
                continue
            tank_url = self.tank_url + str(item['tank_id']) + '-' + item['tank_url']
            html_text = self.get_html_driver(tank_url)
            # html_text = self.get_html(tank_url, method='GET')
            tank_info = self.parser_tankinfo_html(html_text)
            self.tanks[k].update(tank_info)
            self.save_json(os.path.join(self.data_path, '{}.json'.format(file_name)), self.tanks[k])
            time.sleep(1.5)
        self.save_json(os.path.join(self.data_path, 'tank_list_detail.json'), self.tanks)


if __name__ == '__main__':
    tank_spider = WOTSpider()
    tank_spider.run()

  大约半个小时即可获取全部的坦克信息,如下:

在这里插入图片描述

  Selenium 库依赖chromedriver,需要根据自己的Chrome浏览器版本下载合适的版本,chromedriver的官方下载地址为:https://chromedriver.chromium.org/downloads/version-selection

结束语

  本篇的完整代码及爬取的结果已经同步到仓库中,感兴趣的话可以拉取一下,下一篇文章就基于当前获取到的坦克信息来构造一个关于坦克百科的知识图谱。

开源代码仓库


  如果喜欢的话记得给我的GitHub仓库WOT点个Star哦!ヾ(≧∇≦*)ヾ


  公众号已开通:夏小悠,关注以获取更多关于Python文章、AI领域最新技术、LLM大模型相关论文及内部PPT等资料^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062386.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目标检测算法改进系列之Backbone替换为RepViT

RepViT简介 轻量级模型研究一直是计算机视觉任务中的一个焦点,其目标是在降低计算成本的同时达到优秀的性能。轻量级模型与资源受限的移动设备尤其相关,使得视觉模型的边缘部署成为可能。在过去十年中,研究人员主要关注轻量级卷积神经网络&a…

整体网络架构p22

1. 两次卷积,一次池化。得到一个三维特征图,然后让三维的特征图,三个值进行相乘拉成特征向量,把得到的结果需要靠全连接层。 带参数计算才算一层 算conv的个数FC全连接层就得到卷积神经网络的层数 FC:全连接层 2. 3.reset网络&a…

连接查询-多表联合查

一、连接查询的分类 根据表的连接方式,连接查询分为内连接、外连接和全连接。 内连接: 等值连接非等值连接自连接外连接: 左外连接(左连接)右外连接(右连接) 全连接 二、笛卡尔积 交叉连接也…

番外--Task1:

""" 重置root管理员密码添加yum源测试软件包安装成功 """ step1: 在shell界面输入重启命令; step2: 重启过程中出现此界面,快速按键盘‘e’, 进入系统内核程序; step3: 在系统内核程序内&#xff0c…

【MATLAB源码-第42期】基于matlab的人民币面额识别系统(GUI)。

操作环境: MATLAB 2022a 1、算法描述 基于 MATLAB 的人民币面额识别系统设计可以分为以下步骤: 1. 数据收集与预处理 数据收集: 收集不同面额的人民币照片,如 1 元、5 元、10 元、20 元、50 元和 100 元。确保在不同环境、不…

【算法学习】-【双指针】-【盛水最多的容器】

LeetCode原题链接:盛水最多的容器 下面是题目描述: 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。…

Net相关的各类开源项目

Net相关的各类开源项目 WPFHandyControlLive-ChartsWPFDeveloperswpf-uidesignStylet WebScheduleMasterYiShaAdminBlog.CoreNebula.AdminNewLife.CubeOpenAuth UnityuGUIUnityCsReferenceEpitomeMyUnityFrameWorkKSFrameworkTowerDefense-GameFramework-Demo 通用ClientServer…

目标检测算法改进系列之Backbone替换为InceptionNeXt

InceptionNeXt 受 Vision Transformer 长距离依赖关系建模能力的启发,最近一些视觉模型开始上大 Kernel 的 Depth-Wise 卷积,比如一篇出色的工作 ConvNeXt。虽然这种 Depth-Wise 的算子只消耗少量的 FLOPs,但由于高昂的内存访问成本 (memory…

机器学习必修课 - 编码分类变量 encoding categorical variables

1. 数据预处理和数据集分割 import pandas as pd from sklearn.model_selection import train_test_split导入所需的Python库 !git clone https://github.com/JeffereyWu/Housing-prices-data.git下载数据集 # Read the data X pd.read_csv(/content/Housing-prices-data/t…

CTFshow Web入门 文件上传

目录 web151 web152 web153 web154 web155 web156 web157 web158、web159 web160 web161 web162 web163 web164 web165 web166 web167 web168 web169 web170 web151 1. 写马改后缀为png上传,抓包修改文件信息 回显路径,蚁剑连接 2. …

仿函数的学习

仿函数 也叫 函数对象 仿函数是什么东西? 当你第一眼看到下面的代码的时候,你会觉得它是一个函数的调用: bool result less(a, b);但是我如果告诉你,less 是一个我自定义的一个类的对象呢? class Less { public:bo…

不标准的 json 格式的字符串如何转为标准的(json字符串属性名不带双引号如何转

背景 不规范的 json 字符串例如 属性名不带双引号 {name:"abc"}属性名带单引号而不是双引号 {name:"abc"}属性值该用双引号的时候用了单引号 {"name":abc}还有一种情况就是以上情况的混合 所谓规范的json字串就是属性名要用双引号&#xf…

13种改进粒子群优化算法 matlab2022 运行结果和耗时对比

前言 阅读粒子群优化算法的文章,发现代码不仅要付费而且还没有运行结果,需要自己手动写代码运行,这里提供下我的运行结果。包含参数结果和耗时对比。 https://www.bilibili.com/read/cv11905136/?spm_id_from333.999.0.0 % 定义适应度函数 …

Visual Studio Code 安裝

一、Visual Studio Code 安裝 VS Code 下载地址:https://code.visualstudio.com/ windows系统的快速下载地址:https://vscode.cdn.azure.cn/stable/441438abd1ac652551dbe4d408dfcec8a499b8bf/VSCodeUserSetup-x64-1.75.1.exe macOS系统的快速下载地址…

最短路径专题6 最短路径-多路径

题目: 样例: 输入 4 5 0 2 0 1 2 0 2 5 0 3 1 1 2 1 3 2 2 输出 2 0->1->2 0->3->2 思路: 根据题意,最短路模板还是少不了的, 我们要添加的是, 记录各个结点有多少个上一个结点走动得来的…

即时通讯软件

通信协议 发送消息可以是个struct 客户端分两个线程:读取服务器,给服务器发(否则会导致阻塞) read和write的第二个参数类型是:void *buf——————不仅仅是一个字符串,也可以是一个结构体等等&#xf…

【进程管理】初识进程

一.何为进程 教材一般会给出这样的答案: 运行起来的程序 或者 内存中的程序 这样说太抽象了,那我问程序和进程有什么区别呢?诶?这我知道,书上说,动态的叫进程,静态的叫程序。那么静态和动态又是什么意思…

JAVA面经整理(8)

一)为什么要有区,段,页? 1)页是内存和磁盘之间交互的基本单位内存中的值修改之后刷到磁盘的时候还是以页为单位的索引结构给程序员提供了高效的索引实现方式,不过索引信息以及数据记录都是记录在文件上面的,确切来说是…

buuctf-[GXYCTF2019]禁止套娃 git泄露,无参数rce

用dirsearch扫一下&#xff0c;看到flag.php 访问一下没啥东西&#xff0c;使用githack python2 GitHack.py http://8996e81f-a75c-4180-b0ad-226d97ba61b2.node4.buuoj.cn/.git/查看index.php <?php include "flag.php"; echo "flag在哪里呢&#xff1f;…

【Jmeter】性能测试脚本开发——性能测试环境准备、Jmeter脚本编写和执行

文章目录 一、常用的Jmeter元件二、性能测试环境准备三、编写Jmeter脚本四、执行测试脚本 一、常用的Jmeter元件 取样器-HTTP请求 作用&#xff1a;发送HTTP请求配置原件-HTTP请求默认值 作用&#xff1a;设置HTTP请求的默认参数配置原件-用户定义的变量 作用&#xff1a;定义…