使用BeautifulSoup解析<ol>元素 按演出set分组曲目生成JSON
setlist.fm 演出曲目分段爬取实现方案
setlist.fm的演出分段标识存放在class="setlistList"容器的直接子<li>节点中:带class="setLabel"的节点为段落标题(如Set 1、Encore),两个相邻段落标题之间带class="songLabel"的<a>标签,对应该段落下的所有曲目。
核心实现逻辑
- 保留原有全量
songs列表的提取逻辑,同时新增分段存储字典 - 按DOM顺序遍历
setlistList下的直接子节点,自动识别段落标识 - 识别到新段落标识时,自动生成标准化的JSON键名(
set1/set2/encore),初始化对应空列表 - 识别到曲目节点时,同时将曲目名追加到全量歌曲列表和当前激活的分段列表中
- 遍历完成后将分段结果合并到最终输出的JSON结构中
修改后的代码
替换原有代码中songs提取及data组装部分即可:
def getConcertData(i, url, concerts): try: soup = getSoup(url) dateBlock = soup.find_all("div", {"class": "dateBlock"})[0] infoContainer = soup.find_all("div", {"class": "infoContainer"})[0] headLineDiv = infoContainer.find_all("div", {"class": "setlistHeadline"})[0] setlistDiv = soup.find_all("div", {"class": "setlistList"})[0] # 此处保留原有日期、艺人、场地、巡演信息的提取逻辑 songs = [] set_segments = {} current_segment = None set_index = 1 # 仅遍历setlist容器下的直接子li节点,避免嵌套元素干扰顺序 for li in setlistDiv.find_all("li", recursive=False): # 匹配段落标识节点 segment_label = li.find("span", class_="setLabel") if segment_label: label_content = segment_label.get_text(strip=True).lower() if "set" in label_content: current_segment = f"set{set_index}" set_index += 1 elif "encore" in label_content: current_segment = "encore" set_segments[current_segment] = [] continue # 匹配曲目节点 song_tag = li.find("a", class_="songLabel") if song_tag and current_segment: song_name = song_tag.get_text(strip=True) songs.append(song_name) set_segments[current_segment].append(song_name) print(str(year)+"."+str(month).zfill(2)+"."+str(day).zfill(2)+": "+venue) data = dict() data["artist"] = artist data["year"] = year data["month"] = month data["day"] = day data["venue"] = venue data["tour"] = tour data["songs"] = songs # 合并分段曲目数据 data.update(set_segments) concerts[i] = data
适配说明
- 代码默认兼容无明确段落标识的演出场景,第一个识别到的曲目会自动归入
set1 - 若站点段落标识文案有调整,可修改
label_content的关键词匹配规则适配 - 输出结构完全匹配目标JSON格式,同时保留了原有全量曲目字段
内容的提问来源于stack exchange,提问作者thrownsos
相关产品推荐
相关产品推荐

