如何使用Python抓取隐藏在按钮后的CSV文件:批量获取加拿大统计局网站500+CSV文件技术问询
批量下载StatCan 2016 Census CSV文件的Python方案
我明白你要处理的是多层链接嵌套的CSV批量下载问题,这种重复性任务用Python自动化完全能搞定。下面是一步步的解决方案,包含完整代码和关键细节说明:
核心流程拆解
整个任务分为三个关键步骤,逐层穿透链接获取最终的CSV文件:
- 第一步:从初始结果页抓取所有地区详情页的链接
- 第二步:从每个详情页跳转至对应的下载页面
- 第三步:从下载页面提取CSV文件的直接下载链接并保存
所需依赖
先安装必要的Python库:
pip install requests beautifulsoup4
完整代码实现
import requests from bs4 import BeautifulSoup import os import time # 基础配置 BASE_DOMAIN = "https://www12.statcan.gc.ca" INITIAL_PAGE_PATH = "/census-recensement/2016/dp-pd/prof/search-recherche/lst/results-resultats.cfm?Lang=E&TABID=1&G=1&Geo1=&Code1=&Geo2=&Code2=&GEOCODE=35&type=0#" DOWNLOAD_DIR = "statcan_2016_census_csvs" # 创建下载文件夹(不存在则自动创建) os.makedirs(DOWNLOAD_DIR, exist_ok=True) # 模拟浏览器请求头,避免被服务器拦截 REQUEST_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" } def fetch_detail_page_links(): """从初始结果页抓取所有地区详情页的链接""" response = requests.get(BASE_DOMAIN + INITIAL_PAGE_PATH, headers=REQUEST_HEADERS) soup = BeautifulSoup(response.text, "html.parser") # 通过页面元素特征定位详情页链接(可根据实际页面结构调整选择器) detail_links = [] for link in soup.select("a[href*='page.cfm']"): full_link = BASE_DOMAIN + link.get("href") if full_link not in detail_links: detail_links.append(full_link) print(f"共找到 {len(detail_links)} 个地区详情页链接") return detail_links def get_download_page_url(detail_url): """从地区详情页获取下载页面的跳转链接""" response = requests.get(detail_url, headers=REQUEST_HEADERS) soup = BeautifulSoup(response.text, "html.parser") # 定位页面顶部的下载箭头按钮对应的链接 download_btn = soup.select_one("a[href*='page_Download-Telecharger.cfm']") return BASE_DOMAIN + download_btn.get("href") if download_btn else None def extract_csv_download_link(download_page_url): """从下载页面提取选项1的CSV文件直接下载链接""" response = requests.get(download_page_url, headers=REQUEST_HEADERS) soup = BeautifulSoup(response.text, "html.parser") # 定位"选项1"下的CSV下载按钮链接 csv_btn = soup.select_one("a[href*='.csv']") return BASE_DOMAIN + csv_btn.get("href") if csv_btn else None def save_csv_file(csv_url, file_name): """下载并保存CSV文件到指定目录""" response = requests.get(csv_url, headers=REQUEST_HEADERS) file_path = os.path.join(DOWNLOAD_DIR, file_name) with open(file_path, "wb") as file: file.write(response.content) print(f"已成功保存:{file_name}") def main(): detail_links = fetch_detail_page_links() for index, detail_url in enumerate(detail_links, 1): print(f"\n正在处理第 {index}/{len(detail_links)} 个地区:{detail_url.split('Code1=')[1].split('&')[0]}") # 获取下载页面链接 download_page_url = get_download_page_url(detail_url) if not download_page_url: print(f"⚠️ 无法找到该地区的下载页面链接,跳过") continue # 获取CSV下载链接 csv_download_url = extract_csv_download_link(download_page_url) if not csv_download_url: print(f"⚠️ 无法找到该地区的CSV下载链接,跳过") continue # 生成有辨识度的文件名(用地区编码+索引) region_code = detail_url.split('Code1=')[1].split('&')[0] csv_filename = f"census_2016_{region_code}_{index}.csv" # 下载保存文件 save_csv_file(csv_download_url, csv_filename) # 添加延迟,避免请求过于频繁触发反爬机制 time.sleep(1.5) if __name__ == "__main__": main()
关键注意事项
- 选择器适配:如果某一步骤无法找到链接,需要打开浏览器开发者工具,重新定位对应元素的CSS选择器(比如调整
select或select_one的参数),网页结构可能会有微小变动。 - 反爬应对:代码中添加了浏览器请求头和请求延迟,如果还是被拦截,可以适当延长
time.sleep的时长,或者考虑使用代理IP池。 - 文件名优化:示例中用地区编码+索引命名文件,你也可以从详情页提取地区名称作为文件名,让文件更易识别(可在
fetch_detail_page_links时同步抓取地区名称)。 - 异常处理:代码包含基础的空值判断,你可以根据需求添加更多异常捕获逻辑(比如请求超时、文件写入失败等)。
内容的提问来源于stack exchange,提问作者Isaac A
相关产品推荐
相关产品推荐

