手把手教你分析中超16家球队谁是老大

发现一个好玩的数据分析项目《数据告诉你,中超16家球队谁是老大》,
原文链接:https://mp.weixin.qq.com/s/-csfuvlb8xwTsD8p1VQxXg
采集懂球帝网站上的中超16家球队球员的能力信息,分析出,各球队之间的纸面数据差距,依据数据预测出夺冠希望最大的No.1。

整个项目,思路清晰,数据简单,是入门数据分析的不二法门。
中超足球,也是大家期望值比较大的实用项目。
因为朋友是个资深的足球迷,资深到能够半夜三四点定闹钟起来看世界杯的比赛,所以对足球也有一丝丝的兴趣。
去年刷爆的C罗,以及经常听的梅西,还有各个球队间的恩怨情仇,都是有意思的故事。

原文中的项目实现分为两部分:数据采集,数据分析。

1.数据采集

本次项目的数据来源是懂球帝的官方网站。特此说明,本项目仅供学习研究,不用做任何商业用途。

1.1获取中超球队列表

手把手教你分析中超16家球队谁是老大_第1张图片
分析html标签知道每支球队的详情页链接位于class='team’的td标签下的a超链接的href属性。

def ChineseSuper_team(self):
    """
    中超球队列表
    :return:
    """
    url = "https://www.dongqiudi.com/data?competition=51"
    res = self.parse_url(url)

    element = Selector(res)

    # 中超球队列表
    teams = element.xpath("//td[@class='team']/a/@href").extract()
    return teams

parse_url()函数是封装的发送请求获取响应的功能,传入请求url返回得到的响应,后续项目需要请求响应功能时,可以避免重复代码。
通过requests+scrapy.Selector,获取网页的element对象,确定了中超球队的标签位置,使用xpath获取中超球队的具体链接列表。

1.2获取各队球员的详情链接

球员的详情链接获取同球队的获取方式相同。
在这里插入图片描述
打开浏览器的调试模式发现了一个意料之外的问题。html文件中不存在球员的详情链接,对应球员姓名的是球员照片的jpg链接。
原文中使用selenium开发者调试工具,模拟打开网页,模拟点击获取到的球员链接。selenium的效率以及资源使用率消耗过大了,我一般是不推荐使用的。(特殊情况除外,当然也根据个人喜欢,青菜萝卜,各有所爱嘛。)

不用调试工具,html中又没有球员链接。可以肯定的是,球员的链接是在网页点击时,通过某个方法生成。去哪里找到这个方法呢?

这里我们需要使用到逆推的思想。打开球员的详情链接,通过比较,发现不同的球员链接变化的只有最后的一串数字。
可以推测出,球员的详情链接组成方式为:

https://www.dongqiudi.com/player/ + 球员id,例如:广州恒大淘宝的前锋韦世豪的详情链接为:https://www.dongqiudi.com/player/50222265,最后的50222265就是韦世豪的唯一标识符。

在球队源码中搜索韦世豪的球员id:

在这里插入图片描述
发现球员信息存在于一个类似于字典的字符串中,其中person_id就是我们需要的球员id。

1.3 获取球员的详细信息

接下来的思路很简单,获取球队中的所有person_id,拼接成具体的球员详情页url。

def footballer(self, team_url):
    """
    足球运动员id列表url
    :return:
    """
    res = self.parse_url(team_url)
    # 俱乐部信息
    team_content = self.team_content(res)
    club_name = team_content[0]
    print(f"俱乐部名字是:{club_name}")
    # 球员id列表
    r = re.compile("teamMemberData:(.*),relatedNewsList:", re.S)
    info = r.search(res.text)
    footballer_info = info.group(1)

    r2 = re.compile('person_id:"(.*?)",', re.S)
    footballer_list = r2.findall(footballer_info)

    # 球员信息链接
    qiuyuan_url = "https://www.dongqiudi.com/player/{}"
    footballer_url = [qiuyuan_url.format(i) for i in footballer_list]
    # 获取球员信息
    # print(footballer_url)
    content_list = (self.footballer_content(footballer) for footballer in footballer_url)
    content_list = [content for content in content_list if content is not None]
    # print(content_list)
    # 信息写入excel表格
    file_path = pathlib.Path.cwd().joinpath('file', f"中超球队信息.xlsx")
    print(file_path)
    self.write_excel(file_path, content_list)

