python 爬虫(十一)多线程爬虫基础 + 通过ajax接口获取数据(多线程的运用+案例(腾讯招聘+链家)) +(程序+进程+线程+多线程+多线程和多进程的区别 + 互斥锁+ 死锁 + 银行家算法)

小知识:格式化字符串的三种方法:

  1. '....%s.' %i "%"是Python风格的字符串格式化操作符;下面整理了一些符号及其含义在这里插入图片描述
    例子:注意:如果是%和浮点数要用两个%来表示%;如‘%.2f%%’

    # 方式1:使用%运算符,  %s表示任意字符,%d表示整数,%f表示浮点数
    name = 'tom123456'
    age = 18
    height = 180.5
    print('大家好,我叫:%2.4s 年龄:%d 身高:%.2f' % (name, age, height))
    print('当前时间:%d年-%02d月-%d日' % (2019, 1, 24))  # %02d指月份为两位,不足两位则补0(针对int类型)
    '''
        %.2f    保留点数后的两位数(针对浮点型)
        %2.4s   只能保留2-4位字符(针对字符串类型)
    '''
    
    
  2. format‘。{3}。。。{2}。。{1}’.format(a,b,c) ‘。{}。。。{}。。{}’.format(a,b,c)
    例子:
    (1)位置映射

    name = 'tom123456'
    age = 18
    height = 180.5
    print('大家好,我叫{0},年龄{1},身高{2:.2f}'.format(name, age, height))  # 按照指定索引位置
    
    

    (2)关键字映射

    name = 'tom123456'
    age = 18
    height = 180.5
    print('大家好,我叫{name},年龄{age},身高{height}'.format(age=age, name=name, height=height))  # 按照指定名字
    

    (3)元素访问

    print("{0[0]}----{0[1]}".format(["python","java"]))
    python----java
    #添加多个列表的形式
     print("{0[0]}---{0[1]}---{1[0]}---{1[1]}".format(["python","java"],["c++","R.language"]))
    #利用元组的形式进行元素的访问
    print("{0[0]}---{0[1]}".format((1,2)))
    
  3. f嵌入式

    name = 'tom123456'
    age = 18
    height = 180.5
    # 方式3:在字符串前面添加一个f,使用{变量名}来嵌入变量
    print(f'大家好,我叫{name},年龄{age},身高{height:.2f}')
    
    

一、程序、进程、线程的概念


分类 概念
程序 一个应用可以当做一个程序,比如qq程序
进程 程序运行最小的资源分配单位。一个程序可以有多个进程
线程 cpu最小的调度单位,必须依赖进程而存在。线程没有独立的资源,所有线程共享他的资源

详细介绍

  • 程序:是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。

  • 进程:是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。

    扫描二维码关注公众号,回复: 8971963 查看本文章
  • 线程:线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

  • 联系:一个程序至少有一个进程,一个进程至少有一个线程

单进程:只有一个进程

单线程:只有一个线程

问题:线程上下文的切换比进程上下文切换要快很多?
解答

  • 进程切换时,涉及到当前进程的CPU环境的保存和新被调度运行进程的CPU环境的设置。
  • 线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。

二、多线程


1. 多线程


  • 多线程:多线程是指一个程序包含多个并行的线程来完成不同的任务

  • 多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多 核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行。

  • 多线程的优点:可以提高cpu的利用率


2. 创建多线程的方法


