PyQt5中暂停while循环、执行文件写入后恢复的实现问题
解决你的PyQt5 GUI爬虫问题
首先,你遇到的TypeError: argument 1 has unexpected type 'NoneType'错误,根源在于按钮连接时直接调用了函数:
self.save.clicked.connect(self.writer(self.links))
connect()需要接收一个可调用的函数对象,但你这里直接执行了self.writer(self.links),它的返回值是None(因为你的writer函数没有return),所以才会报错。正确的做法是用lambda来包装带参数的函数调用:
self.save.clicked.connect(lambda: self.writer(self.links))
不过这只是表面问题,更大的隐患是:你的爬虫while循环会直接阻塞GUI主线程,导致整个界面卡死,连保存按钮都点不动。所以必须把爬虫逻辑放到子线程中运行,同时用线程安全的方式处理共享数据和暂停逻辑。
下面是完整的修正代码,包含所有必要的改进:
from PyQt5 import QtCore, QtGui, QtWidgets from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By import time # 爬虫子线程类 class SpiderThread(QtCore.QThread): def __init__(self, parent=None): super().__init__(parent) self.links = [] self.is_running = True self.is_paused = False self.mutex = QtCore.QMutex() # 线程锁,保护共享数据 def run(self): # 初始化浏览器(建议用无头模式,避免弹窗) options = webdriver.ChromeOptions() options.add_argument('--headless=new') driver = webdriver.Chrome(options=options) try: while self.is_running: # 检查是否需要暂停 self.mutex.lock() paused = self.is_paused self.mutex.unlock() if paused: time.sleep(0.1) # 暂停时休眠,减少CPU占用 continue # 这里替换成你的爬虫逻辑 driver.get("https://example.com") # 等待元素加载,示例获取所有链接 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, "a")) ) links = driver.find_elements(By.TAG_NAME, "a") for link in links: href = link.get_attribute("href") if href: # 线程安全地添加到列表 self.mutex.lock() self.links.append(href) self.mutex.unlock() time.sleep(2) # 爬取间隔,避免被封 finally: driver.quit() def pause(self): # 设置暂停标志 self.mutex.lock() self.is_paused = True self.mutex.unlock() def resume(self): # 恢复运行 self.mutex.lock() self.is_paused = False self.mutex.unlock() def get_links(self): # 线程安全地获取当前链接列表 self.mutex.lock() links_copy = self.links.copy() self.mutex.unlock() return links_copy class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() self.spider_thread = SpiderThread() def init_ui(self): self.setWindowTitle("爬虫GUI") self.setFixedSize(300, 150) central_widget = QtWidgets.QWidget() self.setCentralWidget(central_widget) layout = QtWidgets.QVBoxLayout(central_widget) # 开始爬虫按钮 self.start_btn = QtWidgets.QPushButton("开始爬取") self.start_btn.clicked.connect(self.start_spider) layout.addWidget(self.start_btn) # 保存按钮 self.save_btn = QtWidgets.QPushButton("保存到文件") self.save_btn.clicked.connect(self.save_links) self.save_btn.setEnabled(False) # 初始不可用 layout.addWidget(self.save_btn) def start_spider(self): self.spider_thread.start() self.start_btn.setEnabled(False) self.save_btn.setEnabled(True) def save_links(self): # 暂停爬虫 self.spider_thread.pause() # 获取线程安全的链接副本 links = self.spider_thread.get_links() # 写入文件 with open("links.txt", "w", encoding="utf-8") as f: for link in links: f.write(link + "\n") print("文件保存完成!") # 恢复爬虫 self.spider_thread.resume() def closeEvent(self, event): # 关闭窗口时停止线程 self.spider_thread.is_running = False self.spider_thread.wait() event.accept() if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
关键改进点说明:
- 子线程运行爬虫:把爬虫逻辑放到
SpiderThread中,避免阻塞GUI主线程,保证界面始终响应。 - 线程安全的数据处理:用
QMutex保护共享的links列表和状态标志,防止多线程同时读写导致的数据混乱。 - 暂停/恢复机制:通过
is_paused标志控制爬虫循环,保存文件时先暂停爬虫,写入完成后再恢复,确保写入的是完整、一致的数据。 - 修正按钮连接:用lambda包装函数调用,同时初始时禁用保存按钮,只有爬虫启动后才可用,避免无效操作。
另外,建议在爬虫中添加异常处理,避免单个页面爬取失败导致整个线程崩溃;如果需要实时显示爬取进度,可以通过Qt的信号(pyqtSignal)在子线程和主线程之间传递数据。
内容的提问来源于stack exchange,提问作者frorekable
相关产品推荐
相关产品推荐

