抓取动态彩票结果表格遇问题:解析结果不一致
问题分析与解决方案
问题背景
尝试抓取https://star49s.com/results/teatime的下午茶时段彩票开奖结果,使用requests+BeautifulSoup时出现以下异常:
- 有时能正常获取最新结果
- 有时会跳过部分行
- 偶尔重新运行脚本出现重复条目
当前代码:
import requests from bs4 import BeautifulSoup url = "https://star49s.com/results/teatime" response = requests.get(url) soup = BeautifulSoup(response.text, "html.parser") results = [] for row in soup.select("div.results-row"): numbers = [num.get_text(strip=True) for num in row.select("span.ball")] date = row.select_one("span.date").get_text(strip=True) if row.select_one("span.date") else None if numbers: results.append({"date": date, "numbers": numbers}) print(results)
核心原因排查
- 动态内容渲染:页面可能通过JavaScript异步加载结果(比如滚动加载、AJAX拉取数据),
requests只能获取初始HTML,无法执行JS,导致部分行未被抓取到。 - 服务器缓存/动态内容:服务器可能返回缓存内容或根据请求头动态调整响应,导致多次请求结果不一致。
- 选择器精度不足:
div.results-row可能不是最精准的选择器,页面中可能存在隐藏的重复行或未完全加载的行被选中。
解决方案
方案1:优先尝试无浏览器的优化(推荐先验证)
如果页面数据是通过AJAX加载的,直接调用API比解析HTML更可靠:
- 打开浏览器开发者工具(F12),切换到Network标签,刷新页面,筛选
XHR/Fetch请求,查找返回开奖结果的API接口(通常是JSON格式)。 - 找到后,直接用
requests调用该API,解析JSON数据,无需处理HTML。
如果必须解析HTML,优化现有代码:
import requests from bs4 import BeautifulSoup from datetime import datetime # 模拟浏览器请求头,避免被识别为爬虫 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" } url = "https://star49s.com/results/teatime" response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() # 抛出请求错误,避免静默失败 soup = BeautifulSoup(response.text, "html.parser") results = [] # 用更精准的选择器,结合父容器缩小范围 for row in soup.select("div.results-container div.results-row"): date_elem = row.select_one("span.date") numbers_elems = row.select("span.ball") # 确保日期和号码都存在才添加,过滤不完整条目 if date_elem and numbers_elems: date = date_elem.get_text(strip=True) numbers = [num.get_text(strip=True) for num in numbers_elems] results.append({"date": date, "numbers": numbers}) # 基于日期去重,避免重复条目 unique_results = {} for item in results: unique_results[item["date"]] = item results = list(unique_results.values()) # 按日期倒序排序,确保最新结果在前 results.sort(key=lambda x: datetime.strptime(x["date"], "%d/%m/%Y"), reverse=True) print(results)
优化点说明:
- 添加浏览器请求头,避免被服务器拦截或返回差异化内容
- 增加请求错误处理,及时发现请求失败问题
- 用更精准的选择器,减少误选隐藏或未加载的行
- 增加数据校验,只保留完整的条目
- 基于日期去重,解决重复条目问题
- 按日期排序,保证结果顺序符合预期
方案2:使用无头浏览器(当方案1无效时)
如果页面确实是完全动态渲染(比如结果由JS生成,无API接口),则需要使用Selenium或Playwright执行JS:
Playwright示例(轻量高效,推荐)
from playwright.sync_api import sync_playwright from datetime import datetime results = [] with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36") # 等待网络空闲,确保所有动态内容加载完成 page.goto("https://star49s.com/results/teatime", wait_until="networkidle") # 滚动到页面底部,触发可能的滚动加载逻辑 page.evaluate("window.scrollTo(0, document.body.scrollHeight)") page.wait_for_timeout(1000) # 等待加载完成 rows = page.query_selector_all("div.results-row") for row in rows: date = row.query_selector("span.date") numbers = row.query_selector_all("span.ball") if date and numbers: date_text = date.text_content().strip() numbers_list = [num.text_content().strip() for num in numbers] results.append({"date": date_text, "numbers": numbers_list}) browser.close() # 去重+排序,保证数据唯一性和顺序 unique_results = {} for item in results: unique_results[item["date"]] = item results = list(unique_results.values()) results.sort(key=lambda x: datetime.strptime(x["date"], "%d/%m/%Y"), reverse=True) print(results)
确保数据可靠的额外措施
- 持久化存储:将抓取到的结果存储到本地文件(如JSON、CSV)或数据库,每次抓取后对比已有数据,只新增未存在的条目。
- 重试机制:对请求失败或数据不完整的情况,添加重试逻辑(比如使用
tenacity库)。 - 定时监控:如果需要定期抓取,设置定时任务,并添加告警(如数据连续多次异常时通知)。
内容的提问来源于stack exchange,提问作者Zuryab
相关产品推荐
相关产品推荐