(1)创建多线程的第一种方法


  • 代码:

    import threading
    
    t = threading.Thread(
    	target = 方法名,
    	args = (,)  ## 方法的参数
    )
    t.start()
    

    单线程:

    import time
    import random
    
    #单线程爬虫
    def download(fileName):
        print(f"{fileName}文件开始下载")
        time.sleep(random.random()*10)
        print(f"{fileName}文件完成下载")
    #单线程 默认主线程
    if __name__ == '__main__':
        for i in range(5):
            ## main下面调用函数属于单线程
            # download(i)
    
    

    多线程:

    import time
    import random
    import threading
    
    #多线程爬虫
    def download(fileName):
        print(f"{fileName}文件开始下载")
        time.sleep(random.random()*10)
        print(f"{fileName}文件完成下载")
    #多线程 默认主线程
    if __name__ == '__main__':
        for i in range(5):
            ## main下面创建线程开启线程属于多线程
            t = threading.Thread(target=download,args=(i,))
            t.start()
    

    多个函数多线程:

    import random,time,threading
    
    def sing():
        for i in range(3):
            print(f'{i}正在唱歌--------')
            time.sleep(random.random())
    def dance():
        for i in range(3):
            print(f'{i}正在跳舞------')
            time.sleep(random.random())
    
    
    if __name__ == '__main__':
        #创建线程来启动这两个任务
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
        t1.start()
        t2.start()
    
        #查看线程数量几个
        while True:
            length  = len(threading.enumerate())
            print(f'当前运行的线程数量:{length}')
            time.sleep(random.random())
            if length<=1:
                break
    
  • 运行结果:

    先打印再休眠:
    在这里插入图片描述
    先休眠再打印:
    在这里插入图片描述


(2)创建进程的第二种方法


  • 代码:创建类继承父类
    下面是简单的写法:
    #父类
    class A:
        def __init__(self):
            print('父类init被触发')
        def run(self):
            print('父类的run被调用')
    
    #子类
    class B(A):
        def run(self):
            print('子类的run被调用')
    #实例化过程
    B()
    

在这里插入图片描述

  • 进程第二种写法:线程类
    在这里插入图片描述

线程类创建步骤:

  • (1)继承threading.Thread
  • (2)重写run方法
  • (3)实例化这个类就可以创建线程,之后再调用start()方法来启动就可以了

线程类传参


线程类传参:必须在线程内的__init__()调用父类的__init__()方法(无论在__init__()方法开头或者结尾调用都可以)

  • 第一种:super().__init__() 也就是super(MyThread, self).__init__()
  • 第二种:threading.Thread.__init__(self) ## self传当前类的cls

示例:在这里插入图片描述



3. 线程的生存期


生存期:当我们启动一个线程到这个线程的任务方法执行完毕的过程就是这个线程的生命周期;即线程的整个状态。


4. 查看线程的数量


查看当前进程下线程的数量

### 格式:threading.enumerate()
#查看线程数量几个
    while True:
        length  = len(threading.enumerate())
        print(f'当前运行的线程数量:{length}')
        time.sleep(random.random())
        if length<=1:
            break

在这里插入图片描述


5. 线程名称


在线程中,我们可以通过MyThread(name= “downloadThread”)修改线程名称

  • 修改名称前:
    在这里插入图片描述
  • 线程名称修改后
    在这里插入图片描述
    在这里插入图片描述

6. 线程的执行顺序,线程的五种状态


线程的执行顺序是不固定的,原因主要是由线程状态决定的

线程的五种状态:

  • 新建:线程创建t=Mythread(i)或者使用t = threading.Thread(target=函数名)
  • 就绪:当启动线程后,线程就进入就绪状态,就绪状态的线程会被放到一个cpu调度的队列里面,cpu会负责让其中的线程运行,变成运行状态。
  • 运行:cpu调度一个就绪状态的线程,该线程就变为运行状态。
  • 阻塞:当运行状态的线程被阻塞就变为阻塞状态,阻塞状态的线程会重新变为就绪状态才能继续运行。
  • 死亡:线程执行完毕。

线程的周期也就是以上一个流程
在这里插入图片描述
在这里插入图片描述


总结:多线程执行混乱 ! 无法确定先后顺序!!


7. 线程间的变量共享



# 多线程--全局变量--共享
from threading import Thread
import time
import random
g_num = 100

def work1():
    global g_num
    for i in range(3):
        g_num += 1
        time.sleep(random.random())
        print('in work1,gum=%d' % g_num)

