使用BeautifulSoup批量下载意大利市政Zip文件遇问题求助
问题解决:意大利市政Zip文件批量下载
问题说明
需要为7000多个意大利市政单位下载Zip文件,文件可通过对应市政页面的“SCARICA I DATI CSV”按钮获取。但原代码访问市政主页后,返回的HTML中找不到任何CSV相关链接,导致代码报错。
原单个市政下载代码:
city_name = "vandoies-vintl" prov_name = "bz" r = urllib.request.urlopen('http://storico.openbilanci.it/bilanci/' + city_name + "-comune-" + prov_name).read() soup = BeautifulSoup(r, 'lxml') # 此处报错:HTML中无csv相关内容 csv = soup.find_all('a', attrs={'class':'pull-right csv'}) csvlink = csv[0]['href'] urllib.request.urlretrieve("http://storico.openbilanci.it" + csvlink, city_name+".zip")
问题原因
你访问的是市政的主页(无参数URL),但下载按钮仅存在于带年份、类型参数的子页面中(比如示例页面的year=2014&type=consuntivo&cas_com_type=cassa参数)。主页没有下载链接,必须先进入对应子页面才能抓取。
解决方案
1. 单个市政下载修复代码
如果固定下载某年份(比如2014)的consuntivo类型数据,可以直接构造子页面URL:
city_name = "vandoies-vintl" prov_name = "bz" # 构造带参数的子页面URL url = f'http://storico.openbilanci.it/bilanci/{city_name}-comune-{prov_name}?year=2014&type=consuntivo&cas_com_type=cassa' r = urllib.request.urlopen(url).read() soup = BeautifulSoup(r, 'lxml') # 查找下载链接 csv_links = soup.find_all('a', attrs={'class':'pull-right csv'}) if csv_links: csvlink = csv_links[0]['href'] urllib.request.urlretrieve(f"http://storico.openbilanci.it{csvlink}", f"{city_name}.zip") print("下载成功") else: print("未找到下载链接")
2. 批量下载代码修改版
以下是修改后的批量代码,增加了子页面URL构造逻辑,完善了错误处理:
import pandas as pd import numpy as np import time from bs4 import BeautifulSoup import urllib.request import re import os import zipfile output_path = r"/Users/aartimalik/Dropbox/delphine-miscellaneous/italy/test" # 市政列表 munis = [("monale", "at"), ("portacomaro", "at")] munis = pd.DataFrame(munis, columns=['municipality_clean','prov_abb']) # 清洗市政名称格式 def remove_paren(string): return re.sub(r'\(.*\)', '', str(string)) munis['municipality_clean'] = munis['municipality_clean'].apply(remove_paren).str.strip() munis['municipality_clean'] = munis['municipality_clean'].str.replace(' ', '-').str.lower() # 初始化结果存储 scrapesuccess = pd.DataFrame(columns=munis.columns) scrapefail = pd.DataFrame(columns=munis.columns) # 遍历市政 for idx, row in munis.iterrows(): city_name = row['municipality_clean'] prov_name = row['prov_abb'] try: # 构造子页面URL(以2014年consuntivo类型为例) sub_url = f'http://storico.openbilanci.it/bilanci/{city_name}-comune-{prov_name}?year=2014&type=consuntivo&cas_com_type=cassa' r = urllib.request.urlopen(sub_url).read() soup = BeautifulSoup(r, 'lxml') # 查找下载链接 csv_links = soup.find_all('a', attrs={'class':'pull-right csv'}) if csv_links: csvlink = csv_links[0]['href'] zip_file_path = os.path.join(output_path, f"{city_name}.zip") urllib.request.urlretrieve(f"http://storico.openbilanci.it{csvlink}", zip_file_path) # 创建文件夹并解压 new_folder = os.path.join(output_path, city_name.capitalize()) os.makedirs(new_folder, exist_ok=True) with zipfile.ZipFile(zip_file_path, 'r') as zip_ref: zip_ref.extractall(new_folder) print(f"{idx}. {city_name}: 成功") scrapesuccess = pd.concat([scrapesuccess, pd.DataFrame([row])], ignore_index=True) else: print(f"{idx}. {city_name}: 未找到下载链接") scrapefail = pd.concat([scrapefail, pd.DataFrame([row])], ignore_index=True) except Exception as e: print(f"{idx}. {city_name}: 失败 - {str(e)}") scrapefail = pd.concat([scrapefail, pd.DataFrame([row])], ignore_index=True) # 保存成功/失败列表(可选) scrapesuccess.to_csv(os.path.join(output_path, 'success_list.csv'), index=False) scrapefail.to_csv(os.path.join(output_path, 'fail_list.csv'), index=False)
额外说明
- 如果需要下载多个年份或类型的数据,可以先从市政主页提取所有可用的参数组合(比如年份下拉框、类型选项),再循环遍历这些组合构造URL
- 建议增加请求间隔(比如
time.sleep(1)),避免对服务器造成过大压力 - 部分市政可能没有对应年份的数据,需做好错误处理
内容的提问来源于stack exchange,提问作者Pepa
相关产品推荐
相关产品推荐

