网络爬虫学习:借助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…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
背包问题双雄:01 背包与完全背包详解(Java 实现)
一、背包问题概述 背包问题是动态规划领域的经典问题,其核心在于如何在有限容量的背包中选择物品,使得总价值最大化。根据物品选择规则的不同,主要分为两类: 01 背包:每件物品最多选 1 次(选或不选&#…...

docker容器互联
1.docker可以通过网路访问 2.docker允许映射容器内应用的服务端口到本地宿主主机 3.互联机制实现多个容器间通过容器名来快速访问 一 、端口映射实现容器访问 1.从外部访问容器应用 我们先把之前的删掉吧(如果不删的话,容器就提不起来,因…...