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


什么是线程,进程

  • 进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。
    密集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()

从下图看出代码运行是不断生产,不断消费。
爬虫篇|开多线程,咱们一起来斗图(九)_python_02实战训练爬虫篇|开多线程,咱们一起来斗图(九)_python_03这次爬取的是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')

剩余代码

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

分析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

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

# 定义消费者
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 + '----下载完成')

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

最后开线程,爬呀爬

page_queue = Queue(100)
img_queue = Queue(1000)

for p in range(1101):
    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张,哇哇,这速度
爬虫篇|开多线程,咱们一起来斗图(九)_python_07本节代码已上传,在公众号回复【斗图

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


■ Over ■

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


回复【1024】获取学习资料


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


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


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