python爬虫----多线程下载文件

涉及知识点:

  • xpath解析
  • requests请求参数auth,stream
  • 消息队列Queue
  • 多线程
  • contextlib.closing(上下文管理器)
  • url解码:from urllib.parse import unquote

参考博客:
https://www.cnblogs.com/chen0307/articles/9706977.html
https://blog.csdn.net/emaste_r/article/details/78105713
https://www.cnblogs.com/nul1/p/9172068.html
https://blog.csdn.net/qq_33876553/article/details/79730246

import requests, time
from lxml import etree
from multiprocessing import Queue, Process
from threading import Thread
from urllib.parse import unquote

# 待解决问题
# 文件下载不完整的问题(目前通过比较文件大小判断是否下载完整,可以支持断点续传吗?)
# 写日志记录源文件大小和下载后的文件大小
# closing的作用?

class Code_spider(object):
    def __init__(self):
        self.url = 'http://****/****/****/'
        self.headers = {'User-Agent': 'Mozilla/5.0'}
        self.folder = 'C:\\Users\\77962\\Desktop\\****/****/****/'
        # 创建消息队列,存放下载链接
        self.q = Queue()


    def work(self):
        self.get_link()
        # 开启多线程,5线程用时677s,10线程用时148s
        L = []
        for i in range(10):
            th = Thread(target=self.down_load, args=(i+1,))
            # th = Process(target=spider.down_load, args=(i+1,))
            th.start()
            L.append(th)

        for th in L:
            th.join()
        

    # 获取页面上的下载链接
    def get_link(self):
        res = requests.get(self.url,headers=self.headers,auth=('****','****'))
        res.encoding = 'utf-8'
        html = res.text

        parseHtml = etree.HTML(html)
        r_list = parseHtml.xpath('//a/@href')[1:]

        for link in r_list:
            self.q.put(link)
        print("本次将下载",self.q.qsize(),"个文件")

        # 多线程循环执行的下载函数
    def down_load(self,n):
        while True:
            try:
                # 中文不能出现在链接地址中,因此会进行url编码,此处使用unquote进行解码
                filename = unquote(self.q.get(block=True,timeout=2))
            except:
                print('取不到链接了,%s号线程结束工作'%n)
                break
            link = self.url + filename
            # closing:可用于创建上下文管理器(保持下载连接?)
            # 设置stream=true时,并不会立即下载(但已获取响应头)
            # 会在使用iter_content或iter_lines遍历内容或访问内容属性时才开始下载。
            # 需要注意一点:文件没有下载之前,它也需要保持连接。
            with closing(requests.get(link,headers=self.headers,stream=True,auth=('tarenacode','code_2014'))) as response:
                chunk_size = 2048    # 设置单次请求大小
                total_size = int(response.headers['content-length'])  # 下载内容的总大小
                data_count = 0  # 已下载大小
                percent = 0    # 记录下载百分比
                with open(self.folder+filename,'wb') as fw:
                    # 循环下载并写入磁盘
                    for data in response.iter_content(chunk_size=chunk_size):
                        last_percent = percent
                        fw.write(data)
                        data_count += len(data)
                        percent = int((data_count/total_size)*100)
                        # 下载总进度有更新则打印
                        if percent > last_percent:
                            print("线程%s正在下载:%s:已完成%s%%(%s/%s)"%(n,filename,percent,data_count,total_size))
                # 判断文件是否下载完整
                if total_size == data_count:
                    print("下载完成,文件名:",filename," 总大小",total_size," 下载完成大小",data_count)
                else:
                    self.q.put(link)


if __name__ == '__main__':
    start_time = time.time()

    spider = Code_spider()
    spider.work()

    end_time = time.time()

    print("爬取总时间:",end_time-start_time)

消息队列

消息队列 : 存储模型,线性的,先进先出原则
原理 : 在内存中建立队列模型,进程通过队列对象将消息存入队列,或从队列取出消息,完成进程间通信

from multiprocessing import Queue

q = Queue(maxsize)  
功能: 创建队列对象
参数: 表示队列中最多存放消息个数
返回值: 队列对象

q.put(data,[block,timeout])
功能:向队列存入消息
参数:data  要存入的内容
      block 默认队列满时会阻塞,设置为False则为非阻塞
      timeout  超时时间

q.get([block,timeout])
功能: 从队列取出消息
参数: block 默认队列为空会阻塞,设置为False则非阻塞
       timeout 超时时间
返回值:取出的内容

q.full()  判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中消息个数
q.close() 关闭队列

url编解码

from urllib.parse import unquote, quote

filename1 = "day07%e4%bb%a3%e7%a0%81-1811-springboot-easymall.zip"
filename2 = "1811-springboot-seckill.zip"

# 解码
name1 = unquote(filename1)
name2 = unquote(filename2)
print(name1,"---",name2)

# 编码
url = "Spark内核模块知识点梳理.xlsx"
url1 = quote(url, "utf-8")
print(url1)

你可能感兴趣的:(爬虫)