Open11
スマブラの特定キャラの勝率を出す
スマメイトで特定キャラのレート対戦結果を集計してそのキャラの有利不利を出す。
やること
- 各キャラ使いのページを取得
- 1.のURLから上位20人のユーザページを取得
- ユーザページから対戦結果を取得
- すべての対戦結果を集計して勝ち数、負け数を算出
- 勝率を出して、ソートする
- 各キャラ使いのページを取得
fighters.py
import requests
from bs4 import BeautifulSoup
url = 'https://smashmate.net/fighter/'
res = requests.get(url)
soup = BeautifulSoup(res.text, "html.parser")
result = soup.find('div', class_="smashlist")
fighters = result.find_all('a')
for fighter in fighters:
print(",".join([fighter.text.strip(), fighter.get('href')]))
python3 fighter.py > fighter.txt
# 取得結果はこんな感じ
head -n 3 fighter.txt
マリオ,https://smashmate.net/fighter/mario/
ドンキーコング,https://smashmate.net/fighter/donkey_kong/
リンク,https://smashmate.net/fighter/link/
1.のURLから上位20人のユーザページを取得
単キャラ使いでない場合はどっちで対戦したか分からないため除外する
top_ranker.py
import requests
from bs4 import BeautifulSoup
# 対象キャラのスマメイトの上位ランカー
url = 'https://smashmate.net/fighter/steve/'
# url = 'https://smashmate.net/fighter/kazuya/'
# url = 'https://smashmate.net/fighter/minmin/'
res = requests.get(url)
soup = BeautifulSoup(res.text, "html.parser")
rankers = soup.find_all('div', class_="row row-center va-middle row-nomargin")
for i, r in enumerate(rankers):
if i < 20:
characters = r.find('div', class_="col-xs-3")
# 単キャラ使いのみ集計
if len(characters.find_all("a")) == 1:
name = r.find('p', class_='name')
rating = r.find('div', class_="col-xs-2 text-center rate")
# 順位、名前、レート、ユーザーページを表示
print(",".join([str(i + 1), name.text, rating.text.strip(), name.find('a').get('href')]))
python3 top_ranker.py > minmin_ranker.txt
# 取得結果はこんな感じ
head -n 3 minmin_ranker.txt
1,オムアツ,2245,https://smashmate.net/user/65286/
2,リム,2147,https://smashmate.net/user/82283/
3,たぁ,2142,https://smashmate.net/user/48115/
ユーザページから対戦結果を取得
count_winlose.py
import sys
import csv
import requests
import time
from bs4 import BeautifulSoup
if len(sys.argv) < 2:
print("argument error")
sys.exit(1)
filename = sys.argv[1]
csv_file = open(filename, "r")
f = csv.reader(csv_file)
for row in f:
user_url = row[3]
res = requests.get(user_url)
soup = BeautifulSoup(res.text, "html.parser")
# 対戦の最大ページ数を取得
pages = soup.find('ul', class_='pagination').find_all('a')
max_page = 0
for p in pages:
if p.get('href') is not None:
page_num = p.get('href').split("=")[1]
page_num = int(page_num)
if max_page < page_num:
max_page = page_num
# 各ページで対戦成績を集計
for page in range(1, max_page):
time.sleep(3)
res = requests.get(user_url + "?page=" + str(page))
results = soup.find_all('div', class_="row row-center va-middle row-battle row-nomargin")
for result in results:
enemy_fighter = ""
atags = result.find_all('a')
for atag in atags:
if 'fighter' in atag.get('href'):
# 単キャラ使いでない場合の勝敗はカウントしない
if enemy_fighter != "" and enemy_fighter != atag.get('href'):
continue
enemy_fighter = atag.get('href')
winlose = result.find('span').text
print(winlose + "," + enemy_fighter)
python3 count_winlose.py minmin_ranker.txt > minmin_result.txt
# 取得結果はこんな感じ
head -n 3 minmin_result.txt
勝ち,https://smashmate.net/fighter/link/
負け,https://smashmate.net/fighter/link/
勝ち,https://smashmate.net/fighter/link/
以下の点を修正
- with構文使った
- 例外処理入れた(通信エラーなど補足するため)
- ページを変えても同じurlを見ていたので修正
- エラー出力に状況を出力
- 最終ページがrangeの範囲外だったので修正
count_winlose.py
import sys
import csv
import requests
import time
from bs4 import BeautifulSoup
if len(sys.argv) < 2:
print("argument error")
sys.exit(1)
filename = sys.argv[1]
with open(filename, 'r') as csv_file:
f = csv.reader(csv_file)
for row in f:
try:
user_url = row[3]
res = requests.get(user_url)
soup = BeautifulSoup(res.text, "html.parser")
# 対戦の最大ページ数を取得
pages = soup.find('ul', class_='pagination').find_all('a')
max_page = 0
for p in pages:
if p.get('href') is not None:
page_num = p.get('href').split("=")[1]
page_num = int(page_num)
if max_page < page_num:
max_page = page_num
print(row[1], str(max_page), "ページ分取得します", file=sys.stderr)
# 各ページで対戦成績を集計
for page in range(max_page):
time.sleep(3)
res = requests.get(user_url + "?page=" + str(page+1))
soup = BeautifulSoup(res.text, "html.parser")
results = soup.find_all('div', class_="row row-center va-middle row-battle row-nomargin")
print(str(page+1) + "ページ目" + str(len(results)) + "件取得中", file=sys.stderr)
for result in results:
enemy_fighter = ""
atags = result.find('div', class_='col-xs-8').find_all('a')
for atag in atags:
if 'fighter' in atag.get('href'):
# 単キャラ使いでない場合の勝敗はカウントしない
if enemy_fighter != "" and enemy_fighter != atag.get('href'):
continue
enemy_fighter = atag.get('href')
winlose = result.find('span').text
print(winlose + "," + enemy_fighter)
except Exception as e:
print(e)
python3 count_winlose.py minmin_ranker.txt > result/minmin.txt
オムアツ 14 ページ分取得します
1ページ目20件取得中
2ページ目20件取得中
3ページ目20件取得中
すべての対戦結果を集計して勝ち数、負け数を算出する
# 対戦中止とキャラがわからない行を除いて集計
cat minmin_result.txt | grep -v "対戦中止" | grep -v ,$ | sort | uniq -c > minmin_winlose.txt
# 集計結果はこんな感じ
head -n 3 minmin_winlose.txt
37 勝ち,https://smashmate.net/fighter/banjo_and_kazooie/
10 負け,https://smashmate.net/fighter/banjo_and_kazooie/
56 勝ち,https://smashmate.net/fighter/bayonetta/
勝率を出して、ソートする
ファイルのクローズ漏れあるので今までのスクリプトもwith
構文使うべき。
count_winrate.py
import csv
import sys
filename = sys.argv[1]
with open("fighter.txt", "r") as fighter_f:
reader_fighter = csv.reader(fighter_f)
for fighter in reader_fighter:
# print(fighter)
with open(filename, "r") as winlose_f:
reader_winlose = csv.reader(winlose_f)
win = 0
lose = 0
for winlose in reader_winlose:
if fighter[1] == winlose[1]:
if "勝ち" in winlose[0]:
win = winlose[0][:-3].strip()
elif "負け" in winlose[0]:
lose = winlose[0][:-3].strip()
if (int(win) + int(lose)) == 0:
winrate = -1
else:
winrate= round(int(win) / (int(win) + int(lose)), 2)
print(fighter[0], win, lose, winrate, sep=",")
下記を実行して結果を見てみるとゲッチが対戦数0なのはおかしい。
スクレイピングで例外出てたかも
python3 calc_winrate.py minmin_winlose.txt
python3 count_winlose.py minmin_ranker.txt > result/minmin.txt
cat result/minmin.txt | grep -v "対戦" | grep -v ,$ | sort | uniq -c > winlose/minmin.txt
python3 calc_winrate.py winlose/minmin.txt > winrate/minmin.txt
勝数負数から勝率を出すスクリプトはこちら
calc_winrate.py
import csv
import sys
filename = sys.argv[1]
with open("fighter.txt", "r") as fighter_f:
reader_fighter = csv.reader(fighter_f)
for fighter in reader_fighter:
with open(filename, "r") as winlose_f:
reader_winlose = csv.reader(winlose_f)
win = 0
lose = 0
for winlose in reader_winlose:
if fighter[1] == winlose[1]:
if "勝ち" in winlose[0]:
win = winlose[0][:-3].strip()
elif "負け" in winlose[0]:
lose = winlose[0][:-3].strip()
if (int(win) + int(lose)) == 0:
winrate = -1
else:
winrate= round(int(win) / (int(win) + int(lose)), 2)
print(fighter[0], win, lose, winrate, sep=",")
結果見てみたけど、そこまではっきりした差がでない。(トップランカーの初期は相性関係なく勝つのでその辺で勝ちを稼いでるのであまり差が出てこないのか)
[0]~/development/ssbu $ cat winrate/king_k_rool.txt | grep -e "スティーブ" -e "カズヤ" -e "ミェンミェン"
ミェンミェン,43,23,0.65
スティーブ/アレックス,61,54,0.53
カズヤ,48,38,0.56
[0]~/development/ssbu $ cat winrate/kazuya.txt | grep -e "スティーブ" -e "カズヤ" -e "ミェンミェン"
ミェンミェン,30,26,0.54
スティーブ/アレックス,46,52,0.47
カズヤ,31,24,0.56
[0]~/development/ssbu $ cat winrate/minmin.txt | grep -e "スティーブ" -e "カズヤ" -e "ミェンミェン"
ミェンミェン,53,30,0.64
スティーブ/アレックス,93,53,0.64
カズヤ,57,30,0.66
[0]~/development/ssbu $ cat winrate/steve.txt | grep -e "スティーブ" -e "カズヤ" -e "ミェンミェン"
ミェンミェン,43,36,0.54
スティーブ/アレックス,60,48,0.56
カズヤ,28,15,0.65