当前位置: 首页 > news >正文

网络爬虫学习:借助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代码&#xff1a; #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 机器上&#xff0c;你可以使用以下几种方法来查看 CPU 的数量&#xff08;物理 CPU 和逻辑 CPU&#xff09;&#xff1a; 方法 1&#xff1a;使用 lscpu 命令 lscpu输出示例&#xff1a; CPU(s): 8 Thread(s) per core: 2 Core(s) per socket: 4 Soc…...

Swoole如何处理内存泄漏

Swoole处理内存泄漏的方式主要包括以下几个方面&#xff1a; 一、内存管理机制 Swoole的内存管理机制与普通PHP-CLI程序一致&#xff0c;但它在事件回调函数返回后会自动回收所有局部对象和变量&#xff0c;不需要手动unset。如果变量是一个资源类型&#xff0c;那么对应的资…...

Llama最新开源大模型Llama3.1

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

Pixflow - CL-DJI Drone LUTs 120个大疆Drone无人机相机航拍电影级镜头LUT调色预设

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

了解AI绘图,Stable Diffusion的使用

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

idea整合deepseek实现AI辅助编程

1.File->Settings 2.安装插件codegpt 3.注册deepseek开发者账号&#xff0c;DeepSeek开放平台 4.按下图指示创建API KEY 5.回到idea配置api信息&#xff0c;File->Settings->Tools->CodeGPT->Providers->Custom OpenAI API key填写deepseek的api key Chat…...

llama_index

目录 安装 llama_index 搜索引擎 用 DeepSeek API 替换本地 Ollama 模型 源代码&#xff1a; 安装 pip install llama_index llama_index 搜索引擎 llama_index框架构建搜索引擎_llamaindex使用正则表达式拆分文档-CSDN博客 用 DeepSeek API 替换本地 Ollama 模型 https…...

Spring Boot统一异常拦截实践指南

Spring Boot统一异常拦截实践指南 一、为什么需要统一异常处理 在Web应用开发中&#xff0c;异常处理是保证系统健壮性和用户体验的重要环节。传统开发模式中常见的痛点包括&#xff1a; 异常处理逻辑分散在各个Controller中错误响应格式不统一敏感异常信息直接暴露给客户端…...

Games104——游戏引擎Gameplay玩法系统:基础AI

这里写目录标题 寻路/导航系统NavigationWalkable AreaWaypoint NetworkGridNavigation Mesh&#xff08;寻路网格&#xff09;Sparse Voxel Octree Path FindingDijkstra Algorithm迪杰斯特拉算法A Star&#xff08;A*算法&#xff09; Path Smoothing Steering系统Crowd Simu…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...

从实验室到产业:IndexTTS 在六大核心场景的落地实践

一、内容创作&#xff1a;重构数字内容生产范式 在短视频创作领域&#xff0c;IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色&#xff0c;生成的 “各位吴彦祖们大家好” 语音相似度达 97%&#xff0c;单条视频播放量突破百万…...