运维开发网

爬虫篇|开多线程,咱们一起来斗图(九)

运维开发网 https://www.qedev.com 2021-03-05 09:11 出处:51CTO 作者:mb5ffd6eef9281a
什么是线程,进程进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)时,用多进程。进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。一个线程在访问内存

爬虫篇|开多线程,咱们一起来斗图(九)

什么是线程,进程

  • 进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。

    密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)时,用多进程。

    进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。

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

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

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

谈了线程和进程,不得不说线程锁(一种安全有序的让多个线程访问内存空间的机制)

来源:百度百科

曾经我看过一篇文章,对于爬虫有这么一个比喻,爬虫就是去果园摘水果,但是一个人多摘不了这么多,这不叫上了隔壁老,咱们一块去,其实就是就是一个线程,人太多了,不如分两个队伍,其实就是两个进程,可是两个队伍摘水果,万一他们都摘了,重复进入别人的领地,所以需要线程锁来隔开两个进程的地盘

线程典型例子

定义一个生产者,再定义一个消费者

import threading

import time

import random

MONEY = 0

gLock = threading.Lock()

def Procuder():

    while True:

        global MONEY

        random_money = random.randint(10,100)

        # 开锁

        gLock.acquire()

        MONEY += random_money

        gLock.release()

        print ('生产者%s-生产了%d' % (threading.current_thread,random_money))

        time.sleep(0.5)

def Customer():

    while True:

        global MONEY

        random_money = random.randint(10,100)

        if MONEY > random_money:

            print ('消费者%s-消费了:%d' % (threading.current_thread,random_money))

            gLock.acquire()

            MONEY -= random_money

            gLock.release()

        else:

            print ('需要消费的钱为:%d,余额为:%d,' % (random_money,MONEY))

        time.sleep(0.5)

def p_c_test():

    # 执行3个线程,来当作生产者

    for x in range(3):

        th = threading.Thread(target=Procuder)

        th.start()

    # 执行3个线程,来当作消费者

    for x in range(3):

        th = threading.Thread(target=Customer)

        th.start()

if __name__ == "__main__":

    p_c_test()

从下图看出代码运行是不断生产,不断消费。

爬虫篇|开多线程,咱们一起来斗图(九)

实战训练

爬虫篇|开多线程,咱们一起来斗图(九)

这次爬取的是www.doutula.com的斗图

http://www.doutula.com/photo/list/

导入对应的模块

import requests

from lxml import etree

from queue import Queue

import threading

import os

# 用于保存图片的下载方式

from urllib import request

这次采用类的继承方式来开多线程和队列的方式

# 定义生产者来生成表情的url

class Producer(threading.Thread):

    headers = {

        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'

                      ' Chrome/69.0.3497.100 Safari/537.36',

    }

    def __init__(self, page_queue, img_queue, *args, **kwargs):

        # 继承threading.Thread的__init__方法

        super(Producer, self).__init__(*args, **kwargs)

        self.page_queue = page_queue

        self.img_queue = img_queue

        if  not os.path.exists(r'D:\images\imgs'):

            os.makedirs(r'D:\images\imgs')

剩余代码

爬虫篇|开多线程,咱们一起来斗图(九)

分析xpath

def run(self):

    while True:

         if self.page_queue.empty():

            break

         url = self.page_queue.get()

         self.parse_page(def parse_page(self, url):

def parse_page(self, url):

    response = requests.get(url, headers=self.headers)

    text = response.text

    html = etree.HTML(text)

    contents = html.xpath("//a[@class='col-xs-6 col-sm-3']")

    for content in contents:

        title = content.xpath(".//p[@style='display: none']/text()")[0]

        href = content.xpath(".//img/@data-original")[0]

        suffix = os.path.splitext(href)[1]

        filename = title + suffix

        self.img_queue.put((href, filename))

如果队列有东西就下载,没用就break

爬虫篇|开多线程,咱们一起来斗图(九)

# 定义消费者

class Consumer(threading.Thread):

    def __init__(self, page_queue, img_queue, *args, **kwargs):

        super(Consumer, self).__init__(*args, **kwargs)

        self.page_queue = page_queue

        self.img_queue = img_queue

    def run(self):

        while True:

            if self.img_queue.empty() and self.page_queue.empty():

                break

            img_url, filename = self.img_queue.get()

            request.urlretrieve(img_url, 'D:\images\imgs\{}'.format(filename))

            print(filename + '----下载完成')

爬虫篇|开多线程,咱们一起来斗图(九)

最后开线程,爬呀爬

page_queue = Queue(100)

img_queue = Queue(1000)

for p in range(1, 101):

    url = 'http://www.doutula.com/photo/list/?page={}'.format(p)

    page_queue.put(url)

for x in range(5):

    producer = Producer(page_queue, img_queue)

    producer.start()

for x in range(5):

    consumer = Consumer(page_queue, img_queue)

    consumer.start()

不到10秒中,就2000张,哇哇,这速度

爬虫篇|开多线程,咱们一起来斗图(九)

本节代码已上传,在公众号回复【斗图】

爬虫篇|开多线程,咱们一起来斗图(九)

■ Over ■

最后,祝有所学习,有所成长

回复【1024】获取学习资料

爬虫篇|开多线程,咱们一起来斗图(九)

转发,好看支持一下,感谢

你的转发,就是对我最大的支持

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消