如何从实例化DownloadHandler的Crawler类持续获取更新后的new_files值?
我开发了一个带GUI的Selenium网络爬虫Crawler,GUI会显示当前URL、爬虫深度和已下载文件数量。原本用requests下载文件时,完成后会直接让计数+1,一切正常。
最近新增了处理需要JS交互才能下载的文件的功能:用Watchdog监控下载目录,通过DownloadHandler类记录新增文件。现在文件监控功能正常,但GUI里的“已下载”计数始终无法更新——从Crawler类访问dl_handler.new_files时,数值一直是0。
精简代码示例
DownloadHandler初始版本
class DownloadHandler: def __init__(self): self.new_files = 0 def handle_download(self): do_stuff() self.new_files += 1
Crawler类调用方式
class Crawler: def __init__(self): self.dl_handler = DownloadHandler()
问题表现
>>>print(self.dl_handler.new_files) >>>output: 0
完整Watchdog相关代码
class DownloadHandler(Observer): def __init__(self, temp_dir): self.temp_dir = 'PATH' self.new_files = 0 def handle_download(self): do_stuff() self.new_files += 1 def monitor_directory(self): event_handler = MyHandler(self.temp_dir) observer = Observer() observer.schedule(event_handler, path=self.temp_dir.name, recursive=False) observer.start() class MyHandler(DownloadHandler, FileSystemEventHandler): def __init__(temp_dir): self.temp_dir = temp_dir def on_created(self, event): if not re.search(r'.*crdownload$|\.com\.google\.Chrome.*', event.src_path) and event.src_path != self.temp_dir.name: try: self.handle_download() except FileNotFoundError as e: pass
问题根源
核心错误是对象实例不共享:MyHandler是DownloadHandler的子类,在monitor_directory里新建的MyHandler实例,和Crawler持有的dl_handler不是同一个对象。Watchdog触发事件时,调用的是MyHandler实例的handle_download,累加的是该实例的new_files,而Crawler读取的是自身持有的DownloadHandler实例的new_files,自然一直为0。
另外还有两个小问题:
MyHandler的__init__方法缺少self参数,且未调用父类构造函数,会导致属性初始化异常DownloadHandler继承Observer后又在方法内新建Observer实例,设计冗余
修复方案
方案1:复用同一个DownloadHandler实例(推荐)
不让MyHandler继承DownloadHandler,而是将Crawler持有的DownloadHandler实例传给MyHandler,确保事件触发时操作的是同一个对象:
from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import re class DownloadHandler: def __init__(self, temp_dir): self.temp_dir = temp_dir self.new_files = 0 self.observer = Observer() def handle_download(self): # 这里编写文件处理逻辑 self.new_files += 1 # 若需更新GUI,可在此触发回调 print(f"已新增文件,当前计数:{self.new_files}") def start_monitoring(self): event_handler = MyHandler(self) self.observer.schedule(event_handler, path=self.temp_dir.name, recursive=False) self.observer.start() def stop_monitoring(self): self.observer.stop() self.observer.join() class MyHandler(FileSystemEventHandler): def __init__(self, download_handler): self.download_handler = download_handler def on_created(self, event): if not event.is_directory and not re.search(r'.*crdownload$|\.com\.google\.Chrome.*', event.src_path): try: self.download_handler.handle_download() except FileNotFoundError as e: pass # Crawler调用示例 class Crawler: def __init__(self, temp_dir): self.dl_handler = DownloadHandler(temp_dir) self.dl_handler.start_monitoring() def check_download_count(self): print(f"当前已下载文件数:{self.dl_handler.new_files}")
方案2:使用类属性共享计数(适合简单场景)
将new_files设为类属性,让所有实例共享同一个值:
class DownloadHandler(Observer): new_files = 0 # 类属性,所有实例共享 def __init__(self, temp_dir): super().__init__() self.temp_dir = temp_dir def handle_download(self): do_stuff() DownloadHandler.new_files += 1 # 访问时直接调用类属性 >>>print(DownloadHandler.new_files)
额外:GUI实时更新建议
在DownloadHandler中加入GUI更新回调,每次计数变化时直接触发界面更新:
class DownloadHandler: def __init__(self, temp_dir, update_gui_callback): self.temp_dir = temp_dir self.new_files = 0 self.observer = Observer() self.update_gui = update_gui_callback def handle_download(self): self.new_files += 1 self.update_gui(self.new_files) # 触发GUI更新
在Crawler初始化时传入GUI的更新方法,即可实现计数变化时实时同步界面。
内容的提问来源于stack exchange,提问作者frumsky

