首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python实现多线程PDF抓取与下载脚本

Python实现多线程PDF抓取与下载脚本

原创
作者头像
华科云商小徐
发布2025-09-02 11:23:47
发布2025-09-02 11:23:47
10800
代码可运行
举报
文章被收录于专栏:小徐学爬虫小徐学爬虫
运行总次数:0
代码可运行

在我们日常工作和学习中,经常会遇到一个网站上有大量有价值的PDF文档,比如研究报告、产品手册或教程。如果一个一个手动去查找和下载,不仅费时费力,还容易遗漏。这个Python脚本就是专门为解决这个问题而生的。它能自动帮你遍历指定的网站,像一个小侦察兵一样把所有PDF文件的链接都找出来,然后利用多线程技术,同时开启多个下载任务,极大地提升批量下载的效率,帮你把繁琐的任务一键自动化。

下面是一个使用 Python 编写的脚本,可以从指定网站收集所有 PDF 文档链接,并通过多线程机制加速下载。

代码语言:javascript
代码运行次数:0
运行
复制
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、安装必要的依赖库:

代码语言:javascript
代码运行次数:0
运行
复制
pip install requests beautifulsoup4

2、运行脚本:

代码语言:javascript
代码运行次数:0
运行
复制
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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用说明
  • 功能特点
  • 注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档