python - 使用beautifulsoup点击页面,然后搜索合适的链接打开和抓取
问题描述
我已经环顾四周,我想知道这是否可能。我正在尝试从https://www.scoreboard.com/en/soccer/england/premier-league/获取一些体育统计数据
我将这些用于刮板和 csv 导出:
import requests
import re
import csv
from bs4 import BeautifulSoup
当刮板到达那里时,我希望它点击具有最终得分的游戏(因此我可能必须使刮板在周五、周六、周日和周一午夜自动运行)。从那里,我希望抓取工具单击“统计信息”,这将在新窗口中打开需要抓取的 URL。
从那里完成那一周的所有比赛并收集统计数据将是理想的选择。
Beautifulsoup 甚至可以做到这一点吗?
我可以通过手动添加链接来抓取每个页面,然后对于输出数据,我必须为每个页面创建一个新的 .csv 文件。我不介意,它很快。我已经做好了一切准备——但如果我可以自动化它,那么我肯定很想学习如何去做。
解决方案
有两件事让这个网站很难单独使用 beautifulsoup 来抓取:
数据不是 html 中的纯文本。它位于
div
带有 id的位置,tournament-page-data-results
如下所示:SA÷1¬~ZA÷ENGLAND: Premier League¬ZEE÷dYlOSQOD¬ZB÷198¬ZY÷England¬ZC÷zTRyeuJg¬ZD÷t¬ZE÷AJuiuwWt¬ZF÷0¬ZO÷0¬ZG÷1¬ZH÷198_dYlOSQOD¬ZJ÷2¬ZL÷/en/soccer/england/premier-league/¬ZX÷00England 007ngland0000000000001000Premier Leag014League000¬ZCC÷0¬ZAF÷England¬~AA÷6J0L2p0r¬AD÷1601835300¬ADE÷1601835300¬AB÷3¬CR÷3¬AC÷3¬CX÷Aston Villa¬ER÷Round 4¬RW÷0¬AX÷1¬AO÷1601842143¬BX÷-1¬HMC÷1¬WQ÷¬WN÷LIV¬AF÷Liverpool¬JB÷Yi2C1SGu¬WV÷liverpool¬AH÷2¬BB÷1¬BD÷1¬WM÷AST¬AE÷Aston Villa¬JA÷f3v9dzKU¬WU÷aston-villa¬AS÷1¬AZ÷1¬AG÷7¬BA÷4¬BC÷3¬AW÷1¬~AA÷l2dtbMED¬AD÷1601825400¬ADE÷1601825400¬AB÷3¬CR÷3¬AC÷3¬CX÷Manchester Utd¬ER÷Round 4¬RW÷0¬AX÷1¬AO÷1601832194¬BX÷-1¬HMC÷1¬WQ÷¬WN÷TOT¬AF÷Tottenham¬JB÷IHkhE50o¬WV÷tottenham¬AS÷2¬AZ÷2¬AH÷6¬BB÷4¬BD÷2¬WM÷MNU¬AE÷Manchester Utd¬JA÷U1dAkMNp¬WU÷manchester-united¬AJ÷1¬AG÷1¬BA÷1¬BC÷0¬AW÷1¬~AA÷0xOh7QiR¬AD÷1601816400¬ADE÷1601816400¬AB÷3¬CR÷3¬AC÷3¬CX÷Arsenal¬ER÷Round 4¬RW÷0¬AX÷1¬AO÷1601823089¬BX÷-1¬HMC÷1¬WQ÷¬WM÷ARS¬AE÷Arsenal¬JA÷MyR1bdkI¬WU÷arsenal¬AS÷1¬AZ÷1¬AG÷2¬BA÷0¬BC÷2¬WN÷SHU¬AF÷Sheffield Utd¬JB÷GCu5cG4O¬WV÷sheffield-utd¬AH÷1¬BB÷0¬BD÷1¬AW÷1¬~AA÷rFhWKqMQ¬AD÷1601816400¬ADE÷1601816400¬AB÷3¬CR÷3¬AC÷3¬C
它是一种自定义格式,~
用作行分隔符、¬
单元格分隔符和÷
分隔键/值。可以通过查看js来推断逻辑(以.开头的文件core_*.js
。为了在python中解析它,我们需要重现逻辑并为标签添加所有匹配项(例如ZA
isSHAREDINDEXES_TOURNAMENT_NAME
等)
- 第二件事是要进入统计页面,您需要一个在 JS 中硬编码在同一文件中的令牌
core_*.js
。
使用正则表达式从 JS 中提取令牌表明在这种情况下您可能会更好地使用 selenium
以下代码提取匹配数据,解析自定义格式,获取 js 文件,从中提取令牌,生成 stats url 并获取 stats html:
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
DELIMITER_ROW = "~"
DELIMITER_CELL = "¬"
DELIMITER_VALUE = "÷"
indexes = {
"COMMONINDEXES_AWAY_FIRST_OUTS": 'ER',
"COMMONINDEXES_FT_WINNER": 'AZ',
"COMMONINDEXES_ROW": 'RW',
"FSCORE_DRAWINDEXES_ROUND_ADVANCING_PARTICIPANT": 'AE',
"FULLFEEDINDEXES_AWAY_3CHAR_NAME": 'WN',
"FULLFEEDINDEXES_AWAY_EVENT_PARTICIPANT_ID": 'JB',
"FULLFEEDINDEXES_AWAY_PARTICIPANT_NAME": 'AF',
"FULLFEEDINDEXES_AWAY_PARTICIPANT_NAME_URL": 'WV',
"FULLFEEDINDEXES_EVENT_START_UTIME": 'ADE',
"FULLFEEDINDEXES_HAS_MATCH_COMMENTS": 'HMC',
"FULLFEEDINDEXES_HOME_3CHAR_NAME": 'WM',
"FULLFEEDINDEXES_HOME_EVENT_PARTICIPANT_ID": 'JA',
"FULLFEEDINDEXES_HOME_PARTICIPANT_NAME_URL": 'WU',
"FULLFEEDINDEXES_SORT_PARTICIPANT": 'CX',
"FULLFEEDINDEXES_WINNER": 'AS',
"SHAREDINDEXES_AWAY_CURRENT_RESULT": 'AH',
"SHAREDINDEXES_AWAY_RESULT_PERIOD_1": 'BB',
"SHAREDINDEXES_AWAY_RESULT_PERIOD_2": 'BD',
"SHAREDINDEXES_CRICKET_RECENT_OVERS": 'WQ',
"SHAREDINDEXES_EVENT_ID": 'AA',
"SHAREDINDEXES_EVENT_STAGE_ID": 'AC',
"SHAREDINDEXES_EVENT_STAGE_TYPE_FROM_EVENT_STAGE_ID": 'CR',
"SHAREDINDEXES_EVENT_STAGE_TYPE_ID": 'AB',
"SHAREDINDEXES_GAME_TIME": 'BX',
"SHAREDINDEXES_HAS_LINEUPS": 'AX',
"SHAREDINDEXES_HAS_LIVE_CENTRE": 'AW',
"SHAREDINDEXES_HOME_CURRENT_RESULT": 'AG',
"SHAREDINDEXES_HOME_RESULT_PERIOD_1": 'BA',
"SHAREDINDEXES_HOME_RESULT_PERIOD_2": 'BC',
"SHAREDINDEXES_MATCH_START_UTIME": 'AD',
"SHAREDINDEXES_PERIOD_START_UTIME": 'AO',
"SHAREDINDEXES_SPORT_ID": 'SA',
"SHAREDINDEXES_TOURNAMENT_NAME": 'ZA',
"LEAGUEINDEXES_COUNTRY_ID": "ZB",
"FULLFEEDINDEXES_TOURNAMENT_TEMPLATE_ID": "ZEE",
"LEAGUEINDEXES_COUNTRY_NAME": "ZY",
"SHAREDINDEXES_TOURNAMENT_STAGE_ID": "ZC",
"LEAGUEINDEXES_TOURNAMENT_TYPE": "ZD",
"LEAGUEINDEXES_TOURNAMENT_ID": "ZE",
"LEAGUEINDEXES_SOURCE_TYPE": "ZF",
"UPDATEINDEXES_HAS_LIVE_TABLE": "ZO",
"LEAGUEINDEXES_STATS_TYPE": "ZG",
"LEAGUEINDEXES_TOURNAMENT_TEMPLATE_KEY": "ZH",
"LEAGUEINDEXES_TOURNAMENT_STAGE_TYPE": "ZJ",
"LEAGUEINDEXES_TOURNAMENT_TEMPLATE_URL": "ZL",
"LEAGUEINDEXES_SORT_KEY": "ZX",
"LEAGUEINDEXES_STAGES_COUNT":"ZCC",
"FULLFEEDINDEXES_CATEGORY_CAPTION": "ZAF",
"SHAREDINDEXES_HOME_RED_CARD_COUNT": "AJ",
"SHAREDINDEXES_AWAY_RED_CARD_COUNT": "AK"
}
r = requests.get("https://www.scoreboard.com/en/soccer/england/premier-league/results/")
soup = BeautifulSoup(r.text, "html.parser")
script = [t["src"] for t in soup.findAll("script") if t.get("src") and "core_" in t["src"]][0]
data = soup.find("div", {"id": "tournament-page-data-results"}).text
rows = [t.split(DELIMITER_CELL) for t in data.split(DELIMITER_ROW)]
data = []
for r in rows:
rowData = dict([(t[0], t[1])
for t in (
t.split(DELIMITER_VALUE)
for t in r
)
if len(t) > 1
])
for key in indexes.keys():
if indexes[key] in rowData:
rowData[key] = rowData[indexes[key]]
del rowData[indexes[key]]
if "SHAREDINDEXES_PERIOD_START_UTIME" in rowData:
data.append(rowData)
df = pd.DataFrame(data)
print(df)
r = requests.get(f"https://www.scoreboard.com{script}")
dataReg = re.search("feed_sign\s*=\s*'(.*)'", r.text, re.MULTILINE)
token = dataReg.group(1)
#get statistics for the first match df[0,0]
eventId = df["SHAREDINDEXES_EVENT_ID"].values[0]
r = requests.get(f"https://d.scoreboard.com/en/x/feed/d_su_{eventId}_en_1",
headers = {
"x-fsign": token
})
soup = BeautifulSoup(r.text, "html.parser")
#get the stats you want from soup
print(soup)
推荐阅读
- php - 多个 Laravel 项目 htaccess
- html - 如何在图像 HTML CSS 中使文本居中
- html - CSS下划线与上述元素相同
- ruby-on-rails - 在关联错误消息中隐藏/删除模型名称
- python - 无法分配“
":" 必须是一个 "" 实例 - javascript - Laravel Passport API 向未使用付费号码的用户发送 OTP
- html - 无法使用 :link 选择器更改链接的颜色
- javascript - 使用 React 的地图加载不正确传单
- reactjs - 返回内部功能组件中的显示值
- javascript - 如何在 Chrome 的全局媒体控件中使用 blob URL 作为图像源?