通过parse_url()函数获取球队信息页的网页response,获取源码,使用re双重正则过滤出当前球队的所有球员person_id。
使用字符串拼接生成球员详情链接的列表。
调用self.footballer_content()函数,以球员详情链接url作为参数,获取球员的具体数据值。
并存入excel表格。

def footballer_content(self, footballer_url):
    """
    球员信息
    :return:
    """
    # footballer_url = "https://www.dongqiudi.com/player/50006963"

    res = self.parse_url(footballer_url)
    element = Selector(res)
    # 球员信息
    content_list = self.get_content(element)

    # 球员能力评分
    ability_list = self.footballer_ability(element)

    # print(content_list, 11)
    # print(ability_list, 22)
    if content_list and ability_list:
        content_list.extend(ability_list)
        # print(content_list)
        # print(footballer_url, 111)

        return content_list

球员的具体信息分为两部分:球员信息,和球员能力评分
手把手教你分析中超16家球队谁是老大_第2张图片

get_content()函数获取球员信息,football_ability()函数获取球员能力评分。

def get_content(self, element):
    """
    球员信息列表
    :return:
    """

    # 姓名
    name = element.xpath("//p[@class='china-name']/text()").extract_first()
    # 俱乐部
    club = element.xpath("//*[contains(text(),'俱乐部:')]/parent::*/text()").extract()
    club = "".join(club).strip()
    # print(club)
    # 位置
    position = element.xpath("//*[contains(text(),'位   置:')]/parent::*/text()").extract_first()
    if position is None or "教练" in position:
        return []

    # 号码
    number = element.xpath("//*[contains(text(),'号   码:')]/parent::*/text()").extract_first()
    # 国籍
    nationnalty = element.xpath("//*[contains(text(),'国   籍:')]/parent::*/text()").extract_first()
    # 年龄
    age = element.xpath("//*[contains(text(),'年   龄:')]/parent::*/text()").extract_first()
    # 生日
    birthday = element.xpath("//*[contains(text(),'生   日:')]/parent::*/text()").extract_first()
    # 身高
    height = element.xpath("//*[contains(text(),'身   高:')]/parent::*/text()").extract_first()
    # 体重
    weight = element.xpath("//*[contains(text(),'体   重:')]/parent::*/text()").extract_first()
    # 惯用脚
    foot = element.xpath("//*[contains(text(),'惯用脚:')]/parent::*/text()").extract_first()
    return [name, club, position, number, nationnalty, age, birthday, height, weight, foot]




 def footballer_ability(self, element):
        """
        球员能力列表
        :return:
        """
        # 综合能力
        synthetic = element.xpath("//*[contains(text(),'综合能力')]/*/text()").extract_first()

        if synthetic is None:
            return []

        # 速度
        speed = element.xpath("//*[contains(text(),'速度')]/*/text()").extract_first()

        # 射门
        shoot = element.xpath("//*[contains(text(),'射门')]/*/text()").extract_first()

        if shoot is None:
            # 门将能力
            # 扑救
            put_ball = element.xpath("//*[contains(text(),'扑救')]/*/text()").extract_first()
            # 手型
            hand_shape = element.xpath("//*[contains(text(),'手型')]/*/text()").extract_first()
            # 开球
            open_ball = element.xpath("//*[contains(text(),'开球')]/*/text()").extract_first()
            # 反应
            reaction = element.xpath("//*[contains(text(),'反应')]/*/text()").extract_first()
            # 位置
            location = element.xpath("//*[contains(text(),'位置')]/*/text()").extract_first()
            return [synthetic, speed, put_ball, hand_shape, open_ball, reaction, location]

        # 传球
        passing = element.xpath("//*[contains(text(),'传球')]/*/text()").extract_first()

        # 盘带
        dribbling = element.xpath("//*[contains(text(),'盘带')]/*/text()").extract_first()

        # 防守
        defense = element.xpath("//*[contains(text(),'防守')]/*/text()").extract_first()

        # 力量
        power = element.xpath("//*[contains(text(),'力量')]/*/text()").extract_first()
        return [synthetic, speed, shoot, passing, dribbling, defense, power]

