Python爬虫中的多线程、线程池

进程和线程的基本介绍

进程是一个资源单位,线程是一个执行单位,CPU调度线程来执行程序代码。

当运行一个程序时,会给这个程序分配一个内存空间,存放变量等各种信息资源,而这个内存空间可以说是一个进程, 一个进程默认情况下会有一个线程,称为主线程(因为执行是靠线程的,CPU调度线程来执行程序代码,如果没有线程,那么进程中的资源就不能被使用,代码也就不能被执行)

做个比喻:一个进程相当于一个公司,公司里有各种办公资源,而公司里的员工就相当于线程,工作由员工使用办公资源完成, 如果没有员工,那么那些办公资源不会自动把工作完成,而一个公司必须至少有一个员工,不然公司还能自己成立嘛?

单线程

def func():for i in range(3):print('func: ', i)if __name__ == '__main__':func()for i in range(3):print('main: ', i)

执行效果:

 

多线程(写法一)

from threading import Thread# 多线程
def func():for i in range(3):print('func: ', i)if __name__ == '__main__':# 创建一个线程,target参数是告诉这个线程该干什么事# 就比如招聘了一个新员工,要给他安排任务# 此处给线程的任务就是调用func方法t = Thread(target=func)# 启动线程,告诉线程可以开始干活了# 注意,start方法只是说线程的状态是工作状态,但是什么时候真的开始执行,由CPU决定# 就是说线程已经随时准备就绪,等待CPU的调度执行t.start()for i in range(3):print('main: ', i)# 多线程
def func(name):for i in range(3):print(name, i)if __name__ == '__main__':# 要给func传递参数,则可以利用args参数# 注意,args接收的是元祖,所以只有一个参数的话,要加上逗号t1 = Thread(target=func, args=('线程1',))t1.start()# 创建第二个线程t2 = Thread(target=func, args='线程2',)for i in range(3):print('main: ', i)

 执行结果:

多线程(写法二)

from threading import Thread# 多线程(写法二)
# 自定义线程类,要继承 Thread
class MyThread(Thread):def run(self):  # 重写run方法for i in range(3):print('子线程: ', i)if __name__ == '__main__':t = MyThread()# 注意,不能 t.run() ,否则就相当于方法调用,那么就是单线程而不是多线程# 必须通过start方法调用# t.run()  # 错误# 调用start方法时,start方法会自己去调用run方法# 所以当线程被执行时,执行的是run方法里的代码逻辑t.start()for i in range(3):print('主线程: ', i)

执行效果

创建多进程

和创建多线程差不多 

from multiprocessing import Processdef func():for i in range(3):print('子进程:', i)# 创建进程比创建线程所耗费的资源要多很多,所以一般我们使用的都是线程
if __name__ == '__main__':# 如果打印结果不是混着的,应该也是正常的,因为我执行的时候结果和单线程一样p = Process(target=func)p.start()for i in range(3):print('主进程:', i)

线程池和进程池基本介绍

假设现在我们要爬取一个网站的评论信息,这个网站的评论由于有很多,被分为1000页,那么我们可以用for循环去爬取每一页的评论数据,但我们想提高效率,利用线程来完成。

我们可以创建1000个线程,每个线程分别爬取一个分页,但是创建线程也需要耗费时间和资源,创建1000个线程的效率可能不一定比for循环效率高。

那我们可以考虑少创建一些线程,比如创建50个线程,然后让这50个线程依次去爬取1000个分页的评论,这就是线程池的作用。我们不需要关心这50个线程的具体调度情况,比如哪个进程爬取了哪些网页之类的,我们只需要知道,这50个线程会一起合作,完成1000个URL的爬取任务即可。而具体的每个线程的调度安排由线程池管理。

所以线程池就是一次性开辟一些线程(如一次性开辟50个线程),我们用户直接给线程池提交某个任务(比如爬取1000个URL),由线程池安排这50个线程去共同完成这个任务。

线程池代码如下,进程池的使用和线程池一样,只需要把 ThreadPoolExecutor 换成 ProcessPoolExecutor就行

# ThreadPoolExecutor 线程池, ProcessPoolExecutor 进程池
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutordef task(name):print('执行任务', name)if __name__ == '__main__':# 创建有3个进程的进程池with ThreadPoolExecutor(3) as t:for i in range(6):  # 现在要完成6个任务,将这6个任务分配给3个线程# 向线程池t提交任务task, 任务task需要传惨直接在后面写上,如name=xxxt.submit(task, name=f'task{i}')# 只有上述线程池代码执行完成后,才会接着往下执行print('任务全部执行完毕...')

执行效果如下图:

 进程池的应用——爬取北京新发地的价格数据

