Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions pybaseball/team_batting.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ def team_batting_bref(team: str, start_season: int, end_season: Optional[int]=No
response = session.get(stats_url)
soup = BeautifulSoup(response.content, 'html.parser')

table = soup.find_all('table', {'class': 'sortable stats_table'})[0]
table = soup.find('table', {'id': 'players_standard_batting'})
if table is None:
raise ValueError(
"Could not find batting data for {} {}. The page structure may have changed.".format(team, season)
)

if headings is None:
headings = [row.text.strip() for row in table.find_all('th')[1:28]]
headings = [th.text.strip() for th in table.find('thead').find_all('th')[1:]]

rows = table.find_all('tr')
for row in rows:
Expand Down
8 changes: 6 additions & 2 deletions pybaseball/team_pitching.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,14 @@ def team_pitching_bref(team: str, start_season: int, end_season: Optional[int]=N
response = session.get(stats_url)
soup = BeautifulSoup(response.content, 'html.parser')

table = soup.find_all('table', {'id': 'team_pitching'})[0]
table = soup.find('table', {'id': 'players_standard_pitching'})
if table is None:
raise ValueError(
"Could not find pitching data for {} {}. The page structure may have changed.".format(team, season)
)

if headings is None:
headings = [row.text.strip() for row in table.find_all('th')[1:34]]
headings = [th.text.strip() for th in table.find('thead').find_all('th')[1:]]

rows = table.find_all('tr')
for row in rows:
Expand Down
11 changes: 11 additions & 0 deletions tests/pybaseball/data/team_batting_bref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html><body>
<table id="players_standard_batting">
<thead>
<tr><th>Rk</th><th>Tm</th><th>BatAge</th><th>G</th><th>PA</th><th>R</th><th>H</th><th>HR</th><th>RBI</th></tr>
</thead>
<tbody>
<tr><th scope="row">1</th><td>New York Yankees</td><td>28.5</td><td>162</td><td>6300</td><td>900</td><td>1400</td><td>250</td><td>860</td></tr>
<tr><th scope="row">2</th><td>Boston Red Sox</td><td>27.9</td><td>162</td><td>6250</td><td>850</td><td>1450</td><td>220</td><td>820</td></tr>
</tbody>
</table>
</body></html>
11 changes: 11 additions & 0 deletions tests/pybaseball/data/team_pitching_bref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html><body>
<table id="players_standard_pitching">
<thead>
<tr><th>Rk</th><th>Tm</th><th>PAge</th><th>G</th><th>IP</th><th>H</th><th>ER</th><th>ERA</th><th>SO</th><th>BB</th></tr>
</thead>
<tbody>
<tr><th scope="row">1</th><td>New York Yankees</td><td>29.1</td><td>162</td><td>1450.0</td><td>1300</td><td>600</td><td>3.72</td><td>1500</td><td>500</td></tr>
<tr><th scope="row">2</th><td>Boston Red Sox</td><td>28.4</td><td>162</td><td>1440.0</td><td>1350</td><td>650</td><td>4.06</td><td>1400</td><td>520</td></tr>
</tbody>
</table>
</body></html>
33 changes: 33 additions & 0 deletions tests/pybaseball/test_team_batting.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,36 @@ def test_team_batting(response_get_monkeypatch: Callable, sample_html: str, samp
team_batting_result = team_batting(season).reset_index(drop=True)

pd.testing.assert_frame_equal(team_batting_result, sample_processed_result, check_dtype=False)


@pytest.fixture(name="sample_bref_html")
def _sample_bref_html(get_data_file_contents: Callable[[str], str]) -> str:
return get_data_file_contents('team_batting_bref.html')


def test_team_batting_bref(bref_get_monkeypatch: Callable, sample_bref_html: str) -> None:
# Regression test for #461: Baseball Reference changed the batting table to
# id='players_standard_batting' with a <thead>. team_batting_bref must parse
# the new structure instead of the removed 'sortable stats_table' class.
from pybaseball.team_batting import team_batting_bref

bref_get_monkeypatch(sample_bref_html)

result = team_batting_bref('NYY', 2019)

assert result is not None
assert not result.empty
assert 'Tm' in result.columns
assert 'Year' in result.columns
assert (result['Year'] == 2019).all()


def test_team_batting_bref_missing_table_raises(bref_get_monkeypatch: Callable) -> None:
# Regression test for #461: a page without the expected table should raise a
# clear ValueError instead of an opaque IndexError.
from pybaseball.team_batting import team_batting_bref

bref_get_monkeypatch("<html><body><p>no table here</p></body></html>")

with pytest.raises(ValueError):
team_batting_bref('NYY', 2019)
33 changes: 33 additions & 0 deletions tests/pybaseball/test_team_pitching.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,36 @@ def test_team_pitching_relievers(response_get_monkeypatch: Callable, sample_html
team_pitching_relievers_result = team_pitching_relievers(season).reset_index(drop=True)

pd.testing.assert_frame_equal(team_pitching_relievers_result, sample_processed_result, check_dtype=False)


@pytest.fixture(name="sample_bref_html")
def _sample_bref_html(get_data_file_contents: Callable[[str], str]) -> str:
return get_data_file_contents('team_pitching_bref.html')


def test_team_pitching_bref(bref_get_monkeypatch: Callable, sample_bref_html: str) -> None:
# Regression test for #461: Baseball Reference changed the pitching table to
# id='players_standard_pitching' with a <thead>. team_pitching_bref must parse
# the new structure instead of the removed 'team_pitching' id.
from pybaseball.team_pitching import team_pitching_bref

bref_get_monkeypatch(sample_bref_html)

result = team_pitching_bref('NYY', 2019)

assert result is not None
assert not result.empty
assert 'Tm' in result.columns
assert 'Year' in result.columns
assert (result['Year'] == 2019).all()


def test_team_pitching_bref_missing_table_raises(bref_get_monkeypatch: Callable) -> None:
# Regression test for #461: a page without the expected table should raise a
# clear ValueError instead of an opaque IndexError.
from pybaseball.team_pitching import team_pitching_bref

bref_get_monkeypatch("<html><body><p>no table here</p></body></html>")

with pytest.raises(ValueError):
team_pitching_bref('NYY', 2019)