网络爬虫学习:借助DeepSeek完善爬虫软件,增加停止任务功能
一、引言
我从24年11月份开始学习网络爬虫应用开发,经过2个来月的努力,终于完成了开发一款网络爬虫软件的学习目标。这几天对本次学习及应用开发进行一下回顾总结。前面已经发布了两篇日志:
网络爬虫学习:应用selenium从搜*狐搜索爬取新闻结果的数据
网络爬虫学习:应用selenium获取Edge浏览器版本号,自动下载对应版本msedgedriver,确保Edge浏览器顺利打开
这是第三篇日志,记录如何为软件增加停止任务功能。
二、问题描述
软件初步开发完成后,我打包成exe文件提供给小伙伴试用,有小伙伴问我,软件怎么没有停止任务的功能?如果点击开始按钮后,又不想爬取了,想换个关键字再爬,但没有停止功能,就只能等着软件完成当前的所有任务或者关闭软件,这样不合理,建议我增加一个停止功能。
这个需求是合情合理的,但奈何我的能力有限,对爬虫知识学得不够深入,关于怎么停止当前正在执行的任务还真不会。但小伙伴提出的改进意见,那么我就要努力去完善,这样也是对自己能力的提升。
三、借助DeepSeek完善软件
在前一篇日志里,我记录了通过向DeepSeek提问,获得了自动下载对应版本msedgedriver的方法,让我的爬虫软件可以在小伙伴的电脑上正常运行。这次我决定继续向DeepSeek提问,获得实现停止爬虫任务的方法。
我一共向DeepSeek提了3个问题,最终获得了我想要的答案。
第1问:“我使用python语言开发爬虫软件,用到了threading.Thread和concurrent.futures,使爬虫在多线程下运行。我现在遇到了一个问题,当开始执行爬虫任务后,我该如何终止爬虫任务?”
DeepSeek思考了22秒给了我答案,答案里有两个方法。这两个方法都是通过捕获 KeyboardInterrupt
(Ctrl+C)或注册信号处理器,来停止所有线程。我在pycharm上尝试运行了这两个方法中的代码,发现都需要通过按下Ctrl+F2来停止爬虫任务,而我是希望在GUI上添加一个停止按钮,点击按钮后,停止爬虫任务,显然DeepSeek提供的答案并没有达到我的目的。
第1问的答案不符合我的需求,应该是我的问题描述还不够详细,我继续提问。
第2问:“上面给出的答案需要按Ctrl+C才能终止多线程爬虫任务,而我要开发的是带GUI的爬虫软件,该软件使用到了wxpython,我希望在GUI中设置一个停止按钮,当用户点击这个按钮后,能够停止多线程爬虫任务。”
这一次,DeepSeek给了我一个基本符合要求的答案,不过DeepSeek提供的Python代码直接运行会报错,需要对错误的地方做一下修改。在pycharm上实现了点击停止按钮后中断爬虫任务的目的。
(DeepSeek的解答)
(DeepSeek提供的代码运行会报错,需要修改)
(在pycharm上运行代码,可以实现停止爬虫任务)
不过我在对上述代码进行深入研究,并尝试按照我的编程风格和实际需求进行修改后发现了其中的不足。由此我提出了第3问。
第3问:“上面的答案有效,不过有一个不足,就是最终实现代码中的CrawlerTask方法里模拟爬取操作代码做得很简单,且又是在while循环中,导致如果不点击中断会无限循环下去。而实际上,爬虫任务基本是有限次数的,任务完成后会通过break退出while循环。我向知道的是在爬虫任务完成后通过break退出while循环后,如何将self.start_btn设置为Enable,将self.stop_btn设置为Disable”
我的这一问虽然把需求都提出来了,但是语句不够精炼,并且还要错别字。DeepSeek的回答真是让人惊艳,它用更精炼的文字,将我的问题准确的描述了出来。
好的,我现在需要解决用户的问题。用户之前已经得到了如何在GUI中通过停止按钮终止多线程爬虫的解决方案,但现在他指出了代码中的一个不足:CrawlerTask中的模拟爬取操作过于简单,且使用了无限循环,而实际爬虫任务应该是有限次数的,完成后自动停止。用户想知道当任务自然完成时,如何正确更新按钮状态(启用开始按钮,禁用停止按钮)。
DeepSeek给出了详细的分析和具体的实现步骤,真的很赞。
(第2问的代码需要修改的地方)
(DeepSeek优化后的代码,依然有bug,需要纠错后才能运行)
(关于优化代码的关键改进说明)
(两种典型场景验证)
我将DeepSeek提供的优化后的代码赋值到pycharm中,修改掉其中的bug,运行代码,让爬虫任务运行完成,完成后软件会自动将开始按钮启用,将停止按钮禁用。
另外,DeepSeek还给出了扩展建议,我对这部分内容也进行了吸收融合。
四、功能实现
我对DeepSeek给出的答案进行了研究和融合,最终得到了具有以下功能的示例:
1.软件的GUI使用wxpython(我基本使用wxpython来制作GUI);
2.使用 threading.Event() 作为全局停止标志,所有爬虫线程定期检查 stop_event.is_set();
3.使用wx.CallAfter实现跨线程安全更新 GUI
4.软件使用的线程池ThreadPoolExecutor,并且界面中添加了滑动条控制线程池的 max_workers
参数实现动态线程数调整,默认值为3,可以在1-6之间调整;
5.示例模拟爬取6个url,每个url生成一个任务占用一个线程,每个任务随机爬取数量不同的页数,通过异常重试机制让每一页的数据均有3次的重试机会;
6.为每个future添加回调函数,触发状态检查;
7.在CheckThreadsStatus方法中,当所有future完成时,更新按钮状态;
8.支持任务进度显示,添加计数器统计已完成任务;
9.使用Queue线程安全队列收集结果,并能在任务结束后,查看结果。
10.按钮状态逻辑:
初始状态下:"开始爬取"按钮启用,“停止爬取”和“查看结果”按钮停用;
执行爬虫任务状态下:"停止爬取"按钮启用,“开始爬取”和“查看结果”按钮停用;
任务完成或停止状态下:"开始爬取"和“查看结果”按钮按钮启用,“停止爬取”停用;
有了这个模版,就可以参照它对我的爬虫软件进行改进,从而实现增加停止爬虫任务的功能了。
(软件初始状态)
(正常执行完所有爬虫任务)
(强制停止爬虫任务)
(查看爬取的结果)
五、代码展示
最后放上实现上述功能的示例代码供参考,可以直接运行。
import wx
import threading
from concurrent.futures import ThreadPoolExecutor
from queue import Queue
import time
import randomclass CrawlerGUI(wx.Frame):def __init__(self):super().__init__(parent=None, title="多线程爬虫工具", size=wx.Size(480, 560))self.stop_event = threading.Event() # 全局停止标志self.executor = None # 线程池self.futures = [] # 记录所有 Future 对象self.max_workers = 3 # 同时执行线程数self.result_queue = Queue() # 线程安全队列,用于传递爬取的结果self.completed_tasks = 0 # 任务完成数统计# 初始化 UIself.InitUI()self.Centre()self.Show()def InitUI(self):panel = wx.Panel(self)vbox = wx.BoxSizer(wx.VERTICAL)# 控制按钮区域self.start_btn = wx.Button(panel, label="开始爬取")self.stop_btn = wx.Button(panel, label="停止爬取")self.thread_slider = wx.Slider(panel, value=3, minValue=1, maxValue=6, style=wx.SL_HORIZONTAL | wx.SL_LABELS)self.view_btn = wx.Button(panel, label="查看结果")self.log_text = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY)self.stop_btn.Enable(False) # 初始状态为不可点击self.view_btn.Enable(False) # 初始状态为不可点击# 按钮事件绑定self.start_btn.Bind(wx.EVT_BUTTON, self.OnStart)self.stop_btn.Bind(wx.EVT_BUTTON, self.OnStop)self.thread_slider.Bind(wx.EVT_SLIDER, self.OnThreadChange)self.view_btn.Bind(wx.EVT_BUTTON, self.OnView)self.Bind(wx.EVT_CLOSE, self.on_close) # 绑定关闭事件处理器# 布局hbox = wx.BoxSizer(wx.HORIZONTAL)hbox.Add(self.start_btn, 0, wx.ALL, 5)hbox.Add(self.stop_btn, 0, wx.ALL, 5)hbox.Add(self.view_btn, 0, wx.ALL, 5)hbox.Add(self.thread_slider, 0, wx.ALL, 5)vbox.Add(hbox, 0, wx.EXPAND)vbox.Add(self.log_text, 1, wx.EXPAND | wx.ALL, 5)panel.SetSizer(vbox)def OnStart(self, event):"""开始爬取按钮事件"""event.Skip()self.start_btn.Disable()self.stop_btn.Enable()self.view_btn.Disable()self.log_text.SetLabel("") # 清空self.log_text.AppendText("爬虫已启动...\n")self.completed_tasks = 0 # 任务完成数重置# 重置停止标志self.stop_event.clear()# 提交任务(示例URL列表)urls = [f"https://example.com/{i}" for i in range(1, 7)] # 示例6个URL# 初始化线程池(默认使用3个线程)self.executor = ThreadPoolExecutor(max_workers=self.max_workers)# 提交任务并绑定回调self.futures = []for url in urls:future = self.executor.submit(self.CrawlerTask, url)future.add_done_callback(self.OnTaskDone) # 关键:添加完成回调self.futures.append(future)def OnTaskDone(self, future):"""单个任务完成时的回调"""wx.CallAfter(self.CheckThreadsStatus) # 安全触发状态检查self.completed_tasks += 1wx.CallAfter(self.UpdateProgress)def CrawlerTask(self, url):"""处理单个URL的爬取任务"""if self.stop_event.is_set():returntry:# 模拟有限次数的爬取操作(实际替换为真实抓取逻辑)pages = random.randint(4, 10) # 生成随机数,让每次抓取的页数不同for page in range(1, pages): # 假设每个URL需要抓取多个页面(页数小于pages)if self.stop_event.is_set():breakretries = 3while retries > 0 and not self.stop_event.is_set():try:# 模拟抓取过程time.sleep(0.5)# 在CrawlerTask中保存结果data = f'{url} page{page} data' # 模拟爬取到的数据self.result_queue.put(data)msg = f"抓取 {url} 第{page}页完成\n"wx.CallAfter(self.log_text.AppendText, msg)# 此处添加实际抓取代码:# response = requests.get(url, timeout=5)# ...breakexcept Exception:retries -= 1time.sleep(1)# 任务自然完成时提示if not self.stop_event.is_set():wx.CallAfter(self.log_text.AppendText, f"{url} 任务完成!\n")except Exception as e:wx.CallAfter(self.log_text.AppendText, f"错误: {str(e)}\n")def CheckThreadsStatus(self):"""检查线程状态并更新UI"""if all(future.done() for future in self.futures):self.start_btn.Enable()self.stop_btn.Disable()self.view_btn.Enable()self.log_text.AppendText("所有任务已完成!\n")self.executor.shutdown() # 关闭线程池def UpdateProgress(self):"""更新任务完成进度"""progress = f"完成进度: {self.completed_tasks}/{len(self.futures)}"self.log_text.AppendText(progress + "\n")def OnStop(self, event):"""停止爬取按钮事件"""event.Skip()self.stop_btn.Disable()self.log_text.AppendText("正在停止爬虫...\n")self.stop_event.set()# 取消未开始的任务for future in self.futures:future.cancel()# 强制关闭线程池(如果使用Python 3.9+)if self.executor:self.executor.shutdown(wait=False)wx.CallAfter(self.CheckThreadsStatus)def OnThreadChange(self, event):"""变更线程数"""event.Skip()self.max_workers = self.thread_slider.GetValue()def OnView(self, event):"""查看结果"""event.Skip()wx.CallAfter(self.ProcessResults)def ProcessResults(self):"""处理结果"""self.log_text.SetLabel("") # 清空while not self.result_queue.empty():data = self.result_queue.get()# 更新GUI或保存到文件self.log_text.AppendText(f"{data}\n")if __name__ == "__main__":app = wx.App()CrawlerGUI()app.MainLoop()
相关文章:

