基于BS4与Requests的欧洲志愿服务爬虫报错求助
问题解决:欧盟青年志愿服务爬虫的404与ValueError问题
问题背景
需要爬取欧盟青年网站的志愿服务公开数据(包含名称、所属国家、网站等字段),并导出为XML/CSV格式。原代码通过遍历1到100000的ID生成详情页URL,运行时出现大量404错误,还触发ValueError: not enough values to unpack (expected 2, got 0)。
错误原因
- 无效ID遍历:1到100000的ID中绝大多数没有对应的志愿服务页面,直接遍历会产生大量无效请求(404),浪费资源。
- 元素缺失未处理:即使页面返回200,部分页面可能缺少预期的DOM元素(比如日期标签、标题标签),直接调用
get_text()或解包变量会抛出异常。
解决方案
核心改进点
- 从列表页获取有效链接:放弃盲目遍历ID,改为从官方志愿服务列表页爬取真实存在的详情页URL,彻底避免404。
- 增加元素存在性判断:对每个要提取的元素先检查是否存在,再进行后续操作,防止因元素缺失抛出异常。
- 模拟浏览器请求:添加请求头,降低被反爬拦截的概率。
- 添加请求延时:控制请求频率,避免触发网站的反爬机制。
修改后的完整代码
import requests from bs4 import BeautifulSoup import pandas as pd import time # 配置请求头,模拟浏览器访问 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' } # 从列表页获取所有志愿服务详情页链接 def get_valid_placement_urls(): base_list_url = "https://youth.europa.eu/go-abroad/volunteering/opportunities_en" urls = [] page = 1 while True: list_url = f"{base_list_url}?page={page}" response = requests.get(list_url, headers=HEADERS) if response.status_code != 200: break soup = BeautifulSoup(response.content, 'html.parser') # 提取列表中所有详情页链接 placement_links = soup.select('a.card__link') if not placement_links: break # 没有更多链接时停止 for link in placement_links: full_url = "https://youth.europa.eu" + link['href'] urls.append(full_url) page += 1 time.sleep(1) # 每页请求后延时1秒 return urls # 单页面数据爬取,增加元素存在性判断 def scrape_data(url): try: response = requests.get(url, headers=HEADERS) response.raise_for_status() # 抛出HTTP错误状态码的异常 soup = BeautifulSoup(response.content, 'html.parser') # 提取标题 title = soup.h1.get_text(', ', strip=True) if soup.h1 else None # 提取地点 location_tag = soup.select_one('p:has(i.fa-location-arrow)') location = location_tag.get_text(', ', strip=True) if location_tag else None # 提取日期 date_elements = soup.select('span.extra strong')[-2:] start_date = date_elements[0].get_text(strip=True) if len(date_elements) >=1 else None end_date = date_elements[1].get_text(strip=True) if len(date_elements) >=2 else None # 提取网站链接 website_tag = soup.find("a", class_="btn__link--website") website = website_tag.get("href") if website_tag else None return { "Title": title, "Location": location, "Start Date": start_date, "End Date": end_date, "Website": website, "URL": url } except Exception as e: print(f"爬取 {url} 失败: {str(e)}") return None # 主流程 if __name__ == "__main__": print("正在获取有效详情页链接...") urls = get_valid_placement_urls() print(f"共获取到 {len(urls)} 个有效链接") print("开始爬取数据...") data = [] for idx, url in enumerate(urls, 1): print(f"正在爬取第 {idx}/{len(urls)} 个链接: {url}") placement_data = scrape_data(url) if placement_data: data.append(placement_data) time.sleep(0.5) # 单页面请求后延时0.5秒 # 转为DataFrame并导出 df = pd.DataFrame(data) print("爬取完成,数据预览:") print(df.head()) # 导出为CSV和XML df.to_csv("eu_volunteering_opportunities.csv", index=False, encoding='utf-8-sig') df.to_xml("eu_volunteering_opportunities.xml", index=False) print("数据已导出为 eu_volunteering_opportunities.csv 和 eu_volunteering_opportunities.xml")
改进说明
- 有效链接获取:通过遍历列表页的分页,提取所有真实存在的志愿服务详情页链接,彻底解决404问题。
- 异常安全处理:每个元素提取前都先判断是否存在,同时用
try-except捕获全局异常,确保爬虫不会因单个页面的问题中断。 - 反爬优化:添加请求头模拟浏览器,设置请求延时,降低被网站拦截的风险。
- 数据导出:直接支持导出为CSV和XML格式,满足需求。
内容的提问来源于stack exchange,提问作者zero
相关产品推荐
相关产品推荐

