Python的多线程爬虫详解

2023-05-16

多线程使用流程

Python 提供了两个支持多线程的模块,分别是 _thread 和 threading。其中 _thread 模块偏底层,它相比于 threading 模块功能有限,因此推荐大家使用 threading 模块。 threading 中不仅包含了  _thread 模块中的所有方法,还提供了一些其他方法,如下所示:

  • threading.currentThread() 返回当前的线程变量。
  • threading.enumerate() 返回一个所有正在运行的线程的列表。
  • threading.activeCount() 返回正在运行的线程数量。

线程的具体使用方法如下所示:

  1. from threading import Thread
  2. ​#线程创建、启动、回收
  3. t = Thread(target=函数名) # 创建线程对象
  4. t.start() # 创建并启动线程
  5. t.join() # 阻塞等待回收线程

创建多线程的具体流程:

  1. t_list = []
  2. for i in range(5):
  3. t = Thread(target=函数名)
  4. t_list.append(t)
  5. t.start()
  6. for t in t_list:
  7. t.join()

除了使用该模块外,您也可以使用  Thread  线程类来创建多线程。

在处理线程的过程中要时刻注意线程的同步问题,即多个线程不能操作同一个数据,否则会造成数据的不确定性。通过 threading 模块的 Lock 对象能够保证数据的正确性。

比如,使用多线程将抓取数据写入磁盘文件,此时,就要对执行写入操作的线程加锁,这样才能够避免写入的数据被覆盖。当线程执行完写操作后会主动释放锁,继续让其他线程去获取锁,周而复始,直到所有写操作执行完毕。具体方法如下所示:

  1. from threading import Lock
  2. lock = Lock()
  3. 获取锁

  4. lock.acquire()
  5. wirter.writerows(“线程锁问题解决”)
  6. 释放锁

  7. lock.release()

Queue队列模型

对于 Python 多线程而言,由于 GIL 全局解释器锁的存在,同一时刻只允许一个线程占据解释器执行程序,当此线程遇到 IO 操作时就会主动让出解释器,让其他处于等待状态的线程去获取解释器来执行程序,而该线程则回到等待状态,这主要是通过线程的调度机制实现的。

由于上述原因,我们需要构建一个多线程共享数据的模型,让所有线程都到该模型中获取数据。queue(队列,先进先出) 模块提供了创建共享数据的队列模型。比如,把所有待爬取的 URL 地址放入队列中,每个线程都到这个队列中去提取 URL。queue 模块的具体使用方法如下:

  1. 导入模块

  2. from queue import Queue
  3. q = Queue() #创界队列对象
  4. q.put(url) 向队列中添加爬取一个url链接
  5. q.get() # 获取一个url,当队列为空时,阻塞
  6. q.empty() # 判断队列是否为空,True/False

多线程爬虫案例

