acetwo3ee commited on
Commit
246c2b4
·
verified ·
1 Parent(s): 6f66d97

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +82 -65
src/streamlit_app.py CHANGED
@@ -19,7 +19,7 @@ def fetch_wnba_data():
19
  "LastNGames": "0",
20
  "LeagueID": "10",
21
  "Location": "",
22
- "MeasureType": "Base",
23
  "Month": "0",
24
  "OpponentTeamID": "0",
25
  "Outcome": "",
@@ -37,23 +37,36 @@ def fetch_wnba_data():
37
  "VsDivision": ""
38
  }
39
  headers = {
40
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
41
  "Accept": "application/json, text/plain, */*",
42
- "x-nba-stats-origin": "stats",
43
- "x-nba-stats-token": "true",
44
- "Referer": "https://stats.wnba.com/players/boxscores-traditional/",
45
  "Accept-Encoding": "gzip, deflate, br",
46
  "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
47
- "sec-ch-ua": '"Not/A)Brand";v="99", "Google Chrome";v="126", "Chromium";v="126"',
 
 
 
48
  "sec-ch-ua-mobile": "?0",
49
  "sec-ch-ua-platform": '"Windows"',
50
- "sec-fetch-dest": "empty",
51
- "sec-fetch-mode": "cors",
52
- "sec-fetch-site": "same-origin"
53
- # Add cf_clearance cookie here if available, e.g.:
54
- # "Cookie": "cf_clearance=your_cookie_value"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
-
57
  try:
58
  st.write("Attempting API request with cloudscraper...")
59
  scraper = cloudscraper.create_scraper()
@@ -61,9 +74,8 @@ def fetch_wnba_data():
61
  response = scraper.get(url, params=params, headers=headers, timeout=60)
62
  response.raise_for_status()
63
  fetch_time = time.time() - start_time
64
- st.success(f"API request successful. Fetched {len(response.json().get('resultSets', [])[0].get('rowSet', []))} records in {fetch_time:.2f} seconds.")
65
- data = response.json()
66
- game_logs = data.get("resultSets", [])[0].get("rowSet", [])
67
  if not game_logs:
68
  st.warning("No game logs found in the response.")
69
  return []
@@ -77,18 +89,17 @@ def fetch_wnba_data():
77
  response = session.get(url, params=params, headers=headers, timeout=60)
78
  response.raise_for_status()
79
  fetch_time = time.time() - start_time
80
- st.success(f"API request successful (requests). Fetched {len(response.json().get('resultSets', [])[0].get('rowSet', []))} records in {fetch_time:.2f} seconds.")
81
- data = response.json()
82
- game_logs = data.get("resultSets", [])[0].get("rowSet", [])
83
  if not game_logs:
84
  st.warning("No game logs found in the response.")
85
  return []
86
  return game_logs
87
  except requests.Timeout:
88
- st.error("Request timed out after 60 seconds. Try uploading a local JSON file, checking your network, or adding a cf_clearance cookie.")
89
  return []
90
  except requests.HTTPError as e:
91
- st.error(f"HTTP error: {e}. The API may require a cf_clearance cookie. See instructions below.")
92
  return []
93
  except requests.RequestException as e:
94
  st.error(f"Request failed: {e}. Possible API or network issue.")
@@ -107,7 +118,11 @@ def create_dataframe(game_logs):
107
  "TO_Rank", "PF_Rank", "PTS_Rank", "PLUS_MINUS_Rank", "Fantasy_Score_Rank",
108
  "Double_Double_Rank", "Triple_Double_Rank", "Minutes_Formatted"
109
  ]
110
- return pd.DataFrame(game_logs, columns=columns)
 
 
 
 
111
 
112
  # Function to convert DataFrame to CSV for download
113
  def convert_to_csv(df):
@@ -116,15 +131,16 @@ def convert_to_csv(df):
116
  return csv_buffer.getvalue()
117
 
118
  # Streamlit app
119
- st.title("WNBA 2025 Player Game Logs")
120
 
121
- # Instructions for cf_clearance cookie
122
  st.markdown("""
123
  **Troubleshooting API Access**:
124
- If the API request fails, you may need a `cf_clearance` cookie:
125
- 1. Open `https://stats.wnba.com/players/boxscores-traditional/` in Chrome.
126
- 2. Press F12, go to Network tab, refresh, and find the `playergamelogs` request.
127
- 3. Copy the `Cookie` header (e.g., `cf_clearance=...`) and add it to the `headers` in the code.
 
128
  """)
129
 
130
  # Option to upload local JSON file
@@ -154,44 +170,45 @@ if game_logs:
154
  with st.spinner("Converting data to DataFrame..."):
