多线程爬虫——图片爬取

引言

最近这几天,我一直在看多线程,感觉多线程并不是想象中的那么容易,自己也尝试用多线程写了几个爬虫的例子。
在这里插入图片描述

思路分析

具体的爬取思路就不说了,都是一些常规的爬虫操作,这里主要来说一下多线程的问题。
这三段爬虫代码都是应用了生产者——消费者设计模式。
官方解释

生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
                                                                                                                                                      ——百度百科

个人理解

生产者生产数据放到容器中,而消费者负责消耗掉容器中的数据。
在这里插入图片描述

代码

(1)斗图啦表情包

# !/usr/bin/env python
# —*— coding: utf-8 —*—
# @Time:    2020/2/3 18:54
# @Author:  Martin
# @File:    doutula_mul.py
# @Software:PyCharm
import requests
import os
import re
import threading
from lxml import etree
from queue import Queue


class Producer(threading.Thread):
    headers = {
        'Referer': 'http://www.doutula.com/photo/list/?page=1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
    }

    def __init__(self, page_queue, img_queue, *args, **kwargs):
        super(Producer, self).__init__(*args, **kwargs)
        self.page_queue = page_queue
        self.img_queue = img_queue

    def run(self):
        while True:
            if self.page_queue.empty():
                break
            url = self.page_queue.get()
            self.parse_page(url)
            print("Producer: %d %d" % (self.page_queue.qsize(), self.img_queue.qsize()))

    def parse_page(self, url):
        try:
            r = requests.get(url, headers=self.headers)
        except:
            print("请求失败!")
            return
        text = r.text
        html = etree.HTML(text)
        imgs = html.xpath("//a[@class='col-xs-6 col-sm-3']/img")
        for img in imgs:
            # 图片信息的默认值
            img_name = ""
            img_url = ""
            suffix = ""

            img_url_list = img.xpath("@data-original")
            if len(img_url_list) != 0:
                img_url = img_url_list[0]
                suffix = os.path.splitext(img_url)[1]
            img_name_list = img.xpath("@alt")
            if len(img_name_list) != 0:
                img_name = img_name_list[0]
                img_name = re.sub(r'\W', "", img_name)
            if len(img_name) != 0 and len(img_url) != 0 and len(suffix) != 0:
                self.img_queue.put((img_name, img_url, suffix))


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():
                if self.page_queue.empty():
                    return
            try:
                img_name, img_url, suffix = self.img_queue.get(True, 1)
                self.img_queue.task_done()
            except :
                break
            try:
                r = requests.get(img_url)
            except :
                print("请求失败!")
                continue
            if not os.path.exists('./result/Expression_Bao_mul'):
                os.makedirs('./result/Expression_Bao_mul')
            with open('./result/Expression_Bao_mul/' + img_name + suffix, 'wb') as f:
                f.write(r.content)
            print(img_name + " 下载完成!")
            print("Consumer: %d %d" % (self.page_queue.qsize(), self.img_queue.qsize()))


def main():
    page_queue = Queue(100)
    img_queue = Queue(1000)
    url = 'http://www.doutula.com/photo/list/?page=%d'
    for i in range(1, 101):
        page_queue.put(url % i)
    for x in range(5):
        t = Producer(page_queue, img_queue)
        t.start()
    for y in range(5):
        t = Consumer(page_queue, img_queue)
        t.start()


if __name__ == '__main__':
    main()

(2)B站壁纸娘相册

# !/usr/bin/env python
# —*— coding: utf-8 —*—
# @Time:    2020/2/28 22:12
# @Author:  Martin
# @File:    Bilibili_wallpaper.py
# @Software:PyCharm
import requests
from urllib import request
from queue import Queue
import threading
import json
import os


class Producer(threading.Thread):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
    }

    def __init__(self, url_queue, img_queue, *args, **kwargs):
        super(Producer, self).__init__(*args, **kwargs)
        self.url_queue = url_queue
        self.img_queue = img_queue

    def run(self):
        while True:
            if self.url_queue.empty():
                break
            url = self.url_queue.get()
            self.parse_data(url)
            print("Producer: %d %d" % (self.url_queue.qsize(), self.img_queue.qsize()))

    def parse_data(self, url):
        r = requests.get(url, headers=self.headers)
        data = json.loads(r.text)['data']
        items = data['items']
        for item in items:
            pictures = item['pictures']
            for picture in pictures:
                self.img_queue.put(picture['img_src'])


