爬虫实战05--python爬虫爬取糗事百科标题,多进程--面向对象版本(3_3)(面向对象,多进程面向对象,多进程面向对象) (3)

0.0说明:

这一个爬取的网站是糗事百科的页面去全部标题!最后是以打印的方式展现的!
这是第二篇文章,多进程的爬取糗事百科全部标题。一共是有三个版本,我一共会发三篇文章!

第一篇:前篇,正常爬取 糗事百科全部标题
第二篇:上篇,多线程的爬取糗事百科全部标题
第三篇:本篇,多进程的爬取糗事百科全部标题

为啥有多线程还要用多进程?

前面这种方式由于GIL全局锁的存在,多线程在python3下可能只是个摆设,对应的解释器执行其中的内容的时候仅仅是顺序执行,此时我们可以考虑多进程的方式实现,思路和多线程相似,只是对应的api不相同。

01、运行环境

# 操作系统:win10 专业版
pycharm professional 2019.1
python 3.8
lxml == 4.5.1
requests == 2.23.0
xpath == 2.0.2

02、开始爬虫

02-1、目标

糗事百科的页面全部标题!最后以打印的方式展现!
地址:

# 页面一共是1-13页
https://www.qiushibaike.com/8hr/page/1/
......
https://www.qiushibaike.com/8hr/page/13/

02-2、开始抓取内容

02-2-1、分析

在这里插入图片描述
图片中的数据就是我们需要获取的。用xpath这一个获取到了全部我们需要的!
对于url地址就是1----13这一个简单处理,其他都是简单的爬虫流程,不再赘述!具体看代码的注释吧!
再看一哈思路!

思路:
1.创建数据容器对象
URL容器
HTML容器
数据容器
2.实现处理任务函数
实现生产URL任务
实现消费URL生产HTML任务
实现消费HTML生产数据任务
实现消费数据任务
3.让任务函数运行在进程上,并以守护进程的形式运行

4.主进程退出条件

5.调整进程数量实现并行执行

02-2-2、线程改成进程

由于整体思路不变,仅仅只是原来运行在线程上的代码需要运行在进程上所以仅需要线程修改成进程即可

# from threading import Thread  修改
from multiprocessing import Process

02-2-3、Queue 改成 JoinableQueue

由于进程间无法共享一个变量,所以需要把队列改编成 JoinableQueue

JoinableQueue队列上层使用Queue 相同的接口,底层使用进程间通讯。

# from queue import Queue
from multiprocessing import JoinableQueue as Queue
# 这里用了as Queue,我们就可以在后面少改变很多东西

02-2-4、设置守护进程

守护线程和守护进程的设置方式不同

task.setDaemon(True)
task.daemon = True

我们直接写代码了!

03、完整代码(多线程面向对象版)

from multiprocessing import Process
from lxml import etree
import requests
import time
# 多进程生产者消费者队列
from multiprocessing import JoinableQueue as Queue
# Queue().task_done() 任务数量减一
# Queue().join()      阻塞,直到队列中的所有项都被获取和处理

class QiushiMultiprocess(object):
	"""糗事百科多进程爬虫"""
	
    def __init__(self):
    	# 构建请求头
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
        }
		
		# 构建基础的url地址
        self.baseurl = "https://www.qiushibaike.com/8hr/page/{}/"

        # 创建url队列容器
        self.url_queue = Queue()

        # 创建html队列容器
        self.html_queue = Queue()

        # 创建数据队列容器
        self.data_queue = Queue()

    # 1.构建url地址
    def get_urls(self):
    	"""
        :return: 目标url列表
        """
        for page in range(1,14):
            url = self.baseurl.format(page)
            # 把获取到url添加到url_queue队列里面
            self.url_queue.put(url)
	
	# 2.发送请求获取数据
    def send_requests(self):
    	# 循环的取出url,发送请求,直到任务全部完成
        # 完成标志:Thread().unfinished_task 值为0
        
        while True:
            # 从url_queue队列里面获取到url元素
            url = self.url_queue.get()
            print(f'获取到的url地址{url}')
			
			# 发送请求获取html页面数据--网络延迟
            response = requests.get(url=url,headers=self.headers)
            html = response.content.decode()

            # 把获取到的页面添加到html_queue队列,队列任务+1
            self.html_queue.put(html)
            # url_queue队列的这一个任务,完成,-1
            self.url_queue.task_done()

	# 3.从响应中提取数据
    def get_html(self):
        while True:
            # 从html_queue队列中获取html
            html = self.html_queue.get()
			
			# 创建etree对象
            reshtml = etree.HTML(html)
            # 使用xpath规则提取数据
            restitle = reshtml.xpath("//li//a[@class='recmd-content']/text()")

            # 吧获取到title数据添加到data_queue队列,队列任务数量+1
            self.data_queue.put(restitle)
            # 这一个html任务完成,任务数量-1
            self.html_queue.task_done()

    def save_data(self):
        while True:
            # data_queue队列中获取数据
            title_data = self.data_queue.get()
            for title in title_data:
                print(title)
            # data_queue数据队列任务完成
            self.data_queue.task_done()

	# 运行多线程爬虫
    def run(self):

        # 进程列表
        process_list = []

        # 创建get_url进程,并且添加到process_list进程列表
        url_process = Process(target=self.get_urls)
        process_list.append(url_process)

        # 创建发送请求send_requests进程,并且添加到process_list进程列表
        request_process = Process(target=self.send_requests)
        process_list.append(request_process)

        # 创建获取get_html进程,并且添加到process_list进程列表
        html_process = Process(target=self.get_html)
        process_list.append(html_process)

        # 创建保存数据的save_data进程,并且添加到process_list进程列表
        save_process = Process(target=self.save_data)
        process_list.append(save_process)

        for procees in process_list:
            # 设置进程守护
            procees.daemon = True
            # 启动子进程
            procees.start()

        # 睡眠,防止太快!
        time.sleep(2)
		
		# # 同时让队列中所有任务执行完毕后才结束程序
        for queue in [self.url_queue,self.html_queue,self.data_queue]:
        	# 如果unfinished_task不为0的时,处于队列就等待状态,等待任务执行,
            # 而每个任务函数中里面都有task_done方法[任务数量-1],任务数量为0时,程序继续执行
            queue.join()
        print('主进程结束!')


if __name__ == '__main__':
    obj1 = QiushiMultiprocess()
    obj1.run()

最后获取的信息是以打印的方式展现的!
上述多进程实现的代码中,multiprocessing 提供的JoinableQueue 可以创建可连接的共享进程队列。和普通的Queue 对象一样,队列允许项目的使用者通知生产者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。 对应的该队列能够和普通队列一样能够调用task_donejoin 方法

04、结语:

个人记录,新手入门,多多学习,欢迎大家交流探讨!
个人网站: http://106.54.78.238/
song_of _sea的个人网站 http://106.54.78.238/

猜你喜欢

转载自blog.csdn.net/weixin_44824717/article/details/108158231