影刀RPA 多线程基础:并发采集入门

发布时间:2026/11/5 19:30:55

影刀RPA 多线程基础:并发采集入门 影刀RPA 多线程基础并发采集入门署名林焱什么情况用什么RPA采集100个网页单线程一个个打开要30分钟。如果同时开5个线程6分钟就跑完了。多线程能显著提升IO密集型任务效率。场景推荐方式特点网页批量采集threading 队列简单易用CPU密集型计算multiprocessing绕过GIL限制大量并发请求ThreadPoolExecutor线程池管理怎么做一基础多线程importthreadingimporttime# 单线程对比deffetch_page(url):模拟采集一个网页print(f开始采集:{url})time.sleep(2)# 模拟网络请求耗时print(f完成采集:{url})returnf{url}的内容urls[https://example.com/page1,https://example.com/page2,https://example.com/page3,https://example.com/page4,https://example.com/page5,]# 单线程约10秒 print( 单线程 )starttime.time()results[]forurlinurls:results.append(fetch_page(url))print(f耗时:{time.time()-start:.1f}秒)# 多线程约2秒 print(\n 多线程 )starttime.time()threads[]results[None]*len(urls)# 预分配结果列表defworker(index,url):results[index]fetch_page(url)fori,urlinenumerate(urls):tthreading.Thread(targetworker,args(i,url))threads.append(t)t.start()# 等待所有线程完成fortinthreads:t.join()print(f耗时:{time.time()-start:.1f}秒)二线程池推荐拼多多店群自动化报活动上架fromconcurrent.futuresimportThreadPoolExecutor,as_completedimporttimedeffetch_page(url):采集网页time.sleep(1)# 模拟请求return{url:url,status:200,data:f{url}的内容}urls[fhttps://example.com/page{i}foriinrange(1,21)]# 使用线程池max_workers5# 最多5个线程同时运行results[]withThreadPoolExecutor(max_workersmax_workers)asexecutor:# 提交所有任务![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/73c282530e3f4f16a0b780322253ab24.png#pic_center)future_to_url{executor.submit(fetch_page,url):urlforurlinurls}# 按完成顺序获取结果forfutureinas_completed(future_to_url):urlfuture_to_url[future]try:resultfuture.result()results.append(result)print(f完成:{url})exceptExceptionase:print(f失败:{url}, 错误:{e})print(f\n共完成{len(results)}个任务)三线程安全的队列importthreadingimportqueueimporttime# 任务队列task_queuequeue.Queue()result_queuequeue.Queue()# 填充任务urls[fhttps://example.com/page{i}foriinrange(1,21)]forurlinurls:task_queue.put(url)defworker(worker_id):工作线程whileTrue:try:# 非阻塞获取任务超时退出urltask_queue.get(timeout1)exceptqueue.Empty:break# 执行任务try:time.sleep(0.5)# 模拟采集resultf[线程{worker_id}] 采集完成:{url}result_queue.put({url:url,status:success,data:result})exceptExceptionase:result_queue.put({url:url,status:error,error:str(e)})finally:task_queue.task_done()# 启动多个工作线程num_workers5threads[]foriinrange(num_workers):tthreading.Thread(targetworker,args(i,))t.start()threads.append(t)# 等待所有任务完成task_queue.join()# 收集结果results[]whilenotresult_queue.empty():results.append(result_queue.get())print(f完成{len(results)}个任务)forrinresults[:5]:print(r[data])四线程安全的数据操作importthreading# 线程安全的计数器classSafeCounter:def__init__(self):self._lockthreading.Lock()self._count0defincrement(self):withself._lock:self._count1returnself._countpropertydefvalue(self):withself._lock:returnself._count# 线程安全的列表classSafeList:def__init__(self):self._lockthreading.Lock()self._data[]defappend(self,item):withself._lock:self._data.append(item)defget_all(self):withself._lock:returnlist(self._data)# 使用counterSafeCounter()resultsSafeList()defworker(url):numcounter.increment()results.append(f第{num}个任务:{url})threads[threading.Thread(targetworker,args(furl{i},))foriinrange(10)]fortinthreads:t.start()fortinthreads:t.join()print(f计数:{counter.value})foriteminresults.get_all():print(item)完整流程并发采集管理器fromconcurrent.futuresimportThreadPoolExecutor,as_completedimportthreadingimporttimefromdatetimeimportdatetime# yd_input: urls, max_workers, delayurlsyd_input.get(urls,[])max_workersyd_input.get(max_workers,5)delayyd_input.get(delay,0.5)# 统计stats{total:len(urls),success:0,failed:0,start_time:datetime.now().strftime(%Y-%m-%d %H:%M:%S)}stats_lockthreading.Lock()results[]results_lockthreading.Lock()deffetch_url(url):采集单个URLtry:time.sleep(delay)# 控制请求频率# 这里放实际的采集逻辑# 例如用requests获取网页、解析数据等importrequests resprequests.get(url,timeout15,headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)})ifresp.status_code200:result{url:url,status:success,code:resp.status_code,length:len(resp.text),timestamp:datetime.now().strftime(%H:%M:%S)}withstats_lock:stats[success]1withresults_lock:results.append(result)returnresultelse:raiseException(fHTTP{resp.status_code})exceptExceptionase:withstats_lock:stats[failed]1result{url:url,status:error,error:str(e),timestamp:datetime.now().strftime(%H:%M:%S)}withresults_lock:results.append(result)returnresult# 执行并发采集start_timetime.time()withThreadPoolExecutor(max_workersmax_workers)asexecutor:futures{executor.submit(fetch_url,url):urlforurlinurls}completed0forfutureinas_completed(futures):completed1urlfutures[future]try:resultfuture.result()status_icon✓ifresult[status]successelse✗print(f{status_icon}[{completed}/{len(urls)}]{url})exceptExceptionase:print(f✗ [{completed}/{len(urls)}]{url}:{e})elapsedtime.time()-start_time stats[end_time]datetime.now().strftime(%Y-%m-%d %H:%M:%S)stats[elapsed]f{elapsed:.1f}秒print(f\n 采集完成 )print(f总计:{stats[total]}, 成功:{stats[success]}, 失败:{stats[failed]})print(f耗时:{elapsed:.1f}秒, 平均:{elapsed/len(urls):.2f}秒/个)yd_output{status:ok,stats:stats,results:results}有什么坑坑一多线程共享变量导致数据错乱现象多个线程同时写同一个列表结果列表长度不对或某些数据丢失。原因Python列表的append操作不是原子操作多线程同时写会导致数据覆盖。解决用锁保护共享数据importthreading# 错误不加锁# results []# def worker(url):# results.append(url) ❌ 多线程同时append可能丢数据# 正确用锁results[]lockthreading.Lock()defworker(url):withlock:results.append(url)# ✓ 同一时刻只有一个线程能写# 更好的方案用Queue线程安全importqueue result_queuequeue.Queue()defworker(url):result_queue.put(url)# ✓ Queue自带线程安全坑二线程数过多反而变慢现象把max_workers设为100结果比5个线程还慢。原因线程太多导致频繁的上下文切换开销大于并行收益。另外大量并发请求可能触发网站反爬导致请求被拒绝后重试。解决合理设置线程数TEMU店群矩阵自动化运营核价报活动# IO密集型任务网络请求线程数 CPU核数 × 2~5importos cpu_countos.cpu_count()# CPU核数optimal_workersmin(cpu_count*3,20)# 推荐3-20个线程# 不要无脑设大# max_workers 100 ❌ 太多反而慢# 根据目标网站承受能力调整# 小网站3-5个线程# 大网站10-20个线程# API接口看API的rate limit要求坑三主线程退出导致子线程中断现象主线程跑完了子线程还没完成就被强制退出结果不完整。原因默认子线程是守护线程daemonFalse主线程会等子线程完成。但如果设了daemonTrue主线程退出时子线程会被杀掉。解决确保正确等待子线程完成importthreading# 方案1用join等待推荐threads[]foriinrange(5):tthreading.Thread(targetworker,args(i,))t.start()threads.append(t)fortinthreads:t.join()# 等待每个线程完成print(所有线程完成)# 方案2用ThreadPoolExecutor自动等待fromconcurrent.futuresimportThreadPoolExecutorwithThreadPoolExecutor(max_workers5)asexecutor:futures[executor.submit(worker,i)foriinrange(5)]# with块结束时自动等待所有任务完成# 错误设为daemonTrue后不等待# t threading.Thread(targetworker, daemonTrue)# t.start()# # 不join主线程退出时子线程被杀 ❌坑四线程中的异常不会自动传播到主线程现象子线程报错了但主线程不知道以为全部成功。原因子线程的异常不会自动抛到主线程需要主动检查。解决用Future.result()获取异常或用try-except记录fromconcurrent.futuresimportThreadPoolExecutor,as_completeddefworker(url):iferrorinurl:raiseValueError(模拟错误)returnfsuccess:{url}urls[url1,error_url,url3]withThreadPoolExecutor(max_workers3)asexecutor:futures{executor.submit(worker,url):urlforurlinurls}forfutureinas_completed(futures):urlfutures[future]try:![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/768973b01eb040f38abbd2c293528c22.png#pic_center)# future.result()会重新抛出子线程中的异常resultfuture.result()print(f✓{url}:{result})exceptExceptionase:print(f✗{url}:{e})# 捕获子线程异常# 如果不用Future需要在worker函数内部catch异常defsafe_worker(url):try:# 实际逻辑returndo_work(url)exceptExceptionase:return{url:url,error:str(e)}# 返回错误信息而不是抛出
延伸阅读

