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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 10:35:09