前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >爬虫篇|开多线程,咱们一起来斗图(九)

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

作者头像
润森
发布2019-08-29 11:11:24
3090
发布2019-08-29 11:11:24
举报
文章被收录于专栏:毛利学Python毛利学Python

什么是线程,进程

  • 进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。 密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)时,用多进程。 进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。
  • 线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。 共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。 一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间。

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

来源:百度百科

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

线程典型例子

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

代码语言:javascript
复制
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/

导入对应的模块

代码语言:javascript
复制
import requests
from lxml import etree
from queue import Queue
import threading
import os
# 用于保存图片的下载方式
from urllib import request

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

代码语言:javascript
复制
# 定义生产者来生成表情的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

代码语言:javascript
复制
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

代码语言:javascript
复制
# 定义消费者
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 + '----下载完成')

最后开线程,爬呀爬

代码语言:javascript
复制
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张,哇哇,这速度

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小刘IT教程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程典型例子
  • 实战训练
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档