import requests
from concurrent.futures import ThreadPoolExecutordef download_one_page(cur_page):url = 'http://www.xinfadi.com.cn/getPriceData.html'data = {'limit': 20,'current': cur_page,  # 第多少页的数据'pubDateStartTime': '','pubDateEndTime': '','prodPcatid': '','prodCatid': '','prodName': ''}resp = requests.post(url, data=data)print(resp.json())if __name__ == '__main__':# 可以分别对比for循环和用线程池两种爬取方式,会看到线程池的速度要快很多# for i in range(1, 101): #     download_one_page(i)with ThreadPoolExecutor(10) as t:  # 创建有10个进程的进程池for i in range(1, 101):  # 数据太多了,这里只爬取前100页t.submit(download_one_page, cur_page=i)print('爬取北京新发地前100页数据完成!')

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/299639.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自学SLAM(9)《第五讲:特征点法视觉里程计》作业

文章目录 1.ORB特征点1.1 ORB提取1.2 ORB描述1.3 暴力匹配1.4 最后,请结合实验,回答下⾯⼏个问题 2.从 E 恢复 R,t3.用 G-N 实现 Bundle Adjustment4.* 用 ICP 实现轨迹对齐 1.ORB特征点 1.1 ORB提取 ORB(Oriented FAST and BRIEF) 特征是 S…

Android camera打开摄像头、预览

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

【MySQL】数据库之事务

目录 一、什么是事务 二、事务的ACID是什么&#xff1f; 三、有哪些典型的不一致性问题&#xff1f; 第一种&#xff1a;脏读 第二种&#xff1a;不可重复读 第三种&#xff1a;幻读 第四种&#xff1a;丢失更新 四、隔离级别有哪些&#xff1f; &#xff08;1&#xf…

doris基本操作,05-Rollup

简述 Rollup类似于mysql的视图&#xff0c;区别在于视图并没有将数据独立存储&#xff0c;视图是逻辑上的连接。而Rollup将数据独立存储了&#xff0c;玩的是真的。当查询命中Rollup时&#xff0c;会从Rollup表里获取数据&#xff0c;提高查询效率。 操作 创建Rollup表 alt…

codeforces round 894题解 A~F

文章目录 A. Gift Carpet题目大意思路AC代码 B. Sequence Game题目大意思路AC代码 C. Flower City Fence题目大意思路AC代码 D. Ice Cream Balls题目大意思路AC代码 E. Kolya and Movie Theatre题目大意思路AC代码 F. Magic Will Save the World题目大意思路AC代码 A. Gift Car…

【软件工程】可执行文件和数据分离

一、概述 可执行文件和数据分离是一种软件设计策略&#xff0c;旨在将程序代码和程序使用的数据分离存储。这种方法通常用于提高软件的模块化程度和灵活性&#xff0c;以及方便软件的管理和维护。 在可执行文件和数据分离中&#xff0c;程序代码通常以可执行文件的形式存储&a…

搭建Nginx文件下载站点

一、下载Nginx 首先&#xff0c;确保你的服务器上已经安装了Nginx&#xff0c;使用编译安装&#xff0c;下载最新版Nginx。 wget https://nginx.org/download/nginx-1.25.3.tar.gz tar -xf nginx-1.25.3.tar.gz二、安装Fancyindex和Nginx-Fancyindex-Theme模块 # 下载Fancyin…

MyBatis:Generator

MyBatis Generator附批量操作分页查询存储过程 Generator 介绍网址&#xff1a;Introduction to MyBatis Generator Generator &#xff0c;一个用于 MyBatis 的代码生成工具&#xff0c;可以根据数据库表结构自动生成对应的实体类、DAO 接口和 SQL 映射文件&#xff0c;提高…

java练习之abstract (抽象) final(最终) static(静态) 练习

1&#xff1a;分析总结&#xff1a;写出private、abstract、static、final之间能否联动使用&#xff0c;并写出分析原因 private static final 之间可以任意结合 abstract 不可以与private static final 结合使用 2&#xff1a;关于三个修饰符描述不正确的是(AD) A. static …

网络编程--socket编程

这里写目录标题 套接字概念通信原理总结 预备知识网络字节序简介字节转换函数 IP地址转换函数为什么单独列出函数原型sockaddr结构体 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录二级目录 套接字 概念 Socket本身有插座的意思&#xff0c;但他是进程之间网络通…

Win10搜索功能失效用不了的解决方法

在Win10电脑中&#xff0c;用户点击搜索框&#xff0c;输入想要搜索的内容&#xff0c;点击搜索即可。但是&#xff0c;有用户遇到了搜索功能坏掉的问题&#xff0c;导致自己不能正常使用搜索功能。下面小编将带来Win10系统搜索功能失效用不了的简单解决方法&#xff0c;操作后…

【开源】基于JAVA语言的学校热点新闻推送系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 新闻类型模块2.2 新闻档案模块2.3 新闻留言模块2.4 新闻评论模块2.5 新闻收藏模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 新闻类型表3.2.2 新闻表3.2.3 新闻留言表3.2.4 新闻评论表3.2.5 新闻收藏表 四、系统展…