pyhton爬虫学习(八):线程和队列的使用

上一篇我们写了python实现图片爬取的实例,有兴趣可以看看
地址:https://blog.csdn.net/stonezry/article/details/106072145

我们还是实现和上面相同的功能,只是这里我们运用了线程和队列进行实现。

python的线程创建方法,这里有两种方法。

1. 将要执行的方法作为参数传给Thread的构造方法 target传递方法名字,args传递方法参数。 如下
#!/usr/bin/python3 
 import threading import time 
 # 为线程定义一个函数 
def printtime( threadName, delay): 
     count = 0
     while count < 5:
          time.sleep(delay) 
          count += 1 
          print (%s: %s” % ( threadName, time.ctime(time.time()) )) 
if __name__ == '__main__'::  
# 创建两个线程     
    t = threading.Thread(target=printtime, args=(“Thread-1, 2,))
    t.start()     
    t = threading.Thread(target=printtime, args=(“Thread-2, 1,))    
    t.start() 
2. 从Thread继承,并重写run()
class MyThread(threading.Thread):
    def init(self, arg):
        threading.Thread.init(self)
        # 注意:这里一定要显式的调用初始化函数。
        self.arg=arg

    def run(self): #定义每个线程要运行的函数
        pass

arg = 'haha'
t = MyThread(arg)
t.start()

附:

  • run(): 用以表示线程活动的方法。
  • start(): 启动线程活动。
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • **setName(): **设置线程名。

队列的使用

队列的创建

  • q = Queue.Queue(),默认为先进先出队列。
  • q = Queue.LifoQueue(),后进先出队列
  • q = Queue.PriorityQueue(),优先级队列

以上三种队列参数均可以传递一个最大的队列数值maxsize。

  • Queue.qsize() 返回队列的大小
  • Queue.empty() 如果队列为空,返回True,反之False
  • Queue.full() 如果队列满了,返回True,反之False,Queue.full 与 maxsize 大小对应
  • Queue.get([block[, timeout]])获取队列,timeout等待时间
  • Queue.getnowait() 相当Queue.get(False)
  • Queue.put(item) 写入队列,timeout等待时间
  • Queue.putnowait(item) 相当Queue.put(item, False)
  • Queue.taskdone() 在完成一项工作之后,Queue.taskdone()函数向任务已经完成的队列发送一个信号 Queue.join() 实际上意味着等到队列为空,再执行别的操作

简单介绍完线程和队列,我们开始实现我们的图片加载。 这里我们创建两个线程,线程t1用来过滤出需要加载的图片地址,线程t2用来加载图片并保存到本地文件。
t2的结束与否,通过判断t1是否还在运行以及队列是否为空来进行判断处理。
完整代码:

import threading
import queue
import re
import urllib.request
import urllib.parse
import time
import urllib.error
import ssl


# 使用代理服务器
def use_proxy(proxy_addr, url):
    try:
        ssl._create_default_https_context = ssl._create_stdlib_context
        #设置代理
        # proxy = urllib.request.ProxyHandler({'https': proxy_addr})
        # opener = urllib.request.build_opener(proxy)
        opener = urllib.request.build_opener()
        # 模拟成浏览器
        headers = {"User-Agent":
                       "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}

        request = urllib.request.Request(url, headers=headers)
        data = opener.open(request).read()
        return data
    except urllib.error.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)
        time.sleep(1)
    except Exception as e:
        print("exception:" + str(e))
        time.sleep(1)


# 线程1,专门获取对应网址并处理成真实网址
class UrlThread(threading.Thread):
    def __init__(self, pagestart, pageend, proxy, urlqueue):
        threading.Thread.__init__(self)
        self.pagestart = pagestart
        self.pageend = pageend
        self.proxy = proxy
        self.urlqueue = urlqueue

    def run(self):

        for page in range(self.pagestart, self.pageend):

            index = 2 * page - 1
            s = index * 30
            data_query = {
                'keyword': '手机',
                'wq': '手机',
                'page': page,
                's': s,
                'click': 0
            }

            url = 'https://search.jd.com/Search?' + urllib.parse.urlencode(data_query)
            print(url)
            # 使用代理服务器爬取,解决ip被封杀问题
            data1 = str(use_proxy(self.proxy, url))
            # url正则
            pat = '<img width="220" height="220" data-img="1" src="//(.+?.jpg)" data-lazy-img="done" />'

            listurl = re.compile(pat).findall(data1)
            # 便于调试
            print("获取到第" + str(page) + "页,该页有" + str(len(listurl)) + "个")
            for i in range(0, len(listurl)):
                try:
                    url = listurl[i]
                    # 处理成真实的图片地址url
                    url = "http://" + url
                    print("第" + str(page) + "页,第" + str(i) + "个图片地址入队列")
                    self.urlqueue.put(url)
                    self.urlqueue.task_done()
                except urllib.error.URLError as e:
                    if hasattr(e, "code"):
                        print(e.code)
                    if hasattr(e, "reason"):
                        print(e.reason)
                except Exception as e:
                    print("exception:" + str(e))
            time.sleep(1)


# 线程2,加载图片
class ImgThread(threading.Thread):
    def __init__(self, urlqueue, proxy, thd):
        threading.Thread.__init__(self)
        self.urlqueue = urlqueue
        self.proxy = proxy
        self.thd = thd

    def run(self):
        i = 1
        while (self.thd.is_alive() or not self.urlqueue.empty()):
            try:
                url = self.urlqueue.get()
                print(url)
                if (url):
                    imagename = "/Users/zhouruiyong/Desktop/python/img/" + str(i) + ".jpg"
                    urllib.request.urlretrieve(url, filename=imagename)
                    print("第" + str(i) + "个图片加载")
                    i += 1
            except urllib.error.URLError as e:
                if hasattr(e, "code"):
                    print(e.code)
                if hasattr(e, "reason"):
                    print(e.reason)
            except Exception as e:
                print("exception:" + str(e))


if __name__ == '__main__':
    urlqueue = queue.Queue()
    proxy = "122.224.65.201:3128"
    pagestart = 1
    pageend = 3

    print("程序执行开始")

    # 创建线程1并启动,主要是将爬取的图片地址加入队列
    print("UrlThread 线程。。。")
    t1 = UrlThread(pagestart, pageend, proxy, urlqueue)
    t1.start()

    # 创建线程2,从队列中获取图片地址并加载到本地文件夹
    print("contentThread 线程。。。")
    t2 = ImgThread(urlqueue, proxy, t1)
    t2.start()

    t1.join()
    t2.join()
    print("程序执行完毕!")

这里我们加入了代理访问,如果你选择的代理地址失效,就会反问失败,可以多试几个代理地址。不过因为用代理速度太慢了,所以这里把设置代理的代码注释了。

欢迎关注本人公众号和小程序,谢谢
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值