需要注意的问题有三个:
1.我们只需要球员的信息,需要将位置为教练的过滤掉。
2.位置不同,能力评分的标准不同。最明显的问题是 其他位置例如前锋,中场的能力评分和门将的能力评分信息不一样。需要单独判断。
3.没有能力评分的球员,无法为数据分析提供帮助,需要过滤掉。

没有能力评分的球员,例如:广州恒大淘宝的门将张建智。球员信息页如下:
手把手教你分析中超16家球队谁是老大_第3张图片

1.4 各球队球员信息分表统计,写入excel表格
def write_excel(self, file_path, content_list):
    """
    数据写入excel表格
    :return:
    """

    # 数据写入excel表格
    try:

        df = pd.read_excel(file_path, header=None)
        ds = pd.DataFrame(content_list)
        print(ds)
        df = df.append(ds, ignore_index=True)
        df.to_excel(file_path, index=False, header=False)
    except Exception as e:
        # 创建excel表格
        print("表格不存在,创建表格")
        columns = ['姓名', '俱乐部', '位置', '号码', '国籍', '年龄', '生日', '身高', '体重', '惯用脚', '综合能力', '速度', '射门(扑救)', '传球(手型)',
                   '盘带(开球)', '防守(反应)',
                   '力量(位置)']
        df = pd.DataFrame(columns=columns)
        df.to_excel(file_path, index=False)
        self.write_excel(file_path, content_list)

使用Pandas,将数据写入excel表格,捕获写入表格异常,表格不存在,先创建excel表格,再重新写入数据。

这里涉及到pandas对excel表格的数据追加问题。
前面写过一篇pandas对excel表格的数据追加问题。有兴趣的可以研究一下。https://blog.csdn.net/mygodit/article/details/97640770

生成的excel格式如下:
在这里插入图片描述

16支球队的总数据量是346条。这里筛选了教练,以及没有能力评分的球员

2.数据分析

数据分析的需求是:
手把手教你分析中超16家球队谁是老大_第4张图片

2.1各球队综合得分的平均值。柱状图
2.1.1获取综合能力的平均值,和与之对应的球队列表。
def read_excel(self):
    """
    读取excel表格数据
    :return:返回俱乐部集合,各球队综合能力的平均值
    """

    # 俱乐部综合能力平均值
    ability_mean_dict = {ds: int(self.df.get(self.df['俱乐部'] == ds)['综合能力'].mean()) for ds in self.team_list}

    # 依据平均值排序
    a_order = sorted(ability_mean_dict.items(), key=lambda x: x[1], reverse=True)

    # 获取对应的值
    team_list = [team[0] for team in a_order]
    ability_mean = [team[1] for team in a_order]

    # 各球队前锋的值
    # ability_mean_dict = {ds: int(df.get(df['俱乐部'] == ds)['前锋'].mean()) for ds in set(team_set)}

    # print(a_order)
    print(team_list)
    # print(ability_mean)

    return team_list, ability_mean

read_excel()函数,返回俱乐部集合,各球队综合能力的平均值。
使用pandas的Dataframe格式数据操作方法,依据球队名获取综合能力值。Dataframe的mean()方法获取数值的平均值。

根据综合能力值进行排序。reverse=True,从大到小。

获取到排序后的队名列表和综合能力列表。

2.1.2 生成柱状图
def create_zhuzhuang(self, team_list, ability_mean):
    """
    创建柱状图,记录各球队综合能力
    :return:
    """
    v1 = ability_mean
    x1 = team_list
    print(x1)
    print(v1)
    bar = (
        Bar()
            .add_xaxis(x1)
            .add_yaxis("中超球队综合得分", v1, category_gap="50%", color='green')
            .set_global_opts(title_opts=opts.TitleOpts(title="懂球帝球队信息分析")))

    bar.render("中超球队综合得分柱状图.html")

create_zhuzhuang()函数传入x轴参数球队列表和y轴参数ability_mean综合能力平均值。