155
  df = create_dataframe(game_logs)
156
 
157
- # Display total number of records
158
- st.write(f"Total records fetched: {len(df)}")
159
-
160
- # Display filters
161
- st.subheader("Filter Data")
162
- player_filter = st.selectbox("Select Player", options=["All"] + sorted(df["Player_Name"].unique()))
163
- team_filter = st.selectbox("Select Team", options=["All"] + sorted(df["Team_Abbreviation"].unique()))
164
-
165
- # Apply filters
166
- filtered_df = df
167
- if player_filter != "All":
168
- filtered_df = filtered_df[filtered_df["Player_Name"] == player_filter]
169
- if team_filter != "All":
170
- filtered_df = filtered_df[filtered_df["Team_Abbreviation"] == team_filter]
171
-
172
- # Display first 1000 records
173
- st.subheader("Game Log Data (First 1000 Records)")
174
- display_df = filtered_df.iloc[:1000]
175
- st.write(f"Displaying {len(display_df)} records (out of {len(filtered_df)} after filtering)")
176
- st.dataframe(display_df, use_container_width=True)
177
-
178
- # Download button for all records
179
- st.subheader("Download Data")
180
- csv_data = convert_to_csv(filtered_df)
181
- st.download_button(
182
- label=f"Download All {len(filtered_df)} Records as CSV",
183
- data=csv_data,
184
- file_name="wnba_player_gamelogs_2025_filtered.csv",
185
- mime="text/csv"
186
- )
187
-
188
- # Visualization
189
- if player_filter != "All":
190
- st.subheader(f"Points Scored by {player_filter}")
191
- player_data = filtered_df[["Game_Date", "PTS"]].copy()
192
- player_data["Game_Date"] = pd.to_datetime(player_data["Game_Date"])
193
- player_data = player_data.sort_values("Game_Date")
194
- st.line_chart(player_data.set_index("Game_Date")["PTS"])
 
195
  else:
196
  st.warning("No data available to display. Check error messages above or upload a JSON file.")
197
 
 
19
  "LastNGames": "0",
20
  "LeagueID": "10",
21
  "Location": "",
22
+ "MeasureType": "Scoring",
23
  "Month": "0",
24
  "OpponentTeamID": "0",
25
  "Outcome": "",
 
37
  "VsDivision": ""
38
  }