def work2():
    global g_num
    for i in range(3):
        g_num += 1
        time.sleep(random.random())
        print('in work2,gum=%d' % g_num)

if __name__ == '__main__':
    t1 = Thread(target=work1)
    t2 = Thread(target=work2)
    t1.start()
    t2.start()

在这里插入图片描述

多个线程对公有变量处理时,容易造成数据的混乱,造成线程不安全问题

注意: 多个线程容易混乱,数据不安全
解决方法:下面提到的互斥锁


三、多线程和多进程



1. 功能:


  • 进程:能够完成多任务,比如在一台能够同时运行多个QQ。
  • 线程:能够完成多任务,比如一个QQ中的多个聊天窗口。

2. 定义:


  • 进程:进程是系统进行资源分配和调试的一个独立单位。
  • 线程:线程是进程的一个实体,是CPU调用和分派的基本单位,它是比进程更小的能独立运行的基本单位。

​ 线程自己基本上不拥有系统资源,但是它可以与同属于一个进程的其它线程共享进程所拥有的全部资源。


3. 区别:


一个程序至少有一个进程,一个进程至少有一个线程,线程的划分尺度小于进程(资源比进程少),使得多线程程序并发性更高。进程在执行过程中拥有独立的内存单元, 而多个线程共识这段儿内存空间。线程不能独立运行,必需依存进程。


4. 优缺点:



​ 线程和进程在使用上各有优势和缺点:线程的执行开销小,但不利于资源的管理和保存。进程正好相反。


5. 多线程的优点:


  • (1)程序逻辑和控制方式复杂;

  • (2)所有线程可以直接共享内存和变量;

  • (3)线程方式消耗的总资源比进程方式好(资源缺少)。


6. 多线程缺点:


  • (1)每个线程与主程序共用地址空间,受限于2GB地址空间;

  • (2)线程之间的同步和加锁控制比较麻烦;

  • (3)一个线程的崩溃可能影响到整个程序的稳定性;


7. 多进程优点:


  • (1)每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;

  • (2)通过增加CPU,就可以容易扩充性能;

  • (3)每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大 。


8. 多进程缺点:


  • (1)逻辑控制复杂,需要和主程序交互;

  • (2)需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大。


9. 总结:



在实际开发中,选择多线程和多进程应该从具体实际开发来进行选择。最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。


四、多线程开发中遇到的问题


1. 线程不安全


from threading import Thread

g_num = 0
def a1():
    global g_num
    for i in range(1000000):
        # g_num += 1
        b = g_num + 1
        g_num = b
    print("---test1---g_num=%d"%g_num)

def a2():
    global g_num
    for i in range(1000000):
        a = g_num + 1
        g_num = a
    print("---test2---g_num=%d"%g_num)
if __name__ == '__main__':
    p1 = Thread(target=a1)
    p1.start()

    p2 = Thread(target=a2)
    p2.start()

#--------------------运行结果------------------------
---test2---g_num=1559989
---test1---g_num=1516811

Process finished with exit code 0

加join后

在这里插入图片描述

问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果达不到预期。这种现象我们称为“线程不安全”


2. 解决方法:互斥锁


互斥锁概念:

  • 互斥锁:当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问“竞争资源”,最简单的同步机制就是引用互斥锁。互斥锁为资源引入一个状态:锁定/非锁定状态。 某个线程要更改共享数据时,先将其锁定,此时资源状态为“锁定”,其它线程不能更改;直到当前线程释放资源,将资源变成"非锁定"状态,其它的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行“写操作”,从而保证多个线程数据正确性。

相当于互不影响的两个线程,一个走完再走另一个

创建互斥锁的步骤:

  • 1.创建锁对象metux = threading.Lock()
  • 2.互斥锁的操作
    if mutex.acpuire():
    			'''
    			对公有变量处理的代码
    			'''
    		mutex.release()#释放锁
    

完整案例代码:

