爬虫第六篇

爬虫第六篇

python采用 多进程/多线程/协程 写爬虫

从操作系统的角度:

进程和线程,都是一种CPU的执行单元。

进程:表示一个程序的上下文执行活动(打开、执行、保存…)

线程:进程执行程序时候的最小调度单位(执行a,执行b…)

一个程序至少有一个进程,一个进程至少有一个线程。

并行:多个CPU核心,不同的程序就分配给不同的CPU来运行。可以让多个程序同时执行

并发:单个CPU核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,需要在多个程序间来回切换

多进程/多线程:表示可以同时执行多个任务,进程和线程的调度是由操作系统自动完成。

进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。
进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。

线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。

共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。

一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间。

互斥锁:一种安全有序的让多个线程访问内存空间的机制。

Python的多线程:

GIL 全局解释器锁:线程的执行权限,在Python的进程里只有一个GIL。

一个线程需要执行任务,必须获取GIL。

好处:直接杜绝了多个线程访问内存空间的安全问题。
坏处:Python的多线程不是真正多线程,不能充分利用多核CPU的资源。

但是,在I/O阻塞的时候,解释器会释放GIL。

所以:

多进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。 multiprocessing
缺陷:多个进程之间通信成本高,切换开销大。

多线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
threading.Thread、multiprocessing.dummy
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

协程:又称微线程,在单线程上执行多个任务,用函数切换,开销极小。不通过操作系统调度,没有进程、线程的切换开销。genvent,monkey.patchall

多线程请求返回是无序的,那个线程有数据返回就处理那个线程,而协程返回的数据是有序的。

缺陷:单线程执行,处理密集CPU和本地磁盘IO的时候,性能较低。处理网络I/O性能还是比较高.

多线程多进程爬虫

from multiprocessing import Process, Queue

# 多进程
p = Process(target=func, name, args, kwargs)

# 队列
q = Queue()
q.put(url)
q.get()
q.empty()
多进程/多线程爬虫的流程图

在这里插入图片描述

import requests
from multiprocessing import Queue
from threading import Thread
from lxml import etree
import time 

class XiaomiSpider:
    def __init__(self):
        self.baseurl = 'http://app.mi.com/category/12#page='
        self.mainurl = 'http://app.mi.com'
        self.headers = {"User-Agent":"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)"}
        # URL队列
        self.urlQueue = Queue()
        # 解析队列
        self.parseQueue = Queue()
    
    # URL入队列
    def getUrl(self):
        for page in range(20):
            url = self.baseurl + str(page)
            # 把拼接的url放到url队列中
            self.urlQueue.put(url)

    # 采集线程函数,get出URL发请求,把html给解析队列
    def getHtml(self):
        while True:
            # 先判断队列是否为空
            if not self.urlQueue.empty():
                url = self.urlQueue.get()
                # 三步走
                res = requests.get(url,headers=self.headers)
                res.encoding = "utf-8"
                html = res.text
                # 把html放到解析队列
                self.parseQueue.put(html)
            else:
                break
            
    # 解析线程函数,get出html源码,提取并处理数据
    def getData(self):
        while True:
            if not self.parseQueue.empty():
                html = self.parseQueue.get()
                # 创建解析对象,调用xpath
                parseHtml = etree.HTML(html)
                # [li1对象,li2对象]
                baseList = parseHtml.xpath('//ul[@id="all-applist"]//li')
                for base in baseList:
                    # 应用名称
                    name = base.xpath('./h5/a/text()')[0]
                    # 应用链接
                    link = self.mainurl + base.xpath('./h5/a/@href')[0]
                    d = {
                         "分类":"学习教育",
                         "名称":name,
                         "链接":link,
                      }
                    with open("XM.json","a", encoding="utf-8") as f:
                        f.write(str(d)+'\n')    
            else:
                break
    # 主函数
    def workOn(self):
        # url先入队列
        self.getUrl()
        # 存放所有采集线程对象和解析线程对象列表
        tList = []

        # 统一创建线程,采集和解析一起干活
        for i in range(5):
            t1 = Thread(target=self.getHtml)
            t2 = Thread(target=self.getData)
            tList.append(t1)
            tList.append(t2)
            t1.start()
            t2.start()
            
        # 统一回收采集线程和解析线程
        for i in tList:
            i.join()
        
if __name__ == "__main__":
    start = time.time()
    spider = XiaomiSpider()
    spider.workOn()
    end = time.time()
    print("执行时间:%.2f" % (end - start))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值