39
  headers = {
40
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
41
  "Accept": "application/json, text/plain, */*",
 
 
 
42
  "Accept-Encoding": "gzip, deflate, br",
43
  "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
44
+ "Connection": "keep-alive",
45
+ "Host": "stats.wnba.com",
46
+ "Referer": "https://stats.wnba.com/players/boxscores-scoring/",
47
+ "sec-ch-ua": '"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"',
48
  "sec-ch-ua-mobile": "?0",
49
  "sec-ch-ua-platform": '"Windows"',
50
+ "Sec-Fetch-Dest": "empty",
51
+ "Sec-Fetch-Mode": "cors",
52
+ "Sec-Fetch-Site": "same-origin",
53
+ "x-nba-stats-origin": "stats",
54
+ "x-nba-stats-token": "true",
55
+ "X-NewRelic-ID": "VQECWF5UChABUVFUBwYEUVwD",
56
+ "newrelic": "eyJ2IjpbMCwxXSwiZCI6eyJ0eSI6IkJyb3dzZXIiLCJhYyI6IjE3NTk4NzIiLCJhcCI6IjE1ODkwMTY1OTQiLCJpZCI6ImQwMTY2YTBlYjJhNGU3NjEiLCJ0ciI6Ijc5OTcwZjg2YmRmMzFiODMwZDIyYjQ2OTNlZDViZjg2IiwidGkiOjE3NTEwMjUyNTAzMjQsInRrIjoiMjg5MDE5OSJ9fQ==",
57
+ "traceparent": "00-79970f86bdf31b830d22b4693ed5bf86-d0166a0eb2a4e761-01",
58
+ "tracestate": "2890199@nr=0-1-1759872-1589016594-d0166a0eb2a4e761----1751025250324",
59
+ "Cookie": (
60
+ "bm_sv=81F390751AEC1D1FB260953D398C5A4D~YAAQDwHARf6uj6+XAQAAAfA5sRzqRJgRh3B32ZqWy6it7Z6fxqJfhoJnTBNhXxLrad7PcTRelS3tWrr8UZ0igPSGq5RPSbx74Th8HRr7JesXhayd4S6iMSj9lY6iDW8DwtZMJ1VV6Z53UgrKLPJNYRNqrXvNW6HpUvolDeVs2DrB1QvWjgUsftRuNmgEb34LqKZKJS8Wb16Nl3Cnz/U5S451tH3wjHJmfEiW54wOIRtb/4mcmteH1Jeg6Lyrzg==~1;"
61
+ "bm_sz=2D0D6F1234C7ABD81BFA31A8E8436692~YAAQDwHARXoaj6+XAQAAK4TysBywZPNxEID0PIkx3xRXCUd0d/29PGKxBcY8BbfGpXK2mQmY4JFFo6jvaCg7oDKoFnuYpEvzSdS4P+HIoJ2YJT44TIRGGONf2u6YhkIdmN+7HML3CAVZbiqkhJrebrrcY0tDKb7oAmjcYbhZFaToEifUsi6Kzqg2/qjX5ZskYiX6HevYgvqKJG2enicqjp1g9BoYzLRKBualYuB8iafHN03nowT28yk86TjZpDVHdB6KZiRIkE/hOQHwY4+4lspWFjXyWZABbigixU4WduxUOieGFkuelkjSSTJCjMxugYyxZ5f5LNvKvDzYa7b8REHx4T4EMCB1CUF72yIJetJDWGptNPnrK7d2GAMsX7hBC4ot9t+rU+c9f7VqDnI=~4339254~3354693;"
62
+ "_abck=8EDF6F70F2EBD7C030552A7F17AE0613~-1~YAAQDwHARYcaj6+XAQAAb43ysA6SWJgciVxlMdQRfU1xnJKoSsGljvAAyewPCum5U5OhlUgVK6JpjvknmoJ25v4W2UuzDyJ9zotyuSnqoel6MclUARV6tAH5UeZVMT29GJ7hiG8lUV9cGmJLbJ1FeFou2EUSMOuLDSx0TF43TL+8jRVLPtG7kLxgeFLJjZdoeUqVfVP5g0RdClaK5VIzHhE0AWBlK2+1ciAiLJHI85LBhkUpikkxGAmO/v7M6I7+PC75bQh6v2b0kIw/BjtNVuY2JwqhagyfJdhFJjBBw3yhZH8AAiRZqEm25erLGhcoELtBLkOmah3AxxWuXKljfWL47mUiOTi0/XdXG+VTK4U2ep6i+c9n1f9/ykoumWR/2+wmpooBZsWAQx6MwMz8QR4zMDQ4M9r92hb6QhLnZvXWQ+nAbzON7WJ/SJBQ1kqXyNzw9y8pETwvI4VmTFamrAV8UAFx4Vnx+CpobQT+6P2wRo07QzqV2Ai6GCmy1aKT3NU4ktXJO2A+ftzmbyNy+fmSkEjXCC3sJKOrAaBrX93eWJ9Siy3+8tOJTobLJIkzmh5FCRkSz4twbpZKH08=~-1~||0||~1751023975;"
63
+ "ak_bmsc=B7BABDC398E92F1DF6650FC251EACFCF~000000000000000000000000000000~YAAQDwHARY0aj6+XAQAAvo/ysBwjFhKD1uQJMSb1wp40PfdvSnbuU85scjOO5zGLWbgaZDTozOc3oJpqfgUyNPOfunAmS964TA6cI3z9rwEzk/kP+ypn4b1wdhToyxyC0swmVj13Gj+09GTAhyXfnG/GW8lJBLMfmxOhQY4EzGGZwX6cY8eBxe8ZWgUUa3zZKo92fZEM5iLkdjyr5X5EoNsqIFHbEtNlWyJBvhyTxmv4n4q8Xr4+YtaDf/dW5xGITmfcSWzzcR1zQTUY6GgDW0NWsW5Zgz4RFqLu9d5fJkROwuaQwH2EHo9nZu5FXT5B3V+rmnGwPsCPRkYuxUc88u1clSTrZ3qpWXkBuBHdCLE/jGH91XelBfHZu00aDh18czfDmdSCiCVSIhKIIbKBMLO+wT4vLcjZjznyLN3wzY0XFGcZ8Bua0vzan0YhFNVR6+3kOO5HZLXjfSDwmw==;"
64
+ "usprivacy=1---;"
65
+ "_ga=GA1.2.141871504.1751020378;"
66
+ "_gid=GA1.2.808457365.1751020378;"
67
+ "_ga_GELQE9ZCTK=GS2.2.s1751024075$o2$g1$t1751025052$j60$l0$h0"
68
+ )
69
  }
 
70
  try:
71
  st.write("Attempting API request with cloudscraper...")
72
  scraper = cloudscraper.create_scraper()
 
74
  response = scraper.get(url, params=params, headers=headers, timeout=60)
75
  response.raise_for_status()
76
  fetch_time = time.time() - start_time
