JFLの試合結果から順位表を作成

# はじめに

日本フットボールリーグオフィシャルWebサイトの[日程・結果](http://www.jfl.or.jp/jfl-pc/view/s.php?a=1542&f=2020A001_spc.html)から試合結果をスクレイピング

# スクレイピング

```python
import requests
from bs4 import BeautifulSoup

url = "http://www.jfl.or.jp/jfl-pc/view/s.php?a=1542&f=2020A001_spc.html"

r = requests.get(url)
r.raise_for_status()

soup = BeautifulSoup(r.content, "html.parser")

data = []

for table in soup.find_all("table", class_="table-data"):

    trs = table.find_all("tr")

    th = int(trs[0].th.get_text(strip=True).strip("第節"))

    for i, tr in enumerate(trs[1:], 1):

        tds = [td.get_text(strip=True) for td in tr.find_all("td")]

        data.append([th, i] + tds)
```

# データラングリング
```python
import pandas as pd

df = pd.DataFrame(
    data, columns=["節", "番号", "日にち", "時間", "ホーム", "スコア", "アウェイ", "スタジアム", "備考"]
)

df.set_index(["節", "番号"], inplace=True)

df

df_score = (
    df["スコア"].str.split("-", expand=True).rename(columns={0: "ホーム得点", 1: "アウェイ得点"})
)

df_score["ホーム得点"] = pd.to_numeric(df_score["ホーム得点"], errors="coerce").astype("Int64")
df_score["アウェイ得点"] = pd.to_numeric(df_score["アウェイ得点"], errors="coerce").astype("Int64")

df1 = pd.concat([df, df_score], axis=1).dropna(subset=["ホーム得点", "アウェイ得点"])

# ホームの結果のみ
df_home = df1.loc[:, ["ホーム", "ホーム得点", "アウェイ得点"]].copy()
df_home.rename(columns={"ホーム": "チーム名", "ホーム得点": "得点", "アウェイ得点": "失点"}, inplace=True)
df_home["戦"] = "ホーム"
df_home.head()

# アウェイの結果のみ
df_away = df1.loc[:, ["アウェイ", "アウェイ得点", "ホーム得点"]].copy()
df_away.rename(columns={"アウェイ": "チーム名", "アウェイ得点": "得点", "ホーム得点": "失点"}, inplace=True)
df_away["戦"] = "アウェイ"
df_away.head()

# ホームとアウェイを結合
df_total = pd.concat([df_home, df_away])

# 得失点を計算
df_total["得失点"] = df_total["得点"] - df_total["失点"]


# 勝点追加
df_total["勝点"] = df_total["得失点"].apply(lambda x: 1 if x == 0 else 0 if x < 0 else 3)
df_total["勝敗"] = df_total["勝点"].replace({0: "敗戦", 1: "引分", 3: "勝利"})

df_total.head()

# 得点・失点・得失点・勝点　集計
pv_score = df_total.pivot_table(
    index="チーム名", values=["得点", "失点", "得失点", "勝点"], aggfunc=sum
)

pv_score

# 勝敗集計
pv_wl = pd.crosstab(df_total["チーム名"], [df_total["戦"], df_total["勝敗"]])
pv_wl

# 列名変更
# pv_wl.columns = ["勝利A", "引分A", "敗戦A", "勝利H", "引分H", "敗戦H"]

pv_wl.rename(columns={"アウェイ": "A", "ホーム": "H"}, inplace=True)
pv_wl.columns = pv_wl.columns.swaplevel(0, 1)
pv_wl.columns = ["".join(col).strip() for col in pv_wl.columns.values]

# 合計追加
pv_wl["勝利"] = pv_wl["勝利H"] + pv_wl["勝利A"]
pv_wl["引分"] = pv_wl["引分H"] + pv_wl["引分A"]
pv_wl["敗戦"] = pv_wl["敗戦H"] + pv_wl["敗戦A"]

# 試合数追加
pv_wl["試合数"] = pv_wl["勝利"] + pv_wl["引分"] + pv_wl["敗戦"]

# 確認
pv_wl

df2 = df_total.copy()

# 評価値を作成
df2["評価値"] = ((df2["勝点"]) * 10000) + (df2["得失点"] * 100) + df2["得点"]
df2

# 評価値集計
df3 = df2.pivot_table(
    values="評価値", index="チーム名", columns="節", aggfunc=sum, fill_value=0
)
df3

# 評価値の累計和
df_eval = df3.apply(lambda d: d.cumsum(), axis=1)
df_eval

# 評価値から順位作成
df_chart = df_eval.rank(ascending=False, method="min").astype(int)
df_chart.sort_values(by=df_chart.columns[-1], inplace=True)
df_chart

# 最終順位取得
s1 = df_chart.iloc[:, -1]
s1.name = "順位"

df_diff = df_chart.diff(axis=1).fillna(0).astype(int)
df_diff

#  前節差分
s2 = df_diff.iloc[:, -1].apply(lambda x: "－" if x == 0 else "▼" if x < 0 else "▲")
s2.name = "前節"
s2

# 全部を結合
df4 = pd.concat([pv_score, pv_wl], axis=1).join([s1, s2])

# 順位で昇順
df4.sort_values(["順位"], inplace=True)

df_rank = df4.reset_index().loc[
    :,
    [
        "前節",
        "順位",
        "チーム名",
        "勝点",
        "試合数",
        "勝利",
        "勝利H",
        "勝利A",
        "引分",
        "引分H",
        "引分A",
        "敗戦",
        "敗戦H",
        "敗戦A",
        "得失点",
        "得点",
        "失点",
    ],
]

df_rank

print(df_rank.to_markdown(index=False))
```

# 順位表

2020/10/07現在

| 前節   |   順位 | チーム名               |   勝点 |   試合数 |   勝利 |   勝利H |   勝利A |   引分 |   引分H |   引分A |   敗戦 |   敗戦H |   敗戦A |   得失点 |   得点 |   失点 |
|:-------|-------:|:-----------------------|-------:|---------:|-------:|--------:|--------:|-------:|--------:|--------:|-------:|--------:|--------:|---------:|-------:|-------:|
| －     |      1 | Ｈｏｎｄａ ＦＣ        |     18 |        8 |      5 |       2 |       3 |      3 |       2 |       1 |      0 |       0 |       0 |       11 |     15 |      4 |
| －     |      2 | ヴェルスパ大分         |     15 |        7 |      5 |       2 |       3 |      0 |       0 |       0 |      2 |       2 |       0 |        5 |     14 |      9 |
| －     |      3 | テゲバジャーロ宮崎     |     14 |        7 |      4 |       1 |       3 |      2 |       1 |       1 |      1 |       1 |       0 |        6 |     11 |      5 |
| －     |      4 | ＭＩＯびわこ滋賀       |     13 |        8 |      4 |       2 |       2 |      1 |       1 |       0 |      3 |       1 |       2 |        4 |     17 |     13 |
| －     |      5 | いわきＦＣ             |     13 |        8 |      4 |       3 |       1 |      1 |       0 |       1 |      3 |       1 |       2 |        0 |     14 |     14 |
| －     |      6 | ソニー仙台ＦＣ         |     13 |        8 |      4 |       1 |       3 |      1 |       1 |       0 |      3 |       1 |       2 |        0 |     13 |     13 |
| －     |      7 | 松江シティＦＣ         |     12 |        9 |      4 |       3 |       1 |      0 |       0 |       0 |      5 |       1 |       4 |       -5 |     10 |     15 |
| ▼      |      8 | ＦＣ大阪               |     11 |        8 |      3 |       2 |       1 |      2 |       2 |       0 |      3 |       1 |       2 |        3 |     12 |      9 |
| ▲      |      9 | 奈良クラブ             |     11 |        8 |      3 |       1 |       2 |      2 |       1 |       1 |      3 |       2 |       1 |        2 |     10 |      8 |
| ▲      |     10 | 鈴鹿ポイントゲッターズ |     11 |        8 |      3 |       2 |       1 |      2 |       0 |       2 |      3 |       3 |       0 |        0 |      9 |      9 |
| ▲      |     11 | ホンダロックＳＣ       |     10 |        8 |      3 |       0 |       3 |      1 |       0 |       1 |      4 |       4 |       0 |       -7 |      8 |     15 |
| －     |     12 | 東京武蔵野シティＦＣ   |      8 |        6 |      2 |       2 |       0 |      2 |       0 |       2 |      2 |       1 |       1 |        1 |      7 |      6 |
| －     |     13 | ヴィアティン三重       |      8 |        8 |      2 |       1 |       1 |      2 |       1 |       1 |      4 |       2 |       2 |       -3 |      8 |     11 |
| －     |     14 | ラインメール青森       |      8 |        7 |      2 |       1 |       1 |      2 |       1 |       1 |      3 |       1 |       2 |       -5 |      7 |     12 |
| －     |     15 | ＦＣマルヤス岡崎       |      5 |        8 |      1 |       0 |       1 |      2 |       1 |       1 |      5 |       3 |       2 |       -5 |      7 |     12 |
| －     |     16 | 高知ユナイテッドＳＣ   |      3 |        8 |      0 |       0 |       0 |      3 |       2 |       1 |      5 |       2 |       3 |       -7 |      9 |     16 |


# つづき

+ [JFLの試合結果から戦績表を作成](https://qiita.com/barobaro/items/5dec246a8dac6f2903f4)