如何用Python下载CDN站点INDOT内的Contract Information Book文件?
解决方案:基于搜索结果批量下载目标文档
要高效下载所有"Contract Information Book"文件,核心是先通过模拟网站搜索获取目标文档的ID列表,再批量下载。以下是具体步骤和代码实现:
步骤1:分析网站搜索机制
打开浏览器开发者工具(F12)执行搜索操作,观察到该站点是ASP.NET架构:
- 搜索提交的是POST请求到
https://erms.indot.in.gov/viewdocs/Default.aspx - 请求必须携带ASP.NET表单的隐藏参数(
__VIEWSTATE、__VIEWSTATEGENERATOR、__EVENTVALIDATION),这些参数需要从初始页面获取 - 搜索关键词、文档类型等筛选条件会包含在POST表单数据中
步骤2:实现搜索与ID提取
使用requests.Session维持会话,获取初始页面的表单参数,构造搜索请求并解析结果页面提取所有目标文档的ID:
import os import time import requests from bs4 import BeautifulSoup # 初始化会话与文件夹 session = requests.Session() base_url = "https://erms.indot.in.gov/viewdocs/Default.aspx" download_folder = "INDOT Contracts" os.makedirs(download_folder, exist_ok=True) # 获取初始页面的ASP.NET表单隐藏参数 response = session.get(base_url) soup = BeautifulSoup(response.text, "html.parser") viewstate = soup.find("input", {"id": "__VIEWSTATE"})["value"] viewstate_generator = soup.find("input", {"id": "__VIEWSTATEGENERATOR"})["value"] event_validation = soup.find("input", {"id": "__EVENTVALIDATION"})["value"] # 构造搜索POST数据(字段需对应网站实际表单,可从开发者工具查看) search_data = { "__VIEWSTATE": viewstate, "__VIEWSTATEGENERATOR": viewstate_generator, "__EVENTVALIDATION": event_validation, "txtSearch": "Contract Information Book", "btnSearch": "Search" } # 发送搜索请求并解析第一页结果 response = session.post(base_url, data=search_data) soup = BeautifulSoup(response.text, "html.parser") document_ids = [] # 提取当前页的文档ID for link in soup.find_all("a", href=True): if "View12.aspx?Id=" in link["href"]: doc_id = link["href"].split("Id=")[-1] document_ids.append(doc_id) # 处理分页,遍历所有结果页 while True: next_page_btn = soup.find("a", text="Next") if not next_page_btn: break # 解析分页请求参数 event_target = next_page_btn["href"].split("__doPostBack('")[1].split("','")[0] event_argument = next_page_btn["href"].split("','")[1].split("')")[0] # 更新表单参数并发送分页请求 viewstate = soup.find("input", {"id": "__VIEWSTATE"})["value"] event_validation = soup.find("input", {"id": "__EVENTVALIDATION"})["value"] pagination_data = { "__VIEWSTATE": viewstate, "__VIEWSTATEGENERATOR": viewstate_generator, "__EVENTVALIDATION": event_validation, "__EVENTTARGET": event_target, "__EVENTARGUMENT": event_argument } response = session.post(base_url, data=pagination_data) soup = BeautifulSoup(response.text, "html.parser") # 提取当前页文档ID for link in soup.find_all("a", href=True): if "View12.aspx?Id=" in link["href"]: doc_id = link["href"].split("Id=")[-1] document_ids.append(doc_id) time.sleep(1) # 避免请求过于频繁触发限制
步骤3:批量下载PDF
拿到所有目标ID后,遍历下载,优先从响应头获取文档原文件名:
# 遍历ID下载文档 for doc_id in document_ids: download_url = f"https://erms.indot.in.gov/viewdocs/View12.aspx?Id={doc_id}" response = session.get(download_url) # 从响应头提取文件名 filename = None if "Content-Disposition" in response.headers: cd = response.headers["Content-Disposition"] filename = cd.split("filename=")[-1].strip('"') # 无原文件名则用ID命名 if not filename: filename = f"contract_book_{doc_id}.pdf" file_path = os.path.join(download_folder, filename) with open(file_path, "wb") as f: f.write(response.content) print(f"已完成:{filename}") time.sleep(0.5)
注意事项
- 网站表单字段可能随更新变化,需根据开发者工具的实际请求参数调整
search_data和分页参数 - 保持适当延时,避免触发反爬机制
- 若后续站点新增登录验证,需补充模拟登录的会话流程
内容的提问来源于stack exchange,提问作者Chris Gat
相关产品推荐
相关产品推荐

