Python 多线程爬虫停止条件如何设置?
多线程爬虫,如何在特定的深度让线程停止呢?
每个线程目前爬取页面后,还会从页面中抓取新的 url 入队
Python 多线程爬虫停止条件如何设置?
8 回复
可以每次开新的线程的时候传入一个变量并+1,记录当前的深度,达到一个阈值就不要再开新的线程。
核心思路: 用队列控制任务流,主线程监控队列空 + 所有工作线程空闲作为停止信号。
具体实现:
- 用
queue.Queue存放待爬取的 URL。 - 工作线程从队列取 URL 执行,新发现的 URL 再放回队列。
- 主线程通过
queue.join()等待所有任务完成,并设置线程为守护线程或发送停止信号。
代码示例:
import threading
import queue
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup
class ThreadedCrawler:
def __init__(self, start_url, max_threads=5):
self.start_url = start_url
self.max_threads = max_threads
self.base_url = '/'.join(start_url.split('/')[:3])
self.visited = set()
self.url_queue = queue.Queue()
self.url_queue.put(start_url)
self.workers = []
self.lock = threading.Lock()
def crawl(self):
# 启动工作线程
for _ in range(self.max_threads):
t = threading.Thread(target=self.worker)
t.daemon = True # 设为守护线程,主线程结束即退出
t.start()
self.workers.append(t)
# 等待队列清空
self.url_queue.join()
print("所有任务完成,爬虫停止")
def worker(self):
while True:
try:
url = self.url_queue.get(timeout=3) # 设置超时避免永久阻塞
if url not in self.visited:
self.process_url(url)
self.url_queue.task_done()
except queue.Empty:
break # 队列空且超时,线程退出
def process_url(self, url):
with self.lock:
if url in self.visited:
return
self.visited.add(url)
try:
print(f"爬取: {url}")
resp = requests.get(url, timeout=5)
soup = BeautifulSoup(resp.text, 'html.parser')
# 提取新链接(示例)
for link in soup.find_all('a', href=True):
new_url = urljoin(self.base_url, link['href'])
if new_url.startswith(self.base_url) and new_url not in self.visited:
self.url_queue.put(new_url)
except Exception as e:
print(f"处理 {url} 时出错: {e}")
if __name__ == "__main__":
crawler = ThreadedCrawler("https://example.com", max_threads=3)
crawler.crawl()
关键点说明:
queue.join()会阻塞直到队列中所有任务被标记为task_done()。- 工作线程设为守护线程(
daemon=True)后,主线程结束时它们会自动退出。 - 线程中的
queue.get(timeout=3)设置超时,避免队列空时线程永久阻塞。
一句话总结: 用队列管理任务,靠 queue.join() 和守护线程实现自然停止。
如果我抓 2 层,那就是抓主页里的所有 url,然后这些 url 页面里的 url 再取出来抓下来,这个过程中 url 不断入队,我如何判断在什么地方停止呢?
downloder 接受 url 参数时顺便传入这个 url 的深度啊,downloader 吐 response 的时候把这个参数吐出来再
谢谢~刚想到了您的这种解决方案。python 里如果想效率高一些,url 去重用 url 做 md5 hash 放到 set 里; url 存储用什么方式呢?
谢谢
维护一个 tasks 队列,tasks 队列存 task 类,深度,url,解析规则存在 task 里,
再维护一个线程池,线程池只从 tasks 中取 task,执行 task。
task 是一个线程池,深度和 url 存在 task 里是什么意思