import threading,time
#全局变量
g_num = 0
def w1():
    global g_num
    for i in range(10000000):
        #上锁
        mutexFlag = mutex.acquire(True)
        if mutexFlag:
            g_num+=1
            #解锁
            mutex.release()
    print("test1---g_num=%d"%g_num)

def w2():
    global g_num
    for i in range(10000000):
        # 上锁
        mutexFlag = mutex.acquire(True)
        if mutexFlag:
            g_num+=1
            # 解锁
            mutex.release()
    print("test2---g_num=%d" % g_num)

if __name__ == "__main__":
    #创建锁
    mutex = threading.Lock()

    t1 = threading.Thread(target=w1)
    t1.start()

    t2 = threading.Thread(target=w2)
    t2.start()

在这里插入图片描述


3. 死锁


  • 死锁第一种情况:当一个线程获取了锁之后,还未释放的前提下,试图获取另一把锁,此时会产生死锁。在这里插入图片描述解决办法:判断:必须释放才能获取锁

  • 死锁第二种情况:线程A获取锁1,线程B获取了锁2,线程A还未释放锁1继续想要获取锁2,线程B也未释放锁2,同时想要获取锁1。在这里插入图片描述


4. 银行家算法:避免死锁


银行家算法是避免死锁的一种重要方法。操作系统按照银行家制定的规则为线程分配资源,当线程首次申请资源时,要测试该线程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。当线程在执行中继续申请资源时,先测试该线程已占用的资源数与本次申请的资源数之和是否超过了该线程对资源的最大需求量。若超过则拒绝分配资源,若没有超过则再测试系统现存的资源能否满足该进程尚需的最大资源量,若能满足则按当前的申请量分配资源,否则也要推迟分配。
银行家算法具体介绍:
每一个线程进入系统时,它必须声明在运行过程中,所需的每种资源类型最大数目,其数目不应超过系统所拥有每种资源总量,当线程请求一组资源系统必须确定有足够资源分配给该进程,若有在进一步计算这些资源分配给进程后,是否会使系统处于不安全状态,不会(即若能在分配资源时找到一个安全序列),则将资源分配给它,否则等待。
假定系统中有五个线程{P0,P1,P2,P3,P4}和三类资源{A,B,C},各类资源数量分别为10,5,7,在T0时刻分配资源情况如图:
Max:表示线程对每类资源的最大需求量;
Allocation:表示系统给线程已分配每类资源的数目;
Need:表示线程还需各类资源数目;
Available:表示系统当前剩下的资源。
在这里插入图片描述
从初始找出安全序列:
(1)首先系统剩下资源{3,3,2},查表可满足5个进程Need的进程有:P1(1,2,2)、P3(0,1,1),先给P1分配;
(2)P1分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{3,3,2}={5,3,2};
(3)根据系统剩下资源查表可满足剩下4个进程Need的进程有P3{0,1,1}、P4{4,3,1},再给P3分配;
(4)P3分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{5,3,2}={7,4,3};
(5)根据系统剩下资源查表可满足剩下3个进程Need的进程有P0{7,4,3}、P2{6,0,0}、P4{4,3,1},再给P4分配;
(6)P4分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{7,4,3}={7,4,5};
(7)根据系统剩下资源查表可满足剩下2个进程Need的进程有P0{7,4,3}、P2{6,0,0},再给P2分配;
(8)P2分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{7,4,5}={10,4,7};
(9)根据系统剩下资源查表可满足剩下1个进程Need的进程有P0{7,4,3},最后给P0分配;
(10)P0分配以后执行完释放其所占资源后系统此时剩下资源有:Allocation+{10,4,7}={10,5,7};
(11)所有进程按此序列{P1,P3,P4,P2,P0}可安全执行完毕,最后系统资源全部释放。(由以上也可知安全序列不唯一,但只要找出一个安全序列,说明此系统是安全的(找到安全序列可按此序列真正执行进程推进顺序,若没找到,则恢复初始状态,其并没有真正给进程分配资源,只是提前避免))
银行家算法在面试过程中需讲出原理,感兴趣的同学可用代码进行实现,这里就不展开了。


