多线程requests爬虫
为了提高爬虫程序效率,由于python解释器GIL,导致同一进程中即使有多个线程,实际上也只会有一个线程在运行,但通过request.get发送请求获取响应时有阻塞,所以采用了多线程依然可以提高爬虫效率:
单线程爬虫思路
先来个单线程的思路图,针对规则url地址的网站:
多线程爬虫思路
待过两天有空再画个多线程的,先mark一下下拉。
多线程爬虫注意点
解耦
- 整个程序分为4部分,url list模块、发送请求,获取响应模块、数据提取模块、保存模块,如果某一模块出现问题,互相之间不会影响。
规则url
- 本文程序针对规则url,比如贴吧、内涵段子、糗事百科等,其他不规则url网址,在程序结构上只需要修改url部分即可。
资源竞争
- 由于使用了多线程,不同线程在共享数据时,容易产生资源竞争,假设共享数据放入列表中,那么同一时刻有可能2个线程去列表中取同一个数据,重复使用。
- 解决办法是使用队列,使得某一线程get数据时,其他线程无法get同一数据,真正起到保护作用,类似互斥锁。
- 下面以url列表为例对队列进行说明(只列举部分代码,全代码在最下方):
import requests
import queue import Queue
pass
class Spider_s:
def __init__(self):
self.url_regular = pass
self.headers = pass
self.myurl_queue = Queue()
pass
def myurl_list(self):
for i in range(100): #构造100个规则rul地址
self.myurl_queue.put(self.url_regular.format(i+1)) #把100个url地址放入队列中
pass- 队列常用方法介绍
- 首先from queue import Queue (q=Queue()):
| 方法 | 说明 |
|---|---|
| q.empty() | 判断队列q是否为空,返回True或False |
| q.full() | 判断队列q是否满了,返回True或False |
| q.qsize() | 获取队列中保存数据的个数 |
| 方法 | 说明 | 注意点 |
|---|---|---|
| q.get() | 从队列中取一个数据 | |
| q.get_nowait() | 不等待直接取 |
注意:get和get_nowait两者的区别是当队列取完了即队列为空时,get()会阻塞,等待着新数据继续取,而get_nowait()会报错.
| 方法 | 说明 |
|---|---|
| q.put() | 把数据放入队列 |
| q.put_nowait() | 不等待直接取 |
注意:put和put_nowait 两者的区别是当队列为满时,put_nowait()会报错.
队列重点方法join task_done注意点
- 重点注意q.join()方法,在python3中,join()会等待子线程、子进程结束之后,主线程、主进程才会结束.
- 重点注意task_done()的使用:
- 队列中put,队列计数会+1,get时计数不会减1,但当get+task_done时,队列计数才会减1,如果没有task_done则程序跑到最后不会终止哟.
- 注意task_done()的位置,应该放在方法的最后以保证所有任务全部完成.
setDaemon方法
把子线程设置为守护线程,即认为该方法不是很重要,记住主线程结束,则该子线程结束- join方法和setDaemon方法搭配使用
主线程进行到join()处,join的效果是让主线程阻塞,等待子线程中队列任务完成之后再解阻塞,等子线程结束,join效果失效,之后主线程结束,由于使用了setDaemon(True),所以子线程跟着结束,此时整个程序结束。
完整代码
- 本文以xxx为例,完整代码如下:下次补上啦啦啦啦
版权声明:本文为qq_22043649原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。