博客
关于我
python爬虫:多线程的简单示例与应用
阅读量:121 次
发布时间:2019-02-27

本文共 2453 字,大约阅读时间需要 8 分钟。

前言

Python的标准库提供了两个模块:_thread和threading。_thread是低级模块,threading是高级模板,对_thread进行了封装。在绝大多数情况下,我们只需使用threading这个高级模块即可。启动线程的方法是将函数传入并创建Thread实例,然后调用start()方法开始执行。

虽然Python的多线程机制受到GIL(全局解锁机制)的限制,并不是真正的并行执行,但在I/O密集型任务中仍能显著提高效率,例如在爬虫操作中。以下将通过一个实例对比多线程与单线程的效率差异。

实例

以下代码用于验证多线程的效率。该示例仅涉及页面获取,不包含解析功能。

```python# -*-coding:utf-8 -*-import requestsimport timeimport threading

class MyThread(threading.Thread):def init(self, func, args):threading.Thread.init(self)self.args = argsself.func = func

def run(self):    self.func(self.args[0])

def open_url(url):response = requests.get(url).textprint(len(response))return response

def normal_method(urlList):start_time = time.time()for each in urlList:open_url(each)end_time = time.time()print('正常方式耗时 %s 秒' % (end_time - start_time))

def thread_method(urlList):start_time = time.time()threadList = [MyThread(open_url, (url,)) for url in urlList]for t in threadList:t.setDaemon(True)t.start()for t in threadList:t.join()end_time = time.time()print('多线程方式耗时 %s 秒' % (end_time - start_time))

if name == 'main':urlList = []for p in range(1, 10):urlList.append('https://so.gushiwen.org/authors/authorvsw.aspx?page=' + str(p) + '&id=9cb3b7c0e4a0')normal_method(urlList)thread_method(urlList)

通过以上代码可以看到,单线程处理需要约1.8秒,而多线程处理仅需0.2秒。多线程在I/O密集型任务中确实能显著提高效率。

多线程简介

多线程编程的典型应用之一是消费者-生产者模式(可参考百度等资源)。以下将介绍两种常见的多线程方式:面向过程和面向对象。

在实际编程中,面向对象的方式可以简化代码编写,但面向过程的方式在某些场景下可能更高效。以下将介绍面向过程的多线程爬虫模板。

面向过程的多线程爬虫模板

思路

该模板的主要思路是将每个URL单独作为一个任务提交给线程池(threadpool),然后等待所有任务完成。具体步骤如下:

  1. 构造一个包含多个URL的列表。
  2. 根据列表中的每个URL创建一个线程。
  3. 启动所有线程并等待它们完成。

代码

```python# -*-coding:utf-8 -*-import requestsimport timeimport threadingdef open_url(url): response = requests.get(url).text print(len(response)) return responsedef doSpyder(urlList): start_time = time.time() threadList = [threading.Thread(target=open_url, args=(url,)) for url in urlList] for t in threadList: t.setDaemon(True) t.start() for t in threadList: t.join() end_time = time.time() print('多线程方式耗时 %s 秒' % (end_time - start_time))if __name__ == '__main__': urlList = [] for p in range(1, 10): urlList.append('https://so.gushiwen.org/authors/authorvsw.aspx?page=' + str(p) + '&id=9cb3b7c0e4a0') doSpyder(urlList)

在该代码中,每个URL都被单独作为一个线程处理,避免了多线程同时访问同一数据结构的问题。

一个误区

需要注意的是,在创建多线程时,每个网页都应单独创建一个线程。如果直接使用已有的线程对列表进行操作,可能会导致数据重复或不一致的问题(即脏数据问题)。这是因为列表是非线程安全的数据结构。

此外,使用锁来保护数据在多线程环境中是必要的,但过度使用锁会导致效率下降。建议在多线程编程中尽量减少锁的使用,以免影响性能。

转载地址:http://ehef.baihongyu.com/

你可能感兴趣的文章
Objective-C实现karger算法(附完整源码)
查看>>
Objective-C实现KMP搜索算法(附完整源码)
查看>>
Objective-C实现Knapsack problem背包问题算法(附完整源码)
查看>>
Objective-C实现knapsack背包问题算法(附完整源码)
查看>>
Objective-C实现knapsack背包问题算法(附完整源码)
查看>>
Objective-C实现knight tour骑士之旅算法(附完整源码)
查看>>
Objective-C实现knight Tour骑士之旅算法(附完整源码)
查看>>
Objective-C实现KNN算法(附完整源码)
查看>>
Objective-C实现KNN算法(附完整源码)
查看>>
Objective-C实现KNN算法(附完整源码)
查看>>
Objective-C实现knuth morris pratt(KMP)算法(附完整源码)
查看>>
Objective-C实现knuth-morris-pratt(KMP)算法(附完整源码)
查看>>
Objective-C实现Koch snowflake科赫雪花曲线算法(附完整源码)
查看>>
Objective-C实现koch snowflake科赫雪花算法(附完整源码)
查看>>
Objective-C实现KPCA(附完整源码)
查看>>
Objective-C实现KruskalMST最小生成树的算法(附完整源码)
查看>>
Objective-C实现kruskal克鲁斯卡尔算法(附完整源码)
查看>>
Objective-C实现kth order statistick阶统计量算法(附完整源码)
查看>>
Objective-C实现lamberts ellipsoidal distance朗伯椭球距离算法(附完整源码)
查看>>
Objective-C实现largest AdjacentNumber最大相邻数算法 (附完整源码)
查看>>