先用soup.find_all('a', href=True)提取所有带href的链接,再用urljoin补全相对路径,通过endswith('.pdf')或正则匹配过滤,最后发HEAD请求校验Content-Type是否为application/pdf。

怎么用 requests 和 BeautifulSoup 找到页面里的所有PDF链接
网页中PDF通常以 <a> 标签指向 .pdf 文件,也可能藏在 <iframe> 或 JavaScript 生成的 URL 里。最稳妥的做法是先提取所有 href 属性,再过滤出以 .pdf(或 application/pdf)结尾的链接。
注意:有些链接是相对路径(如 /files/report.pdf),需用 urllib.parse.urljoin(base_url, href) 拼成完整 URL;有些带查询参数(如 ?v=2),不影响下载,但建议保留原样。
- 别只靠文件后缀判断——部分 PDF 链接没有
.pdf后缀(比如通过download.php?id=123返回 PDF),这时得发 HEAD 请求检查Content-Type是否为application/pdf - 避免重复下载同一份 PDF:对 URL 做
urllib.parse.urlparse()归一化(去掉末尾斜杠、统一大小写等)再去重 - 跳过 mailto:、tel:、javascript: 等伪协议链接,否则
requests.get()会报错
怎么用 requests.get() 安全下载PDF并保存到本地
PDF 是二进制内容,必须用 response.content(不是 text)写入文件,并以 open(..., 'wb') 模式打开。
常见错误是没设超时或没处理 4xx/5xx 响应码,导致程序卡死或静默失败。建议显式设置 timeout=10,并检查 response.status_code == 200。
立即学习“Python免费学习笔记(深入)”;
- 文件名要从 URL 或
Content-Disposition头里提取:优先看响应头response.headers.get('Content-Disposition'),其次用urlparse(url).path.split('/')[-1] - 如果提取的文件名为空或含非法字符(
/ \ : * ? " < > |),用re.sub(r'[^\w.-]', '_', name)替换掉,再补上.pdf - 下载前先
os.makedirs(save_dir, exist_ok=True),避免因目录不存在报错
怎么批量处理多个页面且避免被封IP
连续高频请求容易触发反爬,尤其目标站用了 Cloudflare 或 Nginx 限流。不能靠简单 time.sleep(1) 应付——真实场景中,有些网站要求随机间隔 + User-Agent 轮换 + 会话复用。
- 用同一个
requests.Session()实例复用连接,比反复新建requests.get()快且更像真人行为 - 每次请求前设置不同
User-Agent:从列表里随机选,例如['Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'] - 间隔时间别固定:用
random.uniform(1.5, 4.0)代替time.sleep(2),降低被识别为脚本的概率 - 遇到 429 或 503 响应,暂停 10–30 秒再重试,而不是立刻循环
怎么验证下载的PDF是否有效
下载完成不等于文件可用——网络中断可能产生截断文件,服务器返回 HTML 错误页却被当成 PDF 保存下来。
最轻量的验证方式是检查文件开头是否为 %PDF-(PDF 文件签名),用 with open(path, 'rb') as f: f.read(4) == b'%PDF' 即可快速筛掉明显坏文件。
- 别依赖文件扩展名:有人把 HTML 页面另存为
report.pdf,光看后缀没用 - 完整校验可用
pypdf.PdfReader(path)尝试加载,捕获PyPDFError异常,但开销较大,适合事后抽检 - 记录失败 URL 和错误原因(如
ConnectionError、status_code=404、invalid_pdf_signature)到日志文件,方便后续排查
真正麻烦的是 JavaScript 渲染的 PDF 链接——BeautifulSoup 解析不到,得换 playwright 或 selenium。那已经不是“批量抓取”的范畴,而是自动化浏览器操作了。



