更多相关文章

2026/11/5 7:03:44

从ChatGPT到AGI:2026年AI大模型的终极追问与未来幻想

国内用户访问提示:由于网络限制,OpenAI官网及Anthropic官网在国内无法直接访问。国内用户可通过镜像平台AIGCBAR体验ChatGPT、Claude等主流AI模型。该平台内置Auto模式与Thinking模式,思考程度提供标准和深度两档可选,支持国内邮箱…

2026/11/4 18:33:42

TMC7300+STM32L4S5ZI驱动有刷电机方案解析

1. 为什么选择TMC7300STM32L4S5ZI组合驱动有刷电机 有刷直流电机作为最传统的电机类型,至今仍在各类消费电子、工业设备和自动化系统中广泛应用。但要让这类电机实现精准稳定的转速控制,传统的H桥驱动方案往往面临效率低、发热大、响应慢等问题。这正是T…

2026/10/27 5:28:46

企业AI Agent与数据湖融合架构设计与优化实践

1. 企业AI Agent与数据湖的融合趋势最近两年,企业AI Agent正在从单纯的对话机器人向具备复杂决策能力的智能体演进。我在为三家制造业客户部署AI Agent时发现,传统的数据管道架构已经难以满足实时决策需求。某汽车零部件供应商的质检AI Agent需要同时处理…

2026/11/5 16:51:33

EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析

1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被…

2026/11/5 16:26:13

Rust原来很简单二:所有权系统

Part 2 核心篇 —— 所有权系统 本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理 6…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