五、通过ajax接口获取数据


爬取网站的流程:

  1. 确定网站哪个url是数据的来源。
  2. 简要分析一下网站结构,查看数据一般放在哪里。
  3. 查看是否有分页,解决分页的问题。
  4. 发送请求,查看response.text里面是否有我们想要的数据内容。
  5. 如果有数据,就用相应的提取数据的方法提取数据保存。
  6. 如果没有数据,我们就可以通过以下两种方法来实现爬取:
    (1)分析数据来源,查看是否通过一些接口获取到的页面数据。(首推)
    如果没有在页面中返回数据,我们应该首先想到,数据有可能是从ajax接口中获取的。
    分析接口的步骤:
    1.查看该接口返回的数据是否使我们想要的。
    2.重点查看该接口的请求参数。
    了解哪些请求参数是变化的以及他的变化规律。
    (2)selenium+phantomjs来获取页面内容来获取页面内容(最后一道防线)。

案例:腾讯招聘

  • 普通类写法

    import requests,json
    
    class Tencent:
        def __init__(self,url,headers):
            self.url = url
            self.headers = headers
            self.parse()
    
        def write_to_file(self,list_):
            for item in list_:
                with open("infos.txt","a+",encoding='utf-8') as fp:
                    fp.writelines(str(item)+'\n')
    
        def parse_json(self,text):
            infos = []
            ## 将json字符串解析变成python内置对象
            json_dict = json.loads(text)
            for data in json_dict["Data"]["Posts"]:
                RecruitPostName = data['RecruitPostName']
                CategoryName = data['CategoryName']
                Responsibility = data['Responsibility']
                LastUpdateTime = data['LastUpdateTime']
                detail_url = data['PostURL']
                item = {}
                item["岗位名称"] = RecruitPostName
                item["岗位类型"] = CategoryName
                item["岗位要求"] = Responsibility
                item["发布时间"] = LastUpdateTime
                item['岗位详情页路由'] = detail_url
                infos.append(item)
            self.write_to_file(infos)
    
        def parse(self):
            for i in range(1,10):
                params = {
                    'timestamp': '1572852398914',
                    'countryId': '',
                    'cityId': '',
                    'bgIds': '',
                    'productId': '',
                    'categoryId': '',
                    'parentCategoryId': '',
                    'attrId': '',
                    'keyword': '',
                    'pageIndex': str(i),
                    'pageSize': '10',
                    'language': 'zh-cn',
                    'area': 'cn'
                }
                response = requests.get(self.url,params=params,headers = self.headers)
                # print(response.text)
                self.parse_json(response.text)
    
    if __name__ == '__main__':
        base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?'
        headers = {'referer': 'https://careers.tencent.com/search.html',
                   'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
                   }
        Tencent(base_url,headers)
    
  • 多线程的第一种写法

    import requests,json,threading
    
    class Tencent:
        def __init__(self,url,headers,params):
            self.url = url
            self.headers = headers
            self.params = params
    
        def write_to_file(self,list_):
            for item in list_:
                with open("infos.txt","a+",encoding='utf-8') as fp:
                    fp.writelines(str(item)+'\n')
    
        def parse_json(self,text):
            infos = []
            ## 将json字符串解析变成python内置对象
            json_dict = json.loads(text)
            for data in json_dict["Data"]["Posts"]:
                RecruitPostName = data['RecruitPostName']
                CategoryName = data['CategoryName']
                Responsibility = data['Responsibility']
                LastUpdateTime = data['LastUpdateTime']
                detail_url = data['PostURL']
                item = {}
                item["岗位名称"] = RecruitPostName
                item["岗位类型"] = CategoryName
                item["岗位要求"] = Responsibility
                item["发布时间"] = LastUpdateTime
                item['岗位详情页路由'] = detail_url
                infos.append(item)
            self.write_to_file(infos)
    
        def parse(self):
            response = requests.get(self.url,params=self.params,headers = self.headers)
            self.parse_json(response.text)
    
    if __name__ == '__main__':
        base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?'
        headers = {'referer': 'https://careers.tencent.com/search.html',
                   'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
                   }
        t_list=[]
        for i in range(1, 10):
            params = {
                'timestamp': '1572852398914',
                'countryId': '',
                'cityId': '',
                'bgIds': '',
                'productId': '',
                'categoryId': '',
                'parentCategoryId': '',
                'attrId': '',
                'keyword': '',
                'pageIndex': str(i),
                'pageSize': '10',
                'language': 'zh-cn',
                'area': 'cn'
            }
            tencent = Tencent(base_url,headers,params)
            ## 第一种方法开启线程(缺点:cpu卡顿,系统可能崩溃)
            t = threading.Thread(target = tencent.parse)
            t.start()
            t_list.append(t)
            ## 将每个线程都调用join方法,保证测试运行时间是在每个线程运行执行完毕后的时间
            for t in t_list:
                t.join()
    
    
  • 多线程的第二种写法

    import requests,json,threading
    from queue import Queue
    
    class Tencent(threading.Thread):
        def __init__(self,url,headers,name,q):
            super().__init__()
            self.url = url
            self.headers = headers
            self.name = name
            self.q = q
    
        def run(self):
            self.parse()
    
        def write_to_file(self,list_):
            for item in list_:
                with open("infos.txt","a+",encoding='utf-8') as fp:
                    fp.writelines(str(item)+'\n')
    
        def parse_json(self,text):
            infos = []
            ## 将json字符串解析变成python内置对象
            json_dict = json.loads(text)
            for data in json_dict["Data"]["Posts"]:
                RecruitPostName = data['RecruitPostName']
                CategoryName = data['CategoryName']
                Responsibility = data['Responsibility']
                LastUpdateTime = data['LastUpdateTime']
                detail_url = data['PostURL']
                item = {}
                item["岗位名称"] = RecruitPostName
                item["岗位类型"] = CategoryName
                item["岗位要求"] = Responsibility
                item["发布时间"] = LastUpdateTime
                item['岗位详情页路由'] = detail_url
                infos.append(item)
            self.write_to_file(infos)
    
        def parse(self):
            while True:
                if  self.q.empty():
                    break
                page = self.q.get()
                print(f"++++++++++++++++++第{page}页++++++++是{self.name}线程运行的")
                params = {
                    'timestamp': '1572852398914',
                    'countryId': '',
                    'cityId': '',
                    'bgIds': '',
                    'productId': '',
                    'categoryId': '',
                    'parentCategoryId': '',
                    'attrId': '',
                    'keyword': '',
                    'pageIndex': str(page),
                    'pageSize': '10',
                    'language': 'zh-cn',
                    'area': 'cn'
                }
                response = requests.get(self.url,params,headers = self.headers)
                # print(response.text)
    
                self.parse_json(response.text)
    	if __name__ == '__main__':
    	    base_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?'
    	    headers = {'referer': 'https://careers.tencent.com/search.html',
    	               'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'
    	               }
    	    ## 1. 创建一个任务队列
    	    queue = Queue()
    	    ## 2. 给队列添加任务;任务是每一页的页码
    	    for page in range(1,10):
    	        queue.put(page)
    	    ## 创建一个列表,里面放规定的线程数
    	    thread_list = ["线程A","线程B","线程C","线程D"]
    	    list = []
    	    for thread_name in thread_list:
    	        t = Tencent(base_url,headers,thread_name,queue)
    	        t.start()
    	        list.append(t)
    	    for i in list:
    	        i.join()
    
    
发布了107 篇原创文章 · 获赞 43 · 访问量 6133

猜你喜欢

转载自blog.csdn.net/langdei/article/details/102903455