网络爬虫学习:借助DeepSeek完善爬虫软件,增加停止任务功能
一、引言 我从24年11月份开始学习网络爬虫应用开发,经过2个来月的努力,终于完成了开发一款网络爬虫软件的学习目标。这几天对本次学习及应用开发进行一下回顾总结。前面已经发布了两篇日志: 网络爬虫学习:应用selenium从搜*狐搜…...
docker安装es及分词器ik
系统是macos,docker是docker-desktop 拉取镜像 docker pull bitnami/elasticsearch 启动docker镜像 docker create -e "discovery.typesingle-node" \ --name elasticsearch1 -p 9200:9200 -p 9300:9300 \ bitnami/elasticsearch:8.17.1 测试是否好…...

【论文阅读】On the Security of “VOSA“
On the Security of Verifiable and Oblivious Secure Aggregation for Privacy-Preserving Federated Learning -- 关于隐私保护联邦中可验证与遗忘的安全聚合的安全性 论文来源摘要Introduction回顾 VOSA 方案对VOSA不可伪造性的攻击对于类型 I 的攻击对于类型 II 的攻击 论文…...
Docker 国内最新可用镜像源20250205
2年没用dockerhub了结果今天发现镜像无法拉取了,找了很多镜像都无效,连阿里云镜像都不行了,最后找到下面可以用的。 Docker镜像仓库备注hub.urlsa.us.kg可用http://hub.haod.eu.org可用http://hub.chxza.eu.org可用http://ccoc.eu.org部分地…...