77
+ game_logs = response.json().get("resultSets", [])[0].get("rowSet", [])
78
+ st.success(f"API request successful. Fetched {len(game_logs)} records in {fetch_time:.2f} seconds.")
 
79
  if not game_logs:
80
  st.warning("No game logs found in the response.")
81
  return []
 
89
  response = session.get(url, params=params, headers=headers, timeout=60)
90
  response.raise_for_status()
91
  fetch_time = time.time() - start_time
92
+ game_logs = response.json().get("resultSets", [])[0].get("rowSet", [])
93
+ st.success(f"API request successful (requests). Fetched {len(game_logs)} records in {fetch_time:.2f} seconds.")
 
94
  if not game_logs:
95
  st.warning("No game logs found in the response.")
96
  return []
97
  return game_logs
98
  except requests.Timeout:
99
+ st.error("Request timed out after 60 seconds. Try uploading a local JSON file or checking your network.")
100
  return []
101
  except requests.HTTPError as e:
102
+ st.error(f"HTTP error: {e}. Verify cookies and headers.")
103
  return []
104
  except requests.RequestException as e:
105
  st.error(f"Request failed: {e}. Possible API or network issue.")
 
118
  "TO_Rank", "PF_Rank", "PTS_Rank", "PLUS_MINUS_Rank", "Fantasy_Score_Rank",
119
  "Double_Double_Rank", "Triple_Double_Rank", "Minutes_Formatted"
120
  ]
121
+ try:
122
+ return pd.DataFrame(game_logs, columns=columns)
123
+ except ValueError:
124
+ st.error("Data structure mismatch. The API response for MeasureType=Scoring may have different columns. Please verify the response format.")
125
+ return pd.DataFrame()
126
 
127
  # Function to convert DataFrame to CSV for download
128
  def convert_to_csv(df):
 
131
  return csv_buffer.getvalue()
132
 
133
  # Streamlit app
134
+ st.title("WNBA 2025 Player Game Logs (Scoring)")
135
 
136
+ # Instructions for troubleshooting
137
  st.markdown("""
138
  **Troubleshooting API Access**:
139
+ If the API request fails:
140
+ 1. Verify the `Cookie` header in the code matches a recent browser request.
141
+ 2. Open `https://stats.wnba.com/players/boxscores-scoring/` in Chrome, press F12, go to Network tab, refresh, and find the `playergamelogs` request.
142
+ 3. Copy the `Cookie` header and update the `headers` in the code.
143
+ 4. Upload a local JSON file as a fallback.
144
  """)
145
 
146
  # Option to upload local JSON file
 
170
  with st.spinner("Converting data to DataFrame..."):
171
  df = create_dataframe(game_logs)
172
 
173
+ if not df.empty:
174
+ # Display total number of records
175
+ st.write(f"Total records fetched: {len(df)}")
176
+
177
+ # Display filters
178
+ st.subheader("Filter Data")
179
+ player_filter = st.selectbox("Select Player", options=["All"] + sorted(df["Player_Name"].unique()))
180
+ team_filter = st.selectbox("Select Team", options=["All"] + sorted(df["Team_Abbreviation"].unique()))
181
+
182
+ # Apply filters
183
+ filtered_df = df
184
+ if player_filter != "All":
185
+ filtered_df = filtered_df[filtered_df["Player_Name"] == player_filter]
186
+ if team_filter != "All":
187
+ filtered_df = filtered_df[filtered_df["Team_Abbreviation"] == team_filter]
188
+
189
+ # Display first 1000 records
190
+ st.subheader("Game Log Data (First 1000 Records)")
191
+ display_df = filtered_df.iloc[:1000]
192
+ st.write(f"Displaying {len(display_df)} records (out of {len(filtered_df)} after filtering)")
193
+ st.dataframe(display_df, use_container_width=True)
194
+
195
+ # Download button for all records
196
+ st.subheader("Download Data")
197
+ csv_data = convert_to_csv(filtered_df)
198
+ st.download_button(
199
+ label=f"Download All {len(filtered_df)} Records as CSV",
200
+ data=csv_data,
201
+ file_name="wnba_player_gamelogs_2025_filtered.csv",
202
+ mime="text/csv"
203
+ )
204
+
205
+ # Visualization
206
+ if player_filter != "All":
207
+ st.subheader(f"Points Scored by {player_filter}")
208
+ player_data = filtered_df[["Game_Date", "PTS"]].copy()
209
+ player_data["Game_Date"] = pd.to_datetime(player_data["Game_Date"])
210
+ player_data = player_data.sort_values("Game_Date")
211
+ st.line_chart(player_data.set_index("Game_Date")["PTS"])
212
  else:
213
  st.warning("No data available to display. Check error messages above or upload a JSON file.")
214