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

基于BS4与Requests的欧洲志愿服务爬虫报错求助

问题解决:欧盟青年志愿服务爬虫的404与ValueError问题

问题背景

需要爬取欧盟青年网站的志愿服务公开数据(包含名称、所属国家、网站等字段),并导出为XML/CSV格式。原代码通过遍历1到100000的ID生成详情页URL,运行时出现大量404错误,还触发ValueError: not enough values to unpack (expected 2, got 0)。

错误原因

  1. 无效ID遍历:1到100000的ID中绝大多数没有对应的志愿服务页面,直接遍历会产生大量无效请求(404),浪费资源。
  2. 元素缺失未处理:即使页面返回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")

改进说明

  1. 有效链接获取:通过遍历列表页的分页,提取所有真实存在的志愿服务详情页链接,彻底解决404问题。
  2. 异常安全处理:每个元素提取前都先判断是否存在,同时用try-except捕获全局异常,确保爬虫不会因单个页面的问题中断。
  3. 反爬优化:添加请求头模拟浏览器,设置请求延时,降低被网站拦截的风险。
  4. 数据导出:直接支持导出为CSV和XML格式,满足需求。

内容的提问来源于stack exchange,提问作者zero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:04:55