妹子说头像爬的太慢?升级到多线程程序爬取头像

需求分析

昨天,我给妹子做了一个爬取头像的爬虫程序,妹子总算是开心起来了,我总算也是松了一口气,然而今天,就接到妹子的投诉了

她说,“你这个程序啊,太慢!爬取一次要花的时间太长!你能不能给改进一下?”

害,这有什么困难的呢?妹子不知道,我在程序中加了time.sleep(),只需要把time.sleep()删除掉,速度就加快了。不过呢,这样做虽然有效,但是毕竟能够加快的速度有限。如果我们希望更快,就得升级到多线程的爬虫程序,下面就来试试看吧

实现分析

首先呢,我们需要知道在python中如何实现多线程,这个其实很简单,只需要使用threading就可以了

from threading import Thread 
import time


def thread_test():
    for i in range(10):
        print(i)
        time.sleep(1)

# 启动一个线程,让该线程执行target指定的任务
t = Thread(target=thread_test)
t.start()

线程之间是可以共享全局变量的,但是如果同时对全局变量进行修改的话,可能就会出现一些问题,因此,在爬虫中使用的话,我们不能够直接通过list保存地址,然后通过线程去list中取得地址,这样会由于同时对列表进行操作,而产生冲突。冲突了以后自然就会导致结果不正确

因此,在线程的使用中,我们可以考虑通过queue来完成爬虫任务,首先启动一个线程,把爬取到的图片地址加入到queue中,然后再启动多个线程,去爬取queue中保存的图片地址,以此来实现多线程的爬虫程序,大致结构就像这样

from threading import Thread
from queue import Queue
import time

q = Queue()

def put():
    for i in range(30):
        time.sleep(0.2)
        q.put(i)

def get():
    while True:
        print(q.get())
        time.sleep(1)


t = Thread(target=put)
t.start()

t1 = Thread(target=get)
t2 = Thread(target=get)
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()

time.sleep(20)

完整代码实现

import requests
import time
from base64 import b64decode
from lxml import etree
from threading import Thread
from queue import Queue

url_queue = Queue() 

headers = {"user-agent": "Mozilla/5.0"}
url = b64decode("aHR0cDovL3d3dy5pbWVpdG91LmNvbS9kb25nbWFuLw==").decode()

r = requests.get(url, headers=headers)
r.encoding = "gb2312" 

html = etree.HTML(r.text)

urls = html.xpath("//ul[@class='g-gxlist-imgbox']//li/a/@href")
titles = html.xpath("//ul[@class='g-gxlist-imgbox']//li/a/@title")


def get_urls(): 
    num = 1
    for i in range(len(urls)):
        url = urls[i]
        r = requests.get(url, headers=headers)
        r.encoding = "gb2312"
        html = etree.HTML(r.text)
        img = html.xpath("//div[@class='img-list3']//img/@src")
        for u in img:
            name = str(num) + ".png"
            url_queue.put({"name": name, "url": u})
            num += 1
        time.sleep(1)


def get_img():
    while True:
        u = url_queue.get()
        url = u["url"]
        name = u["name"]
        r = requests.get(url, headers=headers)
        with open(name, "wb") as f:
            f.write(r.content)
        time.sleep(1)
        url_queue.task_done()


def get_info():
    while True:
        time.sleep(10)
        print("目前还剩下" + str(url_queue.qsize()) + "个头像未完成")


def run(num):
    if num > 4:
        num = 4
    tu = Thread(target=get_urls)
    tu.start()

    ti = Thread(target=get_info)
    ti.setDaemon(True)
    ti.start()

    ts = []
    for i in range(num):
        t = Thread(target=get_img)
        t.setDaemon(True)
        ts.append(t)

    for t in ts:
        t.start()
    tu.join()

while True:
    n = input("你希望每秒能爬几张头像:")
    try:
        run(int(n))
        url_queue.join()
        break
    except:
        continue

程序运行效果展示,可以看到,由于是多线程的程序,添加待爬取的头像和爬取头像分别是由不同的线程完成的,因此在剩余数量上就可能出现有时上升,有时下降的情况。添加待爬取的头像的线程工作的快一些,那么剩余的头像数量就会增多,爬取头像的线程工作快一些,那么剩余的头像数量就会减少

 很快,我们就可以看到我们的头像图片已经成功的爬取完成了​​​​​​​,运行程序即可查看结果


注意!虽然我的程序是多线程的程序,而制作多线程程序当然也是为了能够更快爬取,但是我个人是不建议你这样做的。爬取速度太快会给对方网站带来很大的压力,这是很不道德的行为,如果有可能的话,请尽量保持发送请求的间隔时间在1秒以上!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值