Как насчет создания пустого фрейма данных и добавления ваших очищенных данных по очереди:
columns = ("names", "age", "w", "l", "g", "gs", "ip", "hits", "runs", "bb", "so")
df = pd.DataFrame(columns=columns)
for idx, pitcher_row in enumerate(tree.xpath('//table[contains(@class,"stats_table")]//tr[contains(@class,"full_table")]')):
tmp = []
tmp.append(pitcher_row.xpath('.//td[@data-stat="player"]/a')[0].text)
tmp.append(pitcher_row.xpath('.//td[@data-stat="age"]/text()')[0])
tmp.append(pitcher_row.xpath('.//td[@data-stat="W"]/text()')[0])
...
df.loc[idx] = tmp
Или даже проще, если вы хотите придерживаться большей части своего кода:
columns = ("names", "age", "w", "l", "g", "gs", "ip", "hits", "runs", "bb", "so")
df = pd.DataFrame(columns=columns)
for idx, pitcher_row in enumerate(tree.xpath('//table[contains(@class,"stats_table")]//tr[contains(@class,"full_table")]')):
names = pitcher_row.xpath('.//td[@data-stat="player"]/a')[0].text
age = pitcher_row.xpath('.//td[@data-stat="age"]/text()')[0]
w = pitcher_row.xpath('.//td[@data-stat="W"]/text()')[0]
l = pitcher_row.xpath('.//td[@data-stat="L"]/text()')[0]
g = pitcher_row.xpath('.//td[@data-stat="G"]/text()')[0]
gs = pitcher_row.xpath('.//td[@data-stat="GS"]/text()')[0]
ip = pitcher_row.xpath('.//td[@data-stat="IP"]/text()')[0]
hits = pitcher_row.xpath('.//td[@data-stat="H"]/text()')[0]
runs = pitcher_row.xpath('.//td[@data-stat="R"]/text()')[0]
bb = pitcher_row.xpath('.//td[@data-stat="BB"]/text()')[0]
so = pitcher_row.xpath('.//td[@data-stat="SO"]/text()')[0]
df.loc[idx] = (names, age, w, l, g, gs, ip, hits, runs, bb, so)