【问题标题】:Python web scraping on tables with customizable dropdowns使用可自定义的下拉列表对表格进行 Python 网络抓取
【发布时间】:2021-03-11 04:57:10
【问题描述】:

我是一个新手,正在尝试组合我的第一个网页抓取功能,并且遇到了一些我不完全理解的 HTTP 问题。作为参考,我正在尝试从FanGraphs ZIPS projection page 抓取。

他们的页面默认显示包含 30 名玩家的牌桌,但您可以编辑网页,使牌桌每页显示 10、20、30、50、100、500 或 1000 个结果。理想情况下,我会选择每页 500 个结果的选项,但是当我更改下拉菜单时,URL 保持不变,所以我尝试编写一个脚本来获取 HTML 代码并在尝试请求之前编辑 HTML 页面的属性网页的那个变体。 (如果查看页面源代码,则在第 1247 行)

或者,您可以使用页面上的箭头按钮超链接循环浏览其他页面,但它们也不会更改 URL。

我为位置玩家解决了这个问题,因为我可以单独抓取每个位置,然后连接单独的 Pandas 数据帧,但我对 HTTP 请求的工作原理完全不了解,希望得到一些帮助/指导。

我尝试了类似的方法:

from requests import Request, Session
url = 'https://www.fangraphs.com/projections.aspx?pos=all&stats=pit&type=zips&team=0&lg=al&players=0'
page = requests.get(url)
soup=BeautifulSoup(page.text, 'html.parser')
resultSet = soup.findAll(value=30)
for result in resultSet:
    result["value"]="500"
s = Session()
req = requests.Request('POST', url, data = {'ProjectionBoard1_dg1_ctl00_ctl03_ctl01_PageSizeComboBox_Input value': 500})
resp = s.send(prepped)
print(resp.status_code)

我的想法是抓取结构,编辑配置以匹配我想要的网页,然后反刍固定结构以抓取数据。然后我意识到我不知道自己在做什么。

有几点: 我在这里“甚至没有错”吗?这是否可能与请求或我需要像 Selenium 这样的东西?我是否不正确地使用 POST?

旁注:如果上下文有帮助,对于位置球员我这样做了(对投手也会做同样的事情):

base_url = 'https://www.fangraphs.com/projections.aspx?pos=&stats=bat&type=zips&team=0&lg=all&players=0'
positions = ['c', '1b', '2b', '3b', 'ss', 'lf', 'cf', 'rf']

def generate_positional_urls(urlRoot=base_url, pos=positions):
    index = base_url.index('pos=') + 4 position after
    urlList = []
    numPositions = len(positions)
    for i in range(numPositions):
        position = pos[i]
        tempURL = urlRoot[:index] + position + urlRoot[index:]
        urlList.append(tempURL)

    return urlList

其中的内容:

def generate_df(url):
page = urlopen(url)
soup = BeautifulSoup(page, 'html.parser')

masterTable = soup.findAll('table', {"class": "rgMasterTable"}, {"id": "ProjectionBoard1_dg1_ct100"})
table_rows = masterTable[0].find_all('tr')

data = []

for tr in table_rows:
    td = tr.find_all('td')
    row = [tr.text for tr in td]
    data.append(row)
    
headers = masterTable[0].find_all("thead")
colElements = headers[0].find_all("tr")

soupColResults = colElements[1].findChildren("a")
colStrings = [element.text for element in soupColResults]

df = pd.DataFrame(data)
df.columns = colStrings

df.drop(columns=[''], inplace=True)
df = df.iloc[3:, :]

return df

那么循环和连接数据帧就很容易了,但是对于这个HTTP问题,我真的不知道我在做什么。

注意:我这样做是为了尝试学习和练习 Python。我可以通过使用我的网络浏览器单击超链接将数据导出到 csv 来轻松避免这种情况,因此不必费力找出不必要的乏味。

