BeautifulSoup爬取KCBS活动页时无法单独拆分提取目标字段问题
KCBS赛事爬虫字段拆分实现方案
核心问题定位
原解析逻辑失效有三个直接原因:
- 原代码URL拼接存在笔误,
evr_end参数前多了冗余的.字符,可能导致接口返回异常数据 - 活动详情字段全部存放在
class="col-md-4"的div容器内,不同字段仅通过<br>标签分隔,没有独立的HTML标签做区分,直接调用getText()会把所有字段拼接成无分隔的长文本,无法拆分 - 部分字段没有专属class或id属性,无法直接通过标签选择器定位,需要结合文本特征做匹配
实现思路
放弃直接对容器节点取整段文本的做法,遍历详情容器的所有子节点,以<br>标签为分隔符拆分出独立的文本块,再根据每个文本块的固定特征匹配对应字段:
- 带
Prize Money:前缀的文本块对应奖金金额 - 带
Reps:前缀的文本块对应负责人员 - 取值为全大写、属于赛事等级枚举值的文本块对应赛事等级
- 匹配国家名称关键词的文本块对应所属国家
- 剩余符合地址格式(含州缩写、邮编)的文本块对应举办地址
完整可运行代码
import requests from bs4 import BeautifulSoup, Tag from datetime import datetime from dateutil.relativedelta import relativedelta # 构造请求时间范围 evr_begin = datetime.now().strftime("%m/%d/%Y") evr_end = (datetime.now() + relativedelta(months=1)).strftime("%m/%d/%Y") # 修正原URL的拼接笔误,去掉evr_end前多余的点 url = "https://mms.kcbs.us/members/evr_search_ol_json.php?" \ f"otype=TEXT&evr_map_type=2&org_id=KCBA&evr_begin={evr_begin}&evr_end={evr_end}&" \ "evr_radius=50&evr_type=269&evr_region_type=1" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" } response = requests.get(url, headers=headers) soup = BeautifulSoup(response.text, features='lxml') # 可根据实际遇到的赛事等级、国家值扩充关键词列表 LEVEL_KEYWORDS = {"STATE CHAMPIONSHIP", "REGIONAL", "NATIONAL", "LOCAL", "INTERNATIONAL"} COUNTRY_KEYWORDS = {"UNITED STATES", "CANADA", "MEXICO"} for event in soup.find_all('div', class_='row'): # 提取已有的标题、时间字段 event_title = event.find('b').get_text(strip=True) if event.find('b') else "" event_time = event.find('i').get_text(strip=True) if event.find('i') else "" # 初始化待提取字段 address = "" country = "" level = "" reps = "" prize = "" detail_div = event.find('div', class_='col-md-4') if detail_div: # 按br标签拆分独立文本块 text_blocks = [] current_block = [] for child in detail_div.contents: if isinstance(child, Tag) and child.name == 'br': block_text = ''.join(current_block).strip() if block_text: text_blocks.append(block_text) current_block = [] else: current_block.append(str(child).strip()) # 处理最后一个文本块 last_block = ''.join(current_block).strip() if last_block: text_blocks.append(last_block) # 逐块匹配字段 for block in text_blocks: if block.startswith("Prize Money:"): prize = block elif block.startswith("Reps:"): reps = block elif block in LEVEL_KEYWORDS: level = block elif block in COUNTRY_KEYWORDS: country = block else: # 剩余符合地址特征的块赋值给地址 address = block # 输出结果 print("="*50) print(f"活动标题:{event_title}") print(f"活动时间:{event_time}") print(f"举办地址:{address}") print(f"所属国家:{country}") print(f"赛事等级:{level}") print(f"负责人员:{reps}") print(f"奖金金额:{prize}")
适配说明
- 代码新增了常规请求头,避免接口拦截无User-Agent的默认请求
- 爬取过程中如果遇到未覆盖的赛事等级、国家取值,直接将对应值补充到
LEVEL_KEYWORDS和COUNTRY_KEYWORDS集合即可识别 - 当前地址字段为兜底匹配逻辑,如果后续页面出现其他非地址的冗余文本块,可根据实际文本特征新增专属判断规则调整。
内容的提问来源于stack exchange,提问作者user19341179
相关产品推荐
相关产品推荐