(2025|ICLR,音频 LLM,蒸馏/ALLD,跨模态学习,语音质量评估,MOS)音频 LLM 可作为描述性语音质量评估器
Audio Large Language Models Can Be Descriptive Speech Quality Evaluators 目录 1. 概述 2. 研究背景与动机 3. 方法 3.1 语音质量评估数据集 3.2 ALLD 对齐策略 4. 实验结果分析 4.1 MOS 评分预测(数值评估) 4.2 迁移能力(在不同…...

使用 CSS 实现透明效果
在 CSS 中,实现透明效果有几种方法,具体使用哪种方法取决于具体需求。以下是一些常见的方法: 使用 opacity 属性: opacity 属性可以设置整个元素的透明度,包括其所有的子元素。 .transparent { opacity: 0.5; /* 0 表…...

4G核心网的演变与创新:从传统到虚拟化的跨越
4G核心网 随着移动通信技术的不断发展,4G核心网已经经历了从传统的硬件密集型架构到现代化、虚拟化网络架构的重大转型。这一演变不仅提升了网络的灵活性和可扩展性,也为未来的5G、物联网(LOT)和边缘计算等技术的发展奠定了基础。…...

数据库系统概论的第六版与第五版的区别,附pdf
我用夸克网盘分享了「数据库系统概论第五六版资源」,点击链接即可保存。 链接:https://pan.quark.cn/s/21a278378dee 第6版教材修订的主要内容 为了保持科学性、先进性和实用性,在第5版教材基础上对全书内容进行了修改、更新和充实。 在科…...

uniapp小程序自定义中间凸起样式底部tabbar
我自己写的自定义的tabbar效果图 废话少说咱们直接上代码,一步一步来 第一步: 找到根目录下的 pages.json 文件,在 tabBar 中把 custom 设置为 true,默认值是 false。list 中设置自定义的相关信息, pagePath&#x…...
自己实现的一个缓存数据库(搞着玩) .net Core/6/8/9
自己实现的一个缓存数据库(搞着玩) 想法来源特点说明 上代码主体基类测试类 注 想法来源 做过一个小型项目,客户要求易移植,不能使用收费的数据库,最好是一个包搞定,尝试过用sqlite,在部分linux…...
在Qt中,slots 关键字有什么用?
有下面的Qt代码: #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr…...
如何查看linux机器有几个cpu
在 Linux 机器上,你可以使用以下几种方法来查看 CPU 的数量(物理 CPU 和逻辑 CPU): 方法 1:使用 lscpu 命令 lscpu输出示例: CPU(s): 8 Thread(s) per core: 2 Core(s) per socket: 4 Soc…...
Swoole如何处理内存泄漏
Swoole处理内存泄漏的方式主要包括以下几个方面: 一、内存管理机制 Swoole的内存管理机制与普通PHP-CLI程序一致,但它在事件回调函数返回后会自动回收所有局部对象和变量,不需要手动unset。如果变量是一个资源类型,那么对应的资…...

Llama最新开源大模型Llama3.1
Meta公司于2024年7月23日发布了最新的开源大模型Llama 3.1,这是其在大语言模型领域的重要进展。以下是关于Llama 3.1的详细介绍: 参数规模与训练数据 Llama 3.1拥有4050亿(405B)参数,是目前开源领域中参数规模最大的…...

Pixflow - CL-DJI Drone LUTs 120个大疆Drone无人机相机航拍电影级镜头LUT调色预设
120组电影质感DJI大疆无人机航拍视频LOG&Rec 709还原颜色分级调色LUTs预设包Pixflow – CL-DJI Drone LUTs 使用基于城市外观和 DJI 无人机镜头的最佳 Drone Luts 颜色预设来提升您的视频。 120 个出色的颜色分级 LUTS,您可以将其与任何无人机视频素材一起使用…...

了解AI绘图,Stable Diffusion的使用
AI绘图对GPU算力要求较高。 个人电脑配置可参考: CPU:14600kf 盒装 显卡:RTX 4080金属大师 OC,16G显存 主板:z790吹雪d4 内存:芝奇皇家戟4000c18,162G 硬盘:宏基gm7000 1T 散热:追风…...

idea整合deepseek实现AI辅助编程
1.File->Settings 2.安装插件codegpt 3.注册deepseek开发者账号,DeepSeek开放平台 4.按下图指示创建API KEY 5.回到idea配置api信息,File->Settings->Tools->CodeGPT->Providers->Custom OpenAI API key填写deepseek的api key Chat…...
llama_index
目录 安装 llama_index 搜索引擎 用 DeepSeek API 替换本地 Ollama 模型 源代码: 安装 pip install llama_index llama_index 搜索引擎 llama_index框架构建搜索引擎_llamaindex使用正则表达式拆分文档-CSDN博客 用 DeepSeek API 替换本地 Ollama 模型 https…...
Spring Boot统一异常拦截实践指南
Spring Boot统一异常拦截实践指南 一、为什么需要统一异常处理 在Web应用开发中,异常处理是保证系统健壮性和用户体验的重要环节。传统开发模式中常见的痛点包括: 异常处理逻辑分散在各个Controller中错误响应格式不统一敏感异常信息直接暴露给客户端…...

Games104——游戏引擎Gameplay玩法系统:基础AI
这里写目录标题 寻路/导航系统NavigationWalkable AreaWaypoint NetworkGridNavigation Mesh(寻路网格)Sparse Voxel Octree Path FindingDijkstra Algorithm迪杰斯特拉算法A Star(A*算法) Path Smoothing Steering系统Crowd Simu…...

【大模型】【推荐系统】LLM在推荐系统中的应用价值
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点B.4 两大推荐方法 C 模型结构C.1 知识蒸馏(训练过程)C.2 轻量推理(部署过程) D 实验设计E 个人总结 A 论文出处 论文题目:SLMRec:Distilling…...

PHP环境极速搭建
一、为什么选择phpStudy VS Code? 作为一名初次接触PHP的开发者,我深知环境配置往往是学习路上的第一道门槛。传统PHP环境搭建需要手动配置Apache/Nginx、PHP解释器、MySQL等多重组件,光是处理版本兼容性和依赖问题就可能耗费半天时间——这…...

web3-基于贝尔曼福特算法(Bellman-Ford )与 SMT 的 Web3 DeFi 套利策略研究
web3-基于贝尔曼福特算法(Bellman-Ford )与 SMT 的 Web3 DeFi 套利策略研究 如何找到Defi中的交易机会 把defi看做是一个完全开放的金融产品图表,可以看到所有的一切东西;我们要沿着这些金融图表找到一些最优的路径,就…...

行为型设计模式之Mediator(中介者)
行为型设计模式之Mediator(中介者) 1)意图 用一个中介对象来封装一系列的对象的交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 2)结构 其中ÿ…...
Java数据校验:确保数据完整性和正确性
在软件开发中,数据校验是确保应用程序数据完整性和正确性的关键步骤。Java 提供了多种方式来实现数据校验,从简单的条件检查到复杂的框架支持。在这篇博客中,我们将探讨 Java 中数据校验的重要性、常用的校验注解以及如何整合校验框架来提高代…...
Redis线程安全深度解析:单线程模型的并发智慧
Redis线程安全深度解析:单线程模型的并发智慧 引言:Redis的线程模型迷思 “Redis是单线程的”——这个广为流传的说法既正确又不完全正确。Redis的线程安全机制实际上是一套精心设计的并发控制体系,它既保持了单线程的简单性,又…...
n8n 自动化平台 Docker 部署教程(附 PostgreSQL 与更新指南)
n8n 自动化平台 Docker 部署教程(附 PostgreSQL 与更新指南) n8n 是一个强大的可视化工作流自动化工具,支持无代码或低代码地集成各种服务。本文将手把手教你如何通过 Docker 快速部署 n8n,并介绍如何使用 PostgreSQL、设置时区以…...
前端面试五之vue2基础
1.属性绑定v-bind(:) v-bind 是 Vue 2 中用于动态绑定属性的核心指令,它支持多种语法和用法,能够灵活地绑定 DOM 属性、组件 prop,甚至动态属性名。通过 v-bind,可以实现数据与视图之间的高效同…...

WebRTC通话原理与入门难度实战指南
波煮的实习公司主要是音视频业务,所以最近在补习WebRTC的相关内容,会不定期给大家分享学习心得和笔记。 文章目录 WebRTC通话原理进行媒体协商:彼此要了解对方支持的媒体格式网络协商:彼此要了解对方的网络情况,这样才…...
【前端】常用组件的CSS
1. button的样式修改 每个环节有五个不同的状态:link,hover,active,focus和visited. Link是正常的外观,hover当你鼠标悬停时,active是单击它时的状态,focus跟随活动状态,visited是你在最近点击的链接未聚焦时结束的状态。 纯CSS 以下为例子,按下后从浅紫到深紫。注…...