【问题讨论】:

    标签: python html


    【解决方案1】:

    首先,您尝试将html 表刮入pandas 数据帧。这可以在没有BeautifulSoup 的情况下使用pd.read_html 完成。 其次,始终跟踪 Chrome 或 Firefox 的 network tab 中发生的事情。当您单击以更改结果数量时,您可以看到加载的内容。您可以复制请求as curl,然后将其转换为python request,例如使用this tool。您现在可以加载数据并将它们直接传递给pandas。请注意,为了便于阅读,我必须删除__VIEWSTATE__EVENTVALIDATION,但您可以从页面源代码或curl 命令中获取它们,如上所述。

    import requests
    
    params = (
        ('pos', 'all'),
        ('stats', 'pit'),
        ('type', 'zips'),
        ('team', '0'),
        ('lg', 'al'),
        ('players', '0'),
    )
    
    data = {
      'RadScriptManager1_TSM': '',
      '__EVENTTARGET': 'ProjectionBoard1$dg1',
      '__EVENTARGUMENT': 'FireCommand:ProjectionBoard1$dg1$ctl00;PageSize;1000',
      '__VIEWSTATE': '', #text removed for readability
      '__VIEWSTATEGENERATOR': 'C239D6F0',
      '__SCROLLPOSITIONX': '0',
      '__SCROLLPOSITIONY': '0',
      '__EVENTVALIDATION': '', #text removed for readability
      'ProjectionBoard1_tsStats_ClientState': '{"selectedIndexes":["1"],"logEntries":[],"scrollState":{}}',
      'ProjectionBoard1_tsPosition_ClientState': '{"selectedIndexes":["0"],"logEntries":[],"scrollState":{}}',
      'ProjectionBoard1$rcbTeam': 'All Teams',
      'ProjectionBoard1_rcbTeam_ClientState': '',
      'ProjectionBoard1$rcbLeague': 'AL',
      'ProjectionBoard1_rcbLeague_ClientState': '',
      'ProjectionBoard1_tsProj_ClientState': '{"selectedIndexes":["0"],"logEntries":[],"scrollState":{}}',
      'ProjectionBoard1_tsUpdate_ClientState': '{"selectedIndexes":[],"logEntries":[],"scrollState":{}}',
      'ProjectionBoard1$dg1$ctl00$ctl02$ctl00$PageSizeComboBox': '1000',
      'ProjectionBoard1_dg1_ctl00_ctl02_ctl00_PageSizeComboBox_ClientState': '{"logEntries":[],"value":"1000","text":"1000","enabled":true,"checkedIndices":[],"checkedItemsTextOverflows":false}',
      'ProjectionBoard1$dg1$ctl00$ctl03$ctl01$PageSizeComboBox': '30',
      'ProjectionBoard1_dg1_ctl00_ctl03_ctl01_PageSizeComboBox_ClientState': '',
      'ProjectionBoard1_dg1_ClientState': ''
    }
    
    response = requests.post('https://www.fangraphs.com/projections.aspx', params=params, data=data)
    df = pd.read_html(response.text)
    

    read_html 返回所有表的列表作为数据框,您想要的表可以通过df[8] 访问。输出df[8].head()

    Name Unnamed: 1 Team W L ERA GS G IP H ER HR SO BB WHIP K/9 BB/9 FIP WAR ADP
    0 Lucas Giolito nan CHW 16 7 3 30 30 180 129 60 21 248 56 1.03 12.4 2.8 2.98 5.8 18.3
    1 Gerrit Cole nan NYY 16 8 3.21 32 32 193.3 155 69 27 260 49 1.06 12.1 2.28 3.18 5.7 7.4
    2 Shane Bieber nan CLE 14 9 3.51 32 32 197.7 177 77 26 230 36 1.08 10.47 1.64 3.23 5.4 10
    3 Jose Berrios nan MIN 13 10 3.89 31 31 180.3 167 78 24 187 56 1.24 9.33 2.79 3.98 3.5 82.9
    4 Chris Sale nan BOS 9 5 3.35 20 20 121 103 45 14 147 28 1.08 10.93 2.08 3.23 3.4 297

    【讨论】:

    • 谢谢你,我完全不知道 pd.read_html、复制 curl 请求以及上面的所有内容......我一直在寻找一些不同的包(urllib、request、selenium) ) 我对每个包的理解都比之前的包要少,我只是承认我试图强迫自己找到一个解决方案,盲目地投掷飞镖......
    猜你喜欢
    • 2021-06-11
    • 2022-01-05
    • 1970-01-01
    • 2020-03-17
    • 2022-01-12
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-03-06
    相关资源
    最近更新 更多