【Python】【进阶篇】二十一、Python爬虫的多线程爬虫
目录
- 二十一、Python爬虫的多线程爬虫
- 21.1 多线程使用流程
- 21.2 Queue队列模型
- 21.3 多线程爬虫案例
- 1) 案例分析
- 2) 完整程序
二十一、Python爬虫的多线程爬虫
网络爬虫程序是一种 IO 密集型程序,程序中涉及了很多网络 和 本地磁盘的 IO 操作,这会消耗大量的时间,从而降低程序的执行效率,而 Python 提供的多线程能够在一定程度上提升 IO 密集型程序的执行效率。
21.1 多线程使用流程
Python 提供了两个支持多线程的模块,分别是 _thread 和 threading。其中 _thread 模块偏底层,它相比于 threading 模块功能有限,因此推荐大家使用 threading 模块。 threading 中不仅包含了 _thread 模块中的所有方法,
还提供了一些其他方法,如下所示:
- threading.currentThread() 返回当前的线程变量。
- threading.enumerate() 返回一个所有正在运行的线程的列表。
- threading.activeCount() 返回正在运行的线程数量。
线程的具体使用方法如下所示:
from threading import Thread
#线程创建、启动、回收
t = Thread(target=函数名) # 创建线程对象
t.start() # 创建并启动线程
t.join() # 阻塞等待回收线程
创建多线程的具体流程:
t_list = []
for i in range(5):t = Thread(target=函数名)t_list.append(t)t.start()
for t in t_list:t.join()
除了使用该模块外,您也可以使用 Thread 线程类来创建多线程。
在处理线程的过程中要时刻注意线程的同步问题,即多个线程不能操作同一个数据,否则会造成数据的不确定性。通过 threading 模块的 Lock
对象能够保证数据的正确性。
比如,使用多线程将抓取数据写入磁盘文件,此时,就要对执行写入操作的线程加锁,这样才能够避免写入的数据被覆盖。当线程执行完写操作后会主动释放锁,继续让其他线程去获取锁,周而复始,直到所有写操作执行完毕。具体方法如下所示:
from threading import Lock
lock = Lock()
# 获取锁
lock.acquire()
wirter.writerows("线程锁问题解决")
# 释放锁
lock.release()
21.2 Queue队列模型
对于 Python 多线程而言,由于 GIL 全局解释器锁的存在,同一时刻只允许一个线程占据解释器执行程序,当此线程遇到 IO
操作时就会主动让出解释器,让其他处于等待状态的线程去获取解释器来执行程序,而该线程则回到等待状态,这主要是通过线程的调度机制实现的。
由于上述原因,我们需要构建一个多线程共享数据的模型,让所有线程都到该模型中获取数据。queue(队列,先进先出)
模块提供了创建共享数据的队列模型。比如,把所有待爬取的 URL 地址放入队列中,每个线程都到这个队列中去提取 URL。queue 模块的具体使用方法如下:
# 导入模块
from queue import Queue
q = Queue() #创界队列对象
q.put(url) 向队列中添加爬取一个url链接
q.get() # 获取一个url,当队列为空时,阻塞
q.empty() # 判断队列是否为空,True/False
21.3 多线程爬虫案例
下面通过多线程方法抓取小米应用商店(https://app.mi.com/)中应用分类一栏,所有类别下的 APP 的名称、所属类别以及下载详情页 URL。如下图所示:

抓取下来的数据 demo 如下所示:
三国杀,棋牌桌游,http://app.mi.com/details?id=com.bf.sgs.hdexp.mi
1) 案例分析
通过搜索关键字可知这是一个动态网站,因此需要抓包分析。
刷新网页来重新加载数据,可得知请求头的 URL 地址,如下所示:
https://app.mi.com/categotyAllListApi?page=0&categoryId=1&pageSize=30
其中查询参数 pageSize 参数值不变化,page 会随着页码的增加而变化,而类别 Id 通过查看页面元素,如下所示
<ul class="category-list">
<li><a class="current" href="/category/15">游戏</a></li>
<li><a href="/category/5">实用工具</a></li>
<li><a href="/category/27">影音视听</a></li>
<li><a href="/category/2">聊天社交</a></li>
<li><a href="/category/7">图书阅读</a></li>
<li><a href="/category/12">学习教育</a></li>
<li><a href="/category/10">效率办公</a></li>
<li><a href="/category/9">时尚购物</a></li>
<li><a href="/category/4">居家生活</a></li>
<li><a href="/category/3">旅行交通</a></li>
<li><a href="/category/6">摄影摄像</a></li>
<li><a href="/category/14">医疗健康</a></li>
<li><a href="/category/8">体育运动</a></li>
<li><a href="/category/11">新闻资讯</a></li>
<li><a href="/category/13">娱乐消遣</a></li>
<li><a href="/category/1">金融理财</a></li>
</ul>
因此,可以使用 Xpath 表达式匹配 href 属性,从而提取类别 ID 以及类别名称,表达式如下:
基准表达式:xpath_bds = '//ul[@class="category-list"]/li'
提取 id 表达式:typ_id = li.xpath('./a/@href')[0].split('/')[-1]
类型名称:typ_name = li.xpath('./a/text()')[0]
点击开发者工具的 response 选项卡,查看响应数据,如下所示:
{
count: 2000,
data: [
{
appId: 1348407,
displayName: "天气暖暖-关心Ta从关心天气开始",
icon: "http://file.market.xiaomi.com/thumbnail/PNG/l62/AppStore/004ff4467a7eda75641eea8d38ec4d41018433d33",
level1CategoryName: "居家生活",
packageName: "com.xiaowoniu.WarmWeather"
},
{
appId: 1348403,
displayName: "贵斌同城",
icon: "http://file.market.xiaomi.com/thumbnail/PNG/l62/AppStore/0e607ac85ed9742d2ac2ec1094fca3a85170b15c8",
level1CategoryName: "居家生活",
packageName: "com.gbtc.guibintongcheng"
},
...
...
通过上述响应内容,我们可以从中提取出 APP 总数量(count)和 APP (displayName)名称,以及下载详情页的
packageName。由于每页中包含了 30 个 APP,所以总数量(count)可以计算出每个类别共有多少页。
pages = int(count) // 30 + 1
下载详情页的地址是使用 packageName 拼接而成,如下所示:
link = 'http://app.mi.com/details?id=' + app['packageName']
2) 完整程序
完整程序如下所示:
# -*- coding:utf8 -*-
import requests
from threading import Thread
from queue import Queue
import time
from fake_useragent import UserAgent
from lxml import etree
import csv
from threading import Lock
import jsonclass XiaomiSpider(object):def __init__(self):self.url = 'http://app.mi.com/categotyAllListApi?page={}&categoryId={}&pageSize=30'# 存放所有URL地址的队列self.q = Queue()self.i = 0# 存放所有类型id的空列表self.id_list = []# 打开文件self.f = open('XiaomiShangcheng.csv','a',encoding='utf-8')self.writer = csv.writer(self.f)# 创建锁self.lock = Lock()def get_cateid(self):# 请求url = 'http://app.mi.com/'headers = { 'User-Agent': UserAgent().random}html = requests.get(url=url,headers=headers).text# 解析parse_html = etree.HTML(html)xpath_bds = '//ul[@class="category-list"]/li'li_list = parse_html.xpath(xpath_bds)for li in li_list:typ_name = li.xpath('./a/text()')[0]typ_id = li.xpath('./a/@href')[0].split('/')[-1]# 计算每个类型的页数pages = self.get_pages(typ_id)#往列表中添加二元组self.id_list.append( (typ_id,pages) )# 入队列self.url_in()# 获取count的值并计算页数def get_pages(self,typ_id):# 获取count的值,即app总数url = self.url.format(0,typ_id)html = requests.get(url=url,headers={'User-Agent':UserAgent().random}).json()count = html['count']pages = int(count) // 30 + 1return pages# url入队函数,拼接url,并将url加入队列def url_in(self):for id in self.id_list:# id格式:('4',pages)for page in range(1,id[1]+1):url = self.url.format(page,id[0])# 把URL地址入队列self.q.put(url)# 线程事件函数: get() -请求-解析-处理数据,三步骤def get_data(self):while True:# 判断队列不为空则执行,否则终止if not self.q.empty():url = self.q.get()headers = {'User-Agent':UserAgent().random}html = requests.get(url=url,headers=headers)res_html = html.content.decode(encoding='utf-8')html=json.loads(res_html)self.parse_html(html)else:break# 解析函数def parse_html(self,html):# 写入到csv文件app_list = []for app in html['data']:# app名称 + 分类 + 详情链接name = app['displayName']link = 'http://app.mi.com/details?id=' + app['packageName']typ_name = app['level1CategoryName']# 把每一条数据放到app_list中,并通过writerows()实现多行写入app_list.append([name,typ_name,link])print(name,typ_name)self.i += 1# 向CSV文件中写入数据self.lock.acquire()self.writer.writerows(app_list)self.lock.release()# 入口函数def main(self):# URL入队列self.get_cateid()t_list = []# 创建多线程for i in range(1):t = Thread(target=self.get_data)t_list.append(t)# 启动线程t.start()for t in t_list:# 回收线程 t.join()self.f.close()print('数量:',self.i)if __name__ == '__main__':start = time.time()spider = XiaomiSpider()spider.main()end = time.time()print('执行时间:%.1f' % (end-start))
运行上述程序后,打开存储文件,其内容如下:
在我们之间-单机版,休闲创意,http://app.mi.com/details?id=com.easybrain.impostor.gtx粉末游戏,模拟经营,http://app.mi.com/details?id=jp.danball.powdergameviewer.bnn三国杀,棋牌桌游,http://app.mi.com/details?id=com.bf.sgs.hdexp.mi腾讯欢乐麻将全集,棋牌桌游,http://app.mi.com/details?id=com.qqgame.happymj快游戏,休闲创意,http://app.mi.com/details?id=com.h5gamecenter.h2mgc皇室战争,战争策略,http://app.mi.com/details?id=com.supercell.clashroyale.mi地铁跑酷,跑酷闯关,http://app.mi.com/details?id=com.kiloo.subwaysurf
...
...
相关文章:
【Python】【进阶篇】二十一、Python爬虫的多线程爬虫
目录 二十一、Python爬虫的多线程爬虫21.1 多线程使用流程21.2 Queue队列模型21.3 多线程爬虫案例1) 案例分析2) 完整程序 二十一、Python爬虫的多线程爬虫 网络爬虫程序是一种 IO 密集型程序,程序中涉及了很多网络 和 本地磁盘的 IO 操作,这会消耗大…...
Python从入门到精通14天(eval、literal_eval、exec函数的使用)
eval、literal_eval、exec函数的使用 eval函数literal_eval函数exec函数三者的区别 eval函数 eval()是Python中的内置函数,它可以将一个字符串作为参数,并将该字符串作为Python代码执行。它的语法格式为:eval(expression,globalsNone,locals…...
队列的基本操作(C语言链表实现)初始化,入队,出队,销毁,读取数据
文章目录 前言一、队列基本变量的了解二、队列的基本操作2.1队列的初始化(QueueInit)2.2入队(QueuePush)2.3判断是否为空队(QueueEmpty)2.4出队(QueuePop)2.5队列的队头数据…...
项目支付接入支付宝【沙箱环境】
前言 订单支付接入支付宝,使用支付宝提供的沙箱机制模拟为订单付款。我这里主要记录一下沙箱环境如何接入到系统中,具体细节的实现。按照官方文档来就可以了。 1、使用步骤 这里有几个重要数据要拿到,一个是支付宝的公钥和私钥,…...
程序员应该如何提升自己
作为一名程序员,以下是您可以考虑的一些方法来提高自己的技能和知识: 深入学习编程语言和相关工具:了解您使用的编程语言和相关工具的基本原理和高级特性,以便更好地理解其工作方式并更有效地使用它们。 刻意练习:刻意…...
全球上线!ABB中国涡轮增压器分拆 – 数据清理阶段完成
ABB是数字行业的技术前沿者,拥有四项主营业务:电气化,工业自动化,运动控制以及机器人和离散自动化。ABB总部位于瑞士苏黎世,业务遍及100多个国家,拥有约105,000名员工。2021年,该公司…...
手写简易 Spring(三)
文章目录 三. 手写简易 Spring(三)1. Bean 对象初始化和销毁方法1. XML 添加 init-method 与实现 InitializingBean 接口注册初始化2. XML 添加 destroy-method 与实现 DisposableBean 接口注册销毁3. DefaultSingletonBeanRegistry 优秀的解耦方法 2. 定…...
设计模式-看懂UML类图和时序图
这里不会将UML的各种元素都提到,只讲类图中各个类之间的关系; 能看懂类图中各个类之间的线条、箭头代表什么意思后,也就足够应对 日常的工作和交流; 同时,应该能将类图所表达的含义和最终的代码对应起来; 1…...
2023年全国最新安全员精选真题及答案57
百分百题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 101.(单选题)遇有()及以上强风、浓雾等…...
数字图像处理基础
图像增强:不论方法,只要能够得到较好的图像即可 图像复原:找到图像退化的原因,把噪声过滤得到较好的图像 RGB——HSI(色调 饱和度 亮度)彩色图像处理需要用到灰度图像处理 直方图均衡,灰度概率密度函数接近均匀分布&a…...
onnx手动操作
使用onnx.helper可以进行onnx的制造组装操作: 对象描述ValueInfoProto 对象张量名、张量的基本数据类型、张量形状算子节点信息 NodeProto算子名称(可选)、算子类型、输入和输出列表(列表元素为数值元素)GraphProto对象用张量节点和算子节点组成的计算图对象ModelP…...
虚拟机安装Centos7,ping不通百度
虚拟机安装Centos7,ping不通百度 一、虚拟机网络配置 网络适配器选择桥接模式,不勾选复制物理网络连接状态。 同时虚拟机使用默认配置都是桥接。 二、配置静态IP 1、首先,查看宿主机的IP和网关 2、配置静态ip的文件地址及修改命令如下&…...
leetCode算法第一天
今天开始刷算法题,提升自己的算法思维和代码能力,加油! 文章目录 无重复字符的最长子串最长回文子串N形变换字符串转换整数 无重复字符的最长子串 leetCode链接 https://leetcode.cn/problems/longest-substring-without-repeating-characte…...
怎么将太大的word文档压缩变小,3个高效方法
怎么将太大的word文档压缩变小?word文档是我们在办公中使用较多的文件格式之一,相信小伙伴们会遇到这样的问题,编辑完成word文档之后发现,编辑完的文档体积太大了,无论是发送给客户还是上传到邮箱中都不方便࿰…...
mvc+动态代理
不使用MVC的时候系统存在的缺陷 一个Servlet都负责了那些工作? 负责了接收数据负责了核心的业务处理负责了数据表中的CRUD负责了页面的数据展示… 分析银行转账项目存在那些问题? 代码的复用性太差。(代码的重用性太差) 因为没…...
vue-cli(vue脚手架方式搭建)
1.首先安装node前端环境,可以帮助我们去下载其他的组件 下载完成后,去自己的电脑找到node的文件路径,复制去配置环境变量,在path中配 环境搭配完成后,在cmd中进行测试 ,输入一下两个命令进行测试 2.在hbuilderX中创建一个vue-cli项目(标准的前段项目) 3.组件路由 (1)安装 v…...
CentOS 安装 Docker
文章目录 一、更新yum源二、查看docker是否曾经安装过三、安装所需要的软件包四、设置yum源(也可以设置成国内的阿里源等)五、查看docker版本六、.安装docker (默认全部选y)七、查看docker安装版本八、docker 启动/停止/重启/开机…...
别搞了 软件测试真卷不动了...
内卷可以说是 2022年最火的一个词了。2023 年刚开始,在很多网站看到很多 软件测试的 2022 年度总结都是:软件测试 越来越卷了(手动狗头),2022 年是被卷的一年。前有几百万毕业生虎视眈眈,后有在职人员带头“…...
OJ刷题 第十二篇
21308 - 特殊的三角形 时间限制 : 1 秒 内存限制 : 128 MB 有这样一种特殊的N阶的三角形,当N等于3和4时,矩阵如下: 请输出当为N时的三角形。 输入 输入有多组数据,每行输入一个正整数N,1<N<100 输出 按照给出…...
【计算机专业应届生先找培训还是先找个工作过渡一下?】
计算机专业应届生先找培训还是先找个工作过渡一下? 计算机应届生是先培训还是先工作,这个问题应该困扰了很多专业技能一般的同学,尤其是学历方面还没有优势的普通本专科院校。都说技术与学历优秀的人进大厂,技术一般学历优秀的人能…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
