You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python抓取RaceRoster网站的赛事链接及联系信息(解决动态加载内容问题)

如何用Python抓取RaceRoster网站的赛事链接及联系信息(解决动态加载内容问题)

嘿,我看了你的问题和脚本,核心问题一下子就找到了——RaceRoster的搜索结果是通过JavaScript动态加载的!你用requests拿到的只是服务器返回的初始HTML框架,那些赛事卡片是浏览器执行JS之后才生成的,所以BeautifulSoup自然找不到对应的a.search-results__card-event-name元素,这就是为什么你一直看到“Found 0 events”的原因。

下面我给你一步步解决这个问题:

一、为什么原来的脚本失效?

  • 动态内容渲染:网站的搜索结果不是直接写在初始HTML里的,而是浏览器加载页面后,通过JS请求数据再渲染出来的。requests只能获取静态的初始HTML,无法执行JS,所以拿不到渲染后的赛事链接。
  • 你检查soup.prettify()看不到赛事链接,完全符合这个情况。

二、解决方案:用Selenium模拟浏览器

要抓取动态加载的内容,我们需要用能模拟真实浏览器行为的工具——Selenium。它可以打开浏览器,等待页面加载完成(包括JS执行),然后获取完整的渲染后页面源码,再交给BeautifulSoup处理。

准备工作

  1. 安装Selenium:在终端执行pip install selenium
  2. 下载对应浏览器的驱动:比如用Chrome的话,下载ChromeDriver(要和你的Chrome版本完全匹配),可以放在系统PATH里,或者在代码里指定驱动路径。

三、修改后的完整脚本

我已经把你的脚本改成了用Selenium处理搜索页,详情页还是用requests(更高效),关键部分都加了注释:

import requests
from bs4 import BeautifulSoup
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

def scrape_event_contacts(base_url, search_url):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
    event_contacts = []

    # 初始化Chrome浏览器,无头模式(不显示窗口)
    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    options.add_argument("--disable-blink-features=AutomationControlled")  # 避免被反爬识别
    options.add_argument(f"user-agent={headers['User-Agent']}")
    
    driver = webdriver.Chrome(options=options)
    
    try:
        print(f"Scraping page: {search_url}")
        driver.get(search_url)
        
        # 等待赛事链接加载完成,最多等10秒(防止页面加载慢)
        try:
            WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, "a.search-results__card-event-name"))
            )
        except TimeoutException:
            print("等待页面加载超时,未找到赛事链接")
            driver.quit()
            return event_contacts
        
        # 获取渲染后的完整页面源码
        page_source = driver.page_source
        soup = BeautifulSoup(page_source, "html.parser")
        
        # 现在可以正常拿到所有赛事链接了
        event_links = soup.select("a.search-results__card-event-name")
        print(f"Found {len(event_links)} events on the page.")

        for link in event_links:
            event_url = link['href']
            event_name = link.text.strip()

            try:
                print(f"Scraping event: {event_url}")
                # 详情页用requests抓取,一般静态内容足够
                event_response = requests.get(event_url, headers=headers)
                if event_response.status_code != 200:
                    print(f"Failed to fetch event page: {event_url}, status code: {event_response.status_code}")
                    continue

                event_soup = BeautifulSoup(event_response.content, "html.parser")

                # 提取联系人信息,根据页面结构调整选择器
                contact_name = event_soup.find("dd", class_="event-details__contact-list-definition")
                email = event_soup.find("a", href=lambda href: href and "mailto:" in href)

                contact_name_text = contact_name.text.strip() if contact_name else "N/A"
                email_address = email['href'].split("mailto:")[1].split("?")[0] if email else "N/A"

                if contact_name or email:
                    print(f"Found contact: {contact_name_text}, email: {email_address}")
                    event_contacts.append({
                        "Event Name": event_name,
                        "Event URL": event_url,
                        "Event Contact": contact_name_text,
                        "Email": email_address
                    })
                else:
                    print(f"No contact information found for {event_url}")
            except Exception as e:
                print(f"Error scraping event {event_url}: {e}")

    finally:
        # 不管成功失败,都要关闭浏览器,避免资源占用
        driver.quit()

    print(f"Scraped {len(event_contacts)} events.")
    return event_contacts

def save_to_spreadsheet(data, output_file):
    if not data:
        print("No data to save.")
        return
    df = pd.DataFrame(data)
    df.to_excel(output_file, index=False)
    print(f"Data saved to {output_file}")

if __name__ == "__main__":
    base_url = "https://raceroster.com"
    search_url = "https://raceroster.com/search?q=5k&t=upcoming"
    output_file = "/Users/my_name/Documents/event_contacts.xlsx"

    contact_data = scrape_event_contacts(base_url, search_url)
    if contact_data:
        save_to_spreadsheet(contact_data, output_file)
    else:
        print("No contacts were scraped.")

四、关键修改说明

  1. Selenium初始化:用无头模式运行Chrome,既不影响你使用电脑,也能避免一些反爬检测;同时设置了自定义User-Agent,伪装成真实浏览器。
  2. 等待页面加载:用WebDriverWait等待赛事链接元素出现,确保页面完全渲染后再抓取,避免因为加载慢导致的空结果。
  3. 获取渲染后源码:通过driver.page_source拿到浏览器渲染后的完整HTML,再交给BeautifulSoup解析,这样就能拿到所有赛事链接了。
  4. 详情页优化:详情页一般是静态内容,用requests抓取比Selenium更高效,节省资源。

五、注意事项

  • 确保ChromeDriver版本和你的Chrome浏览器版本一致,否则会启动失败。
  • 如果网站有反爬机制,可以尝试加入随机延迟(比如time.sleep(random.uniform(1,3))),避免请求过于频繁。
  • 如果联系人信息的选择器不对,需要打开赛事详情页,用浏览器开发者工具重新查看HTML结构,调整对应的选择器。

备注:内容来源于stack exchange,提问作者Guill T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:09:35