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

0.0说明:

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

第一篇:上篇,正常爬取 糗事百科所有标题
第二篇:本篇,多线程的爬取糗事百科所有标题
第三篇:下篇,多进程的爬取糗事百科所有标题python

0一、运行环境

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

0二、开始爬虫

02-一、目标

糗事百科的页面所有标题!最后以打印的方式展示!
地址:web

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

02-二、开始抓取内容

02-2-一、分析

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

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

4.主线程退出条件app

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

“”"函数

02-三、其余知识

02-3-一、队列模块的使用

# put的时候计数+1,get不会-1,get须要和task_done 一块儿使用才会-1

from queue import Queue
# 构建队列
# maxsize=100 最大容量100
q = Queue(maxsize=100)

item = { 
 
  }
# 把数据放入到队列中,若是队列数据已满,就进行等待直到队列不满时把数据添加到队列中
q.put(item)
# 把数据放入到队列中,若是队列数据已满,就不进行等待,引起异常
q.put_nowait(item)  

# 把数据从队列中取出,若是队列为空,就进行等待直到队列不为空,返回数据
q.get()
# 把数据从队列中取出,若是队列为空,就不进行等待,引起异常
q.get_nowait()

# 获取队列中元素个数
q.qsize()
# 获取未完成的任务数
q.unfinished_tasks

# 让 unfinished_tasks 属性 -1
q.task_done()

# 当前程序进行等待状态,直到队列的 unfinished_tasks 属性为 0 时继续执行
q.join()

注意:学习

  • 当调用 putput_nowait 方法时 q.qsize()+ 1 ,而且 q.unfinished_tasks 也会 + 1
  • 当调用 getget_nowait 方法时 q.qsize()-1 ,可是 unfinished_tasks 不发生变化
  • 当调用 task_done 方法时 unfinished_tasks-1

02-3-二、多线程的方法使用

  • 在python3中,主线程主进程结束,子线程,子进程不会结束
  • 为了可以让主线程回收子线程,能够把子线程设置为守护线程,即该线程不重要,主线程结束,子线程结束
# 建立线程对象
t1 = threading.Thread(target=func,args=(,))
# 设置为守护线程
t1.setDaemon(True)
# 启动线程
t1.start()

02-3-三、多线程实现设计思路

  • 把爬虫中的每一个步骤封装成函数,分别用线程去执行
  • 不一样的函数经过队列相互通讯,函数间解耦
生产消费模式

在这里插入图片描述

单线程爬虫流程

在这里插入图片描述

多线程爬虫流程实现

在这里插入图片描述

咱们直接写代码了!网站

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

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


class QiushiThread():
	"""糗事百科多线程爬虫"""
	
    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 url_list(self):
    	""" :return: 目标url列表 """
        # 一共13页
        for i in range(1,14):
            # 获取到url
            url = self.baseurl.format(i)
            # 把url添加到队列中
            self.url_queue.put(url)
            # print(f"如今开始请求第 {i} 页码")

    # 2.发送请求获取数据
    def send_requests(self):
    	# 循环的取出url,发送请求,直到任务所有完成
        # 完成标志:Thread().unfinished_task 值为0
    	
        while True:
            # 一、从队列中取出url
            url = self.url_queue.get()
            # 打印请求的页面url
            print(url)

            # 2.发送请求获取html页面数据--网络延迟
            response = requests.get(url=url,headers=self.headers)
            html = response.content.decode()

            # 把html页面添加到队列中,html_queue任务数量+1
            self.html_queue.put(html)
            # 这时候url_queue的任务数量完成,就要-1
            self.url_queue.task_done()

    # 3.从响应中提取数据
    def get_response(self):
    	# 循环的取出url,发送请求,直到任务所有完成
        # 完成标志:Thread().unfinished_task 值为0
        
        while True:
            # 从html_queue队列中获取响应
            responsehtml = self.html_queue.get()

            # 获取响应数据
            html = etree.HTML(responsehtml)
            restitle_list = html.xpath("//li//a[@class='recmd-content']/text()")

            # 把获取到的数据添加到数据任务队列data_queue,任务数量加一,+1
            self.data_queue.put(restitle_list)
            # 同时html_queue队列任务减小一个 -1
            self.html_queue.task_done()

    # 保存数据
    def save(self):
        while True:
        	# 从数据队列中取出数据
            resdata_list = self.data_queue.get()
            # print(resdata_list)
            for resdata in resdata_list:
                print(resdata)
            # 通知数据队列任务完成
            self.data_queue.task_done()

    # 执行
    def run(self):
        thread_list = []
        # 建立 获取 url 的任务线程,而且添加到列表
        url_thread = Thread(target=self.url_list)
        thread_list.append(url_thread)

        # 建立发送resquest请求 获取 html 的任务线程,而且添加到列表
        html_thread = Thread(target=self.send_requests)
        thread_list.append(html_thread)

        # 建立 获取 数据响应数据 的任务线程,而且添加到列表
        data_thread = Thread(target=self.get_response)
        thread_list.append(data_thread)

        # 建立 保存数据 的任务线程,而且添加到列表
        save_thread = Thread(target=self.save)
        thread_list.append(save_thread)

        # 开启线程守护
        for thread_item in thread_list:
            # 开启线程守护,主进程结束,子进程也结束
            thread_item.setDaemon(True)
            # 开始子线程
            thread_item.start()

        # 睡眠,防止太快!
        # 保证子线程必定能执行
        time.sleep(2)

        # 同时队列中的所有任务执行完毕,才结束程序
        for queueitem in [self.url_queue,self.html_queue,self.data_queue]:
            # 若是没有完成的任务不是0,就是表明还有任务没有完成,就要任务继续在等待的状态,等待任务执行
            # 每一个任务函数中都有task_done返岗,任务数量,每次执行都会-1
            queueitem.join()



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

最后获取的信息是以打印的方式展示的!

0四、结语:

我的记录,新手入门,多多学习,欢迎你们交流探讨!
我的网站: http://106.54.78.238/
song_of _sea的我的网站 http://106.54.78.238/