<jupyter_start><jupyter_text>Peak Player Performance This notebook is based on the article [The Best Stats Measure](https://www.thecricketmonthly.com/story/1057899/the-best-stats-measure), written by <NAME> who compares alternative ways of measuring the ability of cricketers. He defines here the Peak-33 metric:> Peak-33 is based on the 33 matches in which batsmen scored most runs and bowlers took most wickets, rather than the 33 in which they returned the best average. The search for great metrics for measuring cricket performance is continually evolving and the question about different eras is one that rages on. He does speak further about normalising the numbers for an era and for given matches, but that isn't something that I will explore here, at least initially.  This acts as an implementation of taking averages and summaries for given time periods, matches and innings for a given cricketer to enable comparison of cricketers at their peaks using `BeautifulSoup` to scrape the stats online.<jupyter_code>import numpy as np 
import pandas as pd 
import requests
from bs4 import BeautifulSoup
import re<jupyter_output><empty_output><jupyter_text>I will begin with using <NAME> as a working example.<jupyter_code>player_id = 311158 # <NAME> ESPN CricInfo ID 

base_player_url = f'https://stats.espncricinfo.com/ci/engine/player/{player_id}.html'
batting_innings_link = base_player_url + '?class=1;template=results;type=batting;view=innings'
bowling_innings_link = base_player_url + '?class=1;template=results;type=bowling;view=innings'
def get_innings_by_innings_table(url):
    soup = BeautifulSoup(requests.get(url).text, features="html.parser")
    for caption in soup.find_all('caption'):
        if caption.get_text() == 'Innings by innings list':
            main_table = caption.find_parent(
                'table', {'class': 'engineTable'})
        
    columns = [header.get_text() for header in main_table.find('thead').find_all('tr')[0].find_all('th')]
    rows = []

    for innings in [
            row for row in main_table.find('tbody').find_all('tr')]:
        rows.append([stat.get_text() for stat in innings.find_all('td')])

    final_table = pd.DataFrame(rows, columns=columns).apply(pd.to_numeric, errors='ignore')
        
    # Remove blank columns
    return(final_table.loc[:,[i for i in final_table.columns if i != '']])<jupyter_output><empty_output><jupyter_text>We can get the list of test match batting and bowling innings.<jupyter_code>batting = get_innings_by_innings_table(batting_innings_link)
bowling = get_innings_by_innings_table(bowling_innings_link)
batting.head()
bowling.head()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0912504693954187, 'fraction_numerical': 0.00938790837401427, 'mean_word_length': 5.082191780821918, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 17, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '601139', 'license': 'MIT', 'n_tokens_mistral': 812, 'n_tokens_neox': 767, 'n_words': 256, 'path': 'peak_player_performance.ipynb', 'repo_name': 'willcanniford/python-cricketer-analysis', 'size': '2678'}