在我们日常工作和学习中,经常会遇到一个网站上有大量有价值的PDF文档,比如研究报告、产品手册或教程。如果一个一个手动去查找和下载,不仅费时费力,还容易遗漏。这个Python脚本就是专门为解决这个问题而生的。它能自动帮你遍历指定的网站,像一个小侦察兵一样把所有PDF文件的链接都找出来,然后利用多线程技术,同时开启多个下载任务,极大地提升批量下载的效率,帮你把繁琐的任务一键自动化。
下面是一个使用 Python 编写的脚本,可以从指定网站收集所有 PDF 文档链接,并通过多线程机制加速下载。
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import os
import threading
from queue import Queue
import time
import argparse
class PDFDownloader:
def __init__(self, base_url, max_threads=5, download_dir="downloads"):
self.base_url = base_url
self.max_threads = max_threads
self.download_dir = download_dir
self.pdf_links = set()
self.visited_links = set()
self.lock = threading.Lock()
self.queue = Queue()
# 创建下载目录
if not os.path.exists(download_dir):
os.makedirs(download_dir)
def is_valid_url(self, url):
"""
检查URL是否有效且属于同一域名
"""
parsed = urlparse(url)
base_parsed = urlparse(self.base_url)
return bool(parsed.netloc) and parsed.netloc == base_parsed.netloc
def get_absolute_url(self, url):
"""
将相对URL转换为绝对URL
"""
return urljoin(self.base_url, url)
def extract_pdf_links(self, url):
"""
从页面提取所有PDF链接
"""
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
# 查找所有链接
for link in soup.find_all('a', href=True):
href = link['href']
# 检查是否是PDF链接
if href.lower().endswith('.pdf'):
absolute_url = self.get_absolute_url(href)
with self.lock:
self.pdf_links.add(absolute_url)
# 如果是普通链接,添加到队列中继续爬取
else:
absolute_url = self.get_absolute_url(href)
if self.is_valid_url(absolute_url) and absolute_url not in self.visited_links:
self.queue.put(absolute_url)
self.visited_links.add(absolute_url)
except Exception as e:
print(f"Error processing {url}: {str(e)}")
def download_pdf(self, url):
"""
下载PDF文件
"""
try:
# 从URL提取文件名
filename = os.path.basename(urlparse(url).path)
if not filename:
filename = f"document_{int(time.time())}.pdf"
filepath = os.path.join(self.download_dir, filename)
# 如果文件已存在,跳过下载
if os.path.exists(filepath):
print(f"File already exists: {filename}")
return
print(f"Downloading: {filename}")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers, stream=True, timeout=30)
response.raise_for_status()
# 写入文件
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"Downloaded: {filename}")
except Exception as e:
print(f"Error downloading {url}: {str(e)}")
def crawl_worker(self):
"""
爬取工作线程函数
"""
while True:
url = self.queue.get()
if url is None:
break
self.extract_pdf_links(url)
self.queue.task_done()
def download_worker(self):
"""
下载工作线程函数
"""
while True:
url = self.queue.get()
if url is None:
break
self.download_pdf(url)
self.queue.task_done()
def find_all_pdfs(self, max_pages=50):
"""
查找网站中的所有PDF链接
"""
print("Starting to crawl for PDF links...")
# 初始URL加入队列
self.queue.put(self.base_url)
self.visited_links.add(self.base_url)
# 创建爬取线程
threads = []
for _ in range(self.max_threads):
t = threading.Thread(target=self.crawl_worker)
t.start()
threads.append(t)
# 等待队列完成或达到最大页面数
pages_processed = 0
while not self.queue.empty() and pages_processed < max_pages:
time.sleep(0.1)
pages_processed += 1
# 停止所有线程
for _ in range(self.max_threads):
self.queue.put(None)
for t in threads:
t.join()
print(f"Found {len(self.pdf_links)} PDF documents")
return self.pdf_links
def download_all_pdfs(self):
"""
下载所有找到的PDF文件
"""
print("Starting download process...")
# 将PDF链接加入队列
for pdf_url in self.pdf_links:
self.queue.put(pdf_url)
# 创建下载线程
threads = []
for _ in range(self.max_threads):
t = threading.Thread(target=self.download_worker)
t.start()
threads.append(t)
# 等待所有下载完成
self.queue.join()
# 停止所有线程
for _ in range(self.max_threads):
self.queue.put(None)
for t in threads:
t.join()
print("All downloads completed!")
def main():
parser = argparse.ArgumentParser(description='PDF Downloader from Website')
parser.add_argument('url', help='Base URL to start crawling from')
parser.add_argument('-t', '--threads', type=int, default=5, help='Number of threads (default: 5)')
parser.add_argument('-d', '--directory', default='downloads', help='Download directory (default: downloads)')
parser.add_argument('-m', '--max-pages', type=int, default=50, help='Maximum pages to crawl (default: 50)')
args = parser.parse_args()
# 创建下载器实例
downloader = PDFDownloader(
base_url=args.url,
max_threads=args.threads,
download_dir=args.directory
)
# 查找所有PDF链接
pdf_links = downloader.find_all_pdfs(max_pages=args.max_pages)
if pdf_links:
# 下载所有PDF文件
downloader.download_all_pdfs()
else:
print("No PDF documents found on the website.")
if __name__ == "__main__":
main()
1、安装必要的依赖库:
pip install requests beautifulsoup4
2、运行脚本:
python pdf_downloader.py https://example.com -t 10 -d my_pdfs -m 100
参数说明:
url
: 要爬取的网站URL(必需)-t
, --threads
: 线程数量(默认:5)-d
, --directory
: 下载目录(默认:downloads)-m
, --max-pages
: 最大爬取页面数(默认:50)多线程爬取:使用多线程同时爬取多个页面,提高效率
多线程下载:使用多线程同时下载多个PDF文件,加速下载过程
相对URL处理:自动将相对URL转换为绝对URL
域名限制:只爬取同一域名下的链接
重复检测:避免重复下载同一文件
错误处理:完善的异常处理机制
进度显示:显示下载进度和状态
1、请确保遵守目标网站的robots.txt规则
2、尊重网站服务器负载,适当调整线程数量
3、仅用于合法和道德的目的
4、某些网站可能有反爬虫机制,可能需要额外处理
这个脚本提供了基本功能,您可以根据需要进一步扩展和优化。
使用这个脚本非常简单,你只需要在命令行里指定要抓取的网址,它就会自动开始工作。所有下载好的PDF文件都会整齐地保存在你指定的文件夹中。不过需要注意的是,请务必尊重网站的规定,不要过度频繁请求而给对方服务器造成压力。合理设置线程数量,并确保你的使用方式是合法合规的。希望这个工具能成为你的得力助手,帮你从重复劳动中解放出来,高效地获取所需的资料。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。