生成柱状图使用pyecharts第三方库的Bar,具体实现原理,有兴趣的请面向百度编程。

生成的柱状图如下:

手把手教你分析中超16家球队谁是老大_第5张图片

2.2 后卫能力散点图
2.2.1 获取球队后卫球员的平均盘带,平均传球,平均速度得分
def get_full_back(self):
    """
    获取球队后卫球员的平均防守,平均传球
    :return:
    """
    full_back_list = [
        list(map(lambda x: int(self.df.get(self.df['俱乐部'] == team).get(self.df['位置'] == '后卫')[x].mean()),
                 ["防守(反应)", '传球(手型)','速度 '])) for
        team in self.team_list]
    return full_back_list

使用Pandas读取excel表格。依据俱乐部名称,位置==后卫,从excel表格获取到各球队后卫球员的平均盘带,平均传球,平均速度数据。
生成列表返回。

2.2.2 生成后卫球员散点图
  def create_full_back_sandian(self, full_back_list):
    """
    创建后卫球员平均防守,平均传球,平均速度,综合能力的散点图
    :param full_back_list:
    :return:
    """
    # print(full_back_list)
    # 加入后卫球员综合能力
    full_back_ability = self.get_ability(full_back_list)
    # 球队,能力对应
    full_back_dict = dict(zip(self.team_list, full_back_ability))
    new_dict = sorted(full_back_dict.items(), key=lambda x: x[1][3], reverse=True)
    # print(new_dict)
    print(full_back_ability)

    # x轴队名
    x_data = [team[0] for team in new_dict]

    # y轴 平均防守
    defense_list = [team[1][0] for team in new_dict]

    # y轴 平均传球
    passing_list = [team[1][1] for team in new_dict]

    # y轴 平均速度
    speed_list = [team[1][1] for team in new_dict]
    # y轴 后卫能力
    full_back_ability_list = [team[1][3] for team in new_dict]
    (
        Scatter(init_opts=opts.InitOpts(width='1900px', height='500px', page_title="中超后卫球员能力散点图"))
            .add_xaxis(x_data)
            .add_yaxis(
            series_name="后卫球员平均防守能力",
            y_axis=defense_list,
            symbol_size=20,
            symbol=None,
            label_opts=opts.LabelOpts(is_show=False),
        )
            .add_yaxis(
            series_name="后卫球员平均传球能力",
            y_axis=passing_list,
            symbol=None,
            symbol_size=20,
            label_opts=opts.LabelOpts(is_show=False),
        )
            .add_yaxis(
            series_name="后卫球员平均速度能力",
            y_axis=speed_list,
            symbol_size=20,
            symbol=None,
            label_opts=opts.LabelOpts(is_show=False)
        )
            .add_yaxis(
            series_name="后卫球员综合能力",
            y_axis=full_back_ability_list,
            symbol=None,
            symbol_size=20,
            label_opts=opts.LabelOpts(is_show=False),
        )
            .set_global_opts(
            title_opts=opts.TitleOpts(title='中超后卫球员能力散点图'),
            xaxis_opts=opts.AxisOpts(splitline_opts=opts.SplitLineOpts(is_show=True)),
            yaxis_opts=opts.AxisOpts(splitline_opts=opts.SplitLineOpts(is_show=True))
        )
            .render('中超后卫球员能力散点图.html')
    )

create_full_back_sandian()函数接收后卫球员能力列表作为参数,生成后卫球员能力的散点图。

由于散点图生成的比较混乱,看不出来具体球队后卫球员的能力,因此,调用self.get_ability()函数,生成后卫球员能力的综合值。作为散点图排序的参数。

x轴为排序后的队名,y轴分别为平均防守能力散点图,平均传球能力散点图,综合能力散点图。

散点图使用pyecharts的Scatter():

手把手教你分析中超16家球队谁是老大_第6张图片

球队前锋,球队中场,球队门将的能力散点图生成同后卫球员的生成方式相似。

手把手教你分析中超16家球队谁是老大_第7张图片

至此,我们做完了中超16只球队的数据分析。

至于是不是如原文所说,广州恒大淘宝的夺冠概率最大,咱也不知道,咱也不敢问啊。

你可能感兴趣的:(数据分析)