下面通过多线程方法抓取小米应用商店(https://app.mi.com/)中应用分类一栏,所有类别下的 APP 的名称、所属类别以及下载详情页 URL 。如下图所示:

image.png
图1:小米应用商城

抓取下来的数据 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 通过查看页面元素,如下所示

    • 游戏
    • 实用工具
    • 影音视听
    • 聊天社交
    • 图书阅读
    • 学习教育
    • 效率办公
    • 时尚购物
    • 居家生活
    • 旅行交通
    • 摄影摄像
    • 医疗健康
    • 体育运动
    • 新闻资讯
    • 娱乐消遣
    • 金融理财

    因此,可以使用 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) 完整程序

    完整程序如下所示:

    1. -- coding:utf8 --

    2. import requests
    3. from threading import Thread
    4. from queue import Queue
    5. import time
    6. from fake_useragent import UserAgent
    7. from lxml import etree
    8. import csv
    9. from threading import Lock
    10. import json
    11. class XiaomiSpider(object):
    12. def init(self):
    13. self.url = ‘http://app.mi.com/categotyAllListApi?page={}&categoryId={}&pageSize=30’
    14. 存放所有URL地址的队列

    15. self.q = Queue()
    16. self.i = 0
    17. 存放所有类型id的空列表

    18. self.id_list = []
    19. 打开文件

    20. self.f = open(‘XiaomiShangcheng.csv’,‘a’,encoding=‘utf-8’)
    21. self.writer = csv.writer(self.f)
    22. 创建锁

    23. self.lock = Lock()
    24. def get_cateid(self):
    25. 请求

    26. url = ‘http://app.mi.com/’
    27. headers = { ‘User-Agent’: UserAgent().random}
    28. html = requests.get(url=url,headers=headers).text
    29. 解析

    30. parse_html = etree.HTML(html)
    31. xpath_bds = ‘//ul[@class=“category-list”]/li’
    32. li_list = parse_html.xpath(xpath_bds)
    33. for li in li_list:
    34. typ_name = li.xpath(‘./a/text()’)[0]
    35. typ_id = li.xpath(‘./a/@href’)[0].split(‘/’)[-1]
    36. 计算每个类型的页数

    37. pages = self.get_pages(typ_id)
    38. #往列表中添加二元组
    39. self.id_list.append( (typ_id,pages) )
    40. 入队列

    41. self.url_in()
    42. 获取count的值并计算页数

    43. def get_pages(self,typ_id):
    44. 获取count的值,即app总数

    45. url = self.url.format(0,typ_id)
    46. html = requests.get(
    47. url=url,
    48. headers={‘User-Agent’:UserAgent().random}
    49. ).json()
    50. count = html[‘count’]
    51. pages = int(count) // 30 + 1
    52. return pages
    53. url入队函数,拼接url,并将url加入队列

    54. def url_in(self):
    55. for id in self.id_list:
    56. id格式:(‘4’,pages)

    57. for page in range(1,id[1]+1):
    58. url = self.url.format(page,id[0])
    59. 把URL地址入队列

    60. self.q.put(url)
    61. 线程事件函数: get() -请求-解析-处理数据,三步骤

    62. def get_data(self):
    63. while True:
    64. 判断队列不为空则执行,否则终止

    65. if not self.q.empty():
    66. url = self.q.get()
    67. headers = {‘User-Agent’:UserAgent().random}
    68. html = requests.get(url=url,headers=headers)
    69. res_html = html.content.decode(encoding=‘utf-8’)
    70. html=json.loads(res_html)
    71. self.parse_html(html)
    72. else:
    73. break
    74. 解析函数

    75. def parse_html(self,html):
    76. 写入到csv文件

    77. app_list = []
    78. for app in html[‘data’]:
    79. app名称 + 分类 + 详情链接

    80. name = app[‘displayName’]
    81. link = ‘http://app.mi.com/details?id=’ + app[‘packageName’]
    82. typ_name = app[‘level1CategoryName’]
    83. 把每一条数据放到app_list中,并通过writerows()实现多行写入

    84. app_list.append([name,typ_name,link])
    85. print(name,typ_name)
    86. self.i += 1
    87. 向CSV文件中写入数据

    88. self.lock.acquire()
    89. self.writer.writerows(app_list)
    90. self.lock.release()
    91. 入口函数

    92. def main(self):
    93. URL入队列

    94. self.get_cateid()
    95. t_list = []
    96. 创建多线程

    97. for i in range(1):
    98. t = Thread(target=self.get_data)
    99. t_list.append(t)
    100. 启动线程

    101. t.start()
    102. for t in t_list:
    103. 回收线程

    104. t.join()
    105. self.f.close()
    106. print(‘数量:’,self.i)
    107. if name == ‘main’:
    108. start = time.time()
    109. spider = XiaomiSpider()
    110. spider.main()
    111. end = time.time()
    112. 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全套资料】 即可获取。

    image.png

    👉Python必备开发工具👈

    image.png

    温馨提示:篇幅有限,已打包文件夹获取方式在:点击这里【 Python全套资料】 即可获取。

    👉Python学习视频600合集👈

    image.png
    观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

    👉实战案例👈

    image.png

    光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

    👉100道Python练习题👈检查学习结果。

    image.png

    👉面试刷题👈

    image.png

    温馨提示:篇幅有限,已打包文件夹获取方式在:点击这里【 Python全套资料】 即可获取。

    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

    Python的多线程爬虫详解 的相关文章

    • 滴水石穿

      不积跬步 xff0c 无以至千里 xff1b 不积小流 xff0c 无以成江海 1 hashcode相等两个类一定相等吗 equals呢 相反呢 hashcode相等 xff0c 两个类不一定相等 xff1b equals相等 xff0c
    • CAD批量打图精灵功能列表

      功能简介功能细分识别图框识别直线 多段线 二维多段线 三维多段线 面域 视口 代理实体 块参照 外部参照单图模式 xff0c 识别整个图形的边界 xff0c 适用于模型或布局只有一张图的情况多图模式 xff0c 识别矩形或无矩形块边界标准
    • Linux下VirtualBox虚拟机的命令行启动/关闭方法和开机自动启动

      SUN VirtualBox 的命令行启动 关闭方法简介 VirtualBox 详细命令 linux开机自动启动虚拟机系统 当你安装很多套Virtualbox的虚拟机器系统后 xff0c 希望能在开机后自动启动虚拟机器的系统 开启记事本 x
    • NSIS制作安装软件过程

      目录 1 工具介绍 1 1 界面设计用 xff0d xff0d NSIS Dialog Designer 1 2 编辑及向导 xff0d xff0d nisedit2 0 3 1 3 控件信息查看 Au3Info exe 2 脚本的结构 1
    • 欧拉题目收集

      nbsp https www oschina net group kunpeng 赛题6 容器网络可视化 赛题类别 操作系统 nbsp 赛题难度 中 nbsp 赛题描述 容器场景的微服务运维已经在向可视化方式演进 可视化运维中最大的内容是A
    • ASP.NET Core Blazor: 两种IJSRuntime依赖注入的方式

      1 xff09 将 IJSRuntime 抽象注入Razor组件或者页面 razor 中 xff1a public partial class ToolsWidget Inject private IJSRuntime JSRuntime
    • x86 smbus 下挂eeprom不能写问题

      目录 背景 分析 驱动影响 SPD register 接口 只读 修改验证 总结 背景 x86 smbus上下挂一个eeprom xff0c 只能读取 xff0c 不能写入 写入命令采用 xff1a i2cset y f 0 0x50 0
    • ubuntu编译rk3588异常

      问题现象 在ubuntu上编译 rk3588 的kernel时 xff0c 报如下错误 xff1a LZ4C arch arm64 boot Image lz4 Incorrect parameters Usage lz4 arg inpu
    • linux rs485功能增加

      目录 串口驱动层级结构 485配置流程 dts相关 配置注册 初始化 485收发切换 delay after send 目前linux 内核中已经支持了485的实现 xff0c 但由于底层驱动的支持情况 xff0c 导致我们采用不同芯片时需
    • intel I2C的速率配置

      目录 寄存器篇 修改寄存器 intel I2C 驱动结构 lpss pci文件 lpss文件 驱动结构 Synopsys DesignWare I2C BIOS配置修改 ACPI表的查看 I2C速率 寄存器篇 修改速率很简单 xff0c 看
    • spark 例子运行- spark pi

      原计划用cygwin来运行linux的脚本 xff0c 进行测试 但是在实际运行过程中 xff0c 出现java cp x jar class xff0c 总是失败的问题 xff0c 一直没有解决 xff0c 因而 xff0c 直接用win
    • sas控制器驱动之设备管理

      本文以2 6 32 68内核中的mpt2sas为例子 xff0c 介绍了sas驱动的设备管理 1 基本结构 内核中scsi的结构分三层 xff0c 此在网上已有大量资料 xff0c 不再赘述 本文在此基础上增加了mid layer的 tra
    • 主成分分析、聚类分析、因子分析的基本思想及优缺点

      点击打开链接
    • pyqt5之menu和action

      exitAct 61 QAction QIcon 39 exit png 39 39 amp Exit 39 self exitAct setShortcut 39 Ctrl 43 Q 39 exitAct setStatusTip 39
    • 如何获取控件所在的坐标位置

      窗口的坐标体系及接口 获取坐标的接口在Widget类中 xff0c 即控件的坐标信息属于基类的成员 基本的坐标体系如图所示 通过接口打印出 lable 3的坐标值 print self label 3 geometry x print se
    • 【转载+修改】Gnome菜单项与文件打开方式(文件关联)的更改

      转载自 xff1a http hi baidu com red woods blog item 30a5f845a2247f24cffca397 html 转载有修改 xff01 在KDE中我们可以使用系统设置中提供的设置进行文件关联的修改
    • Kali Linux从零基础入门到精通,看完这一篇就够了。

      1 目录 基于Android设备的Kali Linux渗透测试教程基于Android设备的Kali Linux渗透测试教程2Web渗透测试使用kali linuxkali linux中文指南kali linux wireless pente
    • 客户机无法通过mstsc连接到远程主机的解决方法

      客户机无法通过 mstsc 连接到远程主机的解决方法 症状 当通过 mstsc 命令进行连接时 系统提示 客户端无法连接远程计算机 xff1b 连接可能没有启用 xff0c 或者计算机太忙 xff0c 无法接受新连接 也可能网络问题使您无法
    • 五个同步问题的经典模型之一:生产者/消费者问题

      也叫缓存绑定问题 xff08 bounded buffer xff09 xff0c 是一个经典的 多进程同步问题 单生产者和单消费者 有两个进程 xff1a 一组生产者进程和一组消费者进程共享一个初始为空 固定大小为n的缓存 xff08 缓
    • Android 以太网/有线网Ethernet功能开发

      1 功能介绍 以太网的功能是允许设备提供硬件接口通过插入网线的形式访问互联网的功能 接入网线之后 xff0c 设备可以动态的获取 IP xff0c DNS xff0c Gateway等一系列网络属性 xff0c 我们也可以手动配置设备的网络

    随机推荐

    • 解决www.54kk.com/baidu劫持浏览器的问题

      endurer 原创 2005 10 27第一版 endurer注 xff1a 为了安全起见 xff0c 下文中的 http 均用 hxxp 代替 刚才一位同事的电脑中的浏览器被恶意网站劫持了 xff0c 请我帮忙处理 同事的电脑使用的是
    • Automatic Login

      sudo vim etc gdm custom confAdd the following lines to the field daemon AutomaticLoginEnable 61 true AutomaticLogin 61 i
    • Java Annotation手册

      版权声明 xff1a 本文可以自由转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本声明 作者 cleverpig 作者的Blog http blog matrix org cn page cleverpig 原文 h
    • shell 函数 入参说明

      1 入参个数 2 入参 0 脚本名 1第一个参数 3 64 和 xff1a 34 64 34 34 34 都是所有入参 64 将入参变成一个数组 将入参变成一个字符串 4 数组作为入参 fucn2 arr xff0c 函数内部获取入参数组
    • 程序媛工作几年后的感受!体验?

      黑客技术 点击右侧关注 xff0c 了解黑客的世界 xff01 Java开发进阶 点击右侧关注 xff0c 掌握进阶之路 xff01 Python开发 点击右侧关注 xff0c 探讨技术话题 xff01 作者 xff1a hq nuan 来
    • TIOBE 5月编程语言榜单出炉,C#最受开发者欢迎,C++将冲击Top 3

      x1f447 x1f447 关注后回复 进群 xff0c 拉你进程序员交流群 x1f447 x1f447 TIOBE Index for May 2022 和 4 月相比 xff0c 本月编程语言 Top 10 并没有明显的位置变化 xff
    • crontab 定时任务避免重复执行

      使用crontab设置一个脚本每个一段时间自动执行一次 xff0c 当脚本的执行时间超过crontab设置的时间间隔 xff0c 那个脚本就会在同一时刻同时执行 比如设置crontab每隔五分钟执行一次task sh xff1a span
    • ubuntu 通过 apt-get 安装软件失败时的解决方案

      最近在 vmware上的ubuntu系统下安装 软件时出现安装失败情况 xff0c 在网上搜了一通 xff0c 终于找到了解决方案 遇到的问题和解决方案如下 xff1a 一 apt get install vim二 apt get upda
    • Spring注解处理机制

      前言 众所周知 xff0c spring 从 2 5 版本以后开始支持使用注解代替繁琐的 xml 配置 xff0c 到了 springboot 更是全面拥抱了注解式配置 平时在使用的时候 xff0c 点开一些常见的等注解 xff0c 会发现
    • 解决SpringBoot使用时类找不到问题

      解决方案 第一步 xff1a 勾选这个选项 第二步 xff0c 在pom xml中添加以下代码 lt resources gt lt resource gt lt directory gt src main resources lt dir
    • java设计模式之建造者模式(Builder Pattern)

      目的 xff1a 将产品与产品的创建过程解耦 他是按照相应的步骤来构建产品 下面看一下UML序列图 对于序列图的一个解释 下面来上一个标准代码 Product java package com pxx public class Produc
    • 如何在 Github Pages 搭建库(创建免费域名)来管理和浏览自己的项目

      看了 这篇文章 你能学会 两大技能 如何在 Github Pages 上搭建库来管理自己的项目你能访问你的项目 就像访问域名一样 查看自己做的网页 说明 像我们学前端的朋友 xff0c 好不容易做好一个很炫的网页 xff0c 没法放在网站上
    • vim 快捷键修改

      ubuntu默认的vim确实不好用 xff0c 但它最强大的地方在于可修改的配置文件 xff0c 以及专门为vim所开发的vimscript脚本语言 后者暂时不用学习 xff0c 先来研究一下配置文件 vimrc 是控制 vim 行为的配置
    • Excel合并计算和分类汇总

      一 实现合并计算 合并计算主要实现将几个分开的表格按照需求的函数功能计算到一个表中 xff1a 1 分类合并 将下面三个城市的销售额分类合并到一个表当中 xff08 这里的销售额必须指明地区 xff0c 不然合并计算时会统计求和 xff09
    • 使用连接池方式和多线程方式连接mysql的测试说明

      前面文章讨论了mysql做高可用的配置 xff0c 参考文章链接 xff0c 而本文则是开发项目过程需要用的部分 xff0c 从配置数据库到实用数据库 xff0c 以及再用SQL做BI分析再到SQL优化 xff0c 这些都是全栈工程师的基本
    • Python中的图形绘制-Matplotlib简单动画制作

      Matplotlib 是一个非常广泛的库 xff0c 它也支持图形动画 动画工具以 matplotlib animation 基类为中心 xff0c 它提供了一个框架 xff0c 围绕该框架构建动画功能 主要接口有TimedAnimatio
    • 明面上是个歌手!暗地里是个程序员的明星你只知道许嵩和潘玮柏?

      在5月9日 xff0c 知名演员刘涛在社交平台发文公布 xff1a 已正式入职聚划算成官方优选官了 xff0c 而且还有花名叫刘一刀 xff0c 以后就专职给大家挑好物了 当然 xff0c 刘涛在5 14号已经开始上班了 xff0c 还邀请
    • 教你搭建FTP文件共享服务器

      一 什么是FTP FTP 文件传输协议 xff08 File Transfer Protocol xff0c FTP xff09 是用于在网络上进行文件传输的一套标准协议 xff0c 它工作在 OSI 模型的第七层 xff0c TCP 模型
    • 田忌赛马 - 去哪儿2018校招哈尔滨在线笔试题 - 开发工程师

      时间限制 xff1a C C 43 43 语言 1000MS xff1b 其他语言 3000MS 内存限制 xff1a C C 43 43 语言 65536KB xff1b 其他语言 589824KB 题目描述 xff1a 田忌和齐王赛马
    • Python的多线程爬虫详解

      多线程使用流程 Python 提供了两个支持多线程的模块 xff0c 分别是 thread 和 threading 其中 thread 模块偏底层 xff0c 它相比于 threading 模块功能有限 xff0c 因此推荐大家使用 thr