class Consumer(threading.Thread):
    def __init__(self, url_queue, img_queue, *args, **kwargs):
        super(Consumer, self).__init__(*args, **kwargs)
        self.url_queue = url_queue
        self.img_queue = img_queue

    def run(self):
        while True:
            if self.img_queue.empty() and self.url_queue.empty():
                break
            try:
                url = self.img_queue.get(True, 1)
                self.img_queue.task_done()
            except :
                break
            name = url.split('/')[-1]
            try:
                request.urlretrieve(url, './result/壁纸娘/' + name)
                print(name + '    下载完成!')
            except Exception as e:
                print(e)
                print(name + '  下载失败!')
            print("Consumer: %d %d" % (self.url_queue.qsize(), self.img_queue.qsize()))


def main():
    url_queue = Queue(11)
    img_queue = Queue(1000)
    if not os.path.exists('./result/壁纸娘/'):
        os.makedirs('./result/壁纸娘/')
    for i in range(11):
        url = 'https://api.vc.bilibili.com/link_draw/v1/doc/doc_list?uid=6823116&page_num=%s&page_size=30&biz=all' % str(
            i)
        url_queue.put(url)
    for x in range(5):
        t = Producer(url_queue, img_queue)
        t.start()
    for y in range(5):
        t = Consumer(url_queue, img_queue)
        t.start()


if __name__ == '__main__':
    main()

(3)某博客的文章头图

# !/usr/bin/env python
# —*— coding: utf-8 —*—
# @Time:    2020/2/28 9:15
# @Author:  Martin
# @File:    xiaoyou_mul.py
# @Software:PyCharm
import requests
import json
import os
import threading
from queue import Queue


class Spider(threading.Thread):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
    }

    def __init__(self, page_queue, img_queue, *args, **kwargs):
        super(Spider, self).__init__(*args, **kwargs)
        self.page_queue = page_queue
        self.img_queue = img_queue

    def run(self):
        while True:
            if self.page_queue.empty():
                break
            url = self.page_queue.get()
            self.parse_page(url)
            print("Producer: %d %d" % (self.page_queue.qsize(), self.img_queue.qsize()))

    def parse_page(self, url):
        r = requests.get(url, headers=self.headers)
        data = json.loads(r.text)['data']
        try:
            postlist = data['Postlist']
            for item in postlist:
                self.img_queue.put(item['image'])
        except Exception as e:
            print(e)


class Saver(threading.Thread):
    def __init__(self, page_queue, img_queue, *args, **kwargs):
        super(Saver, 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
            try:
                img_url = self.img_queue.get(True, 1)
                self.img_queue.task_done()
            except :
                break
            img_name = img_url.split('/')[-1]
            if not os.path.exists('./result/封面图片(mul)/'):
                os.makedirs('./result/封面图片(mul)/')
            try:
                r = requests.get(img_url)
                with open('./result/封面图片(mul)/'+img_name, 'wb') as f:
                    f.write(r.content)
                print(img_name + " 下载完成!")
            except Exception as e:
                print(e)
                print(img_name + " 下载失败!")
            print("Consumer: %d %d" % (self.page_queue.qsize(), self.img_queue.qsize()))


def main():
    page_queue = Queue(30)
    img_queue = Queue(1000)
    raw_url = 'https://api.blog.xiaoyou66.com/api/getpostlist?id=%s'
    for i in range(1, 30):
        url = raw_url % str(i)
        page_queue.put(url)
    for x in range(5):
        t = Spider(page_queue, img_queue)
        t.start()
    for y in range(5):
        t = Saver(page_queue, img_queue)
        t.start()


if __name__ == '__main__':
    main()

效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结分析

据说Python的多线程不是很好用,等有时间我再用Java试一试。
在这里插入图片描述

发布了169 篇原创文章 · 获赞 244 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Deep___Learning/article/details/104569426