축구선수 몸값 분석 웹 크롤링 (5) - 선택조건 체크박스 추가 및 데이터 처리

1. HTML 체크박스 추가

Document

전 세계 축구선수 몸값 보기



Select Value Type :

  • 체크박스를 추가하여 기존의 유로 단위를 달러와 원화까지 선택지를 늘렸음.
  • 아무것도 선택하지 않을 시 Value 값은 출력되지 않음
  • Radio 타입의 경우 한 가지 밖에 선택되지 않기 때문에 체크박스 타입으로 함.

2. Checkbox 데이터 Post 메서드로 가져오기

from flask import Flask, render_template, request
import pandas as pd
import transfermarkt

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route("/list", methods=['POST'])
def post():
    num = request.form["listnum"]
    # checkbox 데이터 POST로 가져오기
    typeList = request.form.getlist('type')
    df = transfermarkt.show_valueList(num, typeList)
    return df.to_html(index=False)


if __name__ == "__main__":
    app.run(debug=True)
  • checkbox의 데이터가 여러 개이므로 List 타입으로 가져옴. 그리고 이를 크롤링 모듈에 넘겨줌.

3. Checkbox 입력 값에 따라 Dataframe 열 추가

from bs4 import BeautifulSoup
import requests
import pandas as pd
import time
from math import ceil 

def show_valueList(list_num, typeList):
    list_num = int(list_num)
    url = "https://www.transfermarkt.com/"

    headers = {'User-Agent' : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"}
    player_list=[]
    
    for i in range(1, ceil(list_num/25)+1):
        url = f"https://www.transfermarkt.com/spieler-statistik/wertvollstespieler/marktwertetop?ajax=yw1&page={i}"

        r = requests.get(url, headers=headers)

        soup = BeautifulSoup(r.text, 'html.parser')
        player_info = soup.find_all('tr', class_ = ['odd','even'])

        for info in player_info:
            player = info.find_all("td")
            number = player[0].text 
            name = player[3].text 
            position = player[4].text 
            age = player[5].text 
            nation = player[6].img['alt'] 
            team = player[7].img['alt'] 
            value = player[8].text.strip()
            player_list.append([number, name, position, age, nation, team, value])

        time.sleep(1)
    
    df = pd.DataFrame(player_list, 
        columns=['#', 'Player', 'Position', 'Age', 'Nat.', 'Club', 'Value'])
    # value 값 전처리
    df['Value'] = df['Value'].str.replace('€','')
    df['Value'] = df['Value'].str.replace('m','').astype('float')

    # 입력 조건에 따라 값 표시
    if not typeList:
    # checkbox 선택 하나도 없을 땐 값 표시 X
        df.drop(columns=['Value'], inplace=True)
    # index의 checkbox 데이터가 배열로 넘어옴
    else:
        for data in typeList:
            if data == "USD":
                df['Value($)'] = df['Value']*1.01
                df['Value($)'] = df['Value($)'].astype(str)+'M'
            elif data == "EUR":
                df['Value(€)'] = df['Value']
                df['Value(€)'] = df['Value(€)'].astype(str)+'M'
            elif data == "KRW":
                df['Value(₩)'] = df['Value']*13
                df['Value(₩)'] = df['Value(₩)'].astype(str)+'억'
        df.drop(columns=['Value'], inplace=True)

    return df[0:list_num] # 입력한 명 수만큼 인덱싱



if __name__ == "__main__":
    show_valueList(10)
  • Checkbox 배열 값을 인자로 받아온 뒤, for문으로 순회하며 조건에 따라 value 열 추가
  • 기존의 크롤링한 value 열이 있기 때문에 이를 사용하기 위해 전처리 과정을 거침
  • 출력하기 전에 가독성을 위해 화폐 기호 및 단위 표시를 하였고 이를 위해 value 값을 다시 str로 변환함

유로화, 달러화, 원화 추가

4. 문제 해결

  • checkbox 데이터가 여러 개이기 때문에 이를 딕셔너리로 관리할 지 리스트로 관리할 지 고민이 되었으나,
    데이터가 많지 않고 사용하기 익숙한 리스트를 사용함. 이를 위해 getlist라는 함수를 찾아 사용함.
  • 입력 조건에 따라 데이터를 출력해야 하기 때문에 코드가 길어지는 것을 방지하기 위해 함수를 새로 작성하려고 하였음. 하지만 판다스에서 제어문을 사용하면 쉽게 열을 추가할 수 있음을 알았고, 조건에 맞게끔 출력하는 코드를 작성하여 문제를 해결함. -> 반복문과 조건문이 왜 많이 쓰이고 중요한 지 이번 과정을 통해 몸소 알게 되었음. 이 둘을 잘만 활용 한다면 여러 함수를 사용하는 것보다 더 쉽고 간결할 수 있다는 걸 알게됨.

5. 어려웠던 점

  • 기능이 추가될수록, 코드가 길어질수록 어떻게 짜야할 지 고민이 많이 됨. 새로운 함수를 만들어야 할지 새로운 모듈을 작성해야 할지 뭐가 정답인지 모르는 상황에서 먼저 작성하고 보자는 생각으로 접근함. 하지만 계속 수정을 거듭할 수록 썼다 지웠다하는 시간이 많아지면서 시간이 많이 낭비됨. 따라서 앞으로는 기능을 추가하기 전에 먼저 자료를 충분히 찾아본 뒤 먼저 기획하고 설계하는 과정을 거칠 필요를 느낌.