Python爬取视频(其实是一篇福利)过程解析


Posted in Python onAugust 01, 2019

窗外下着小雨,作为单身程序员的我逛着逛着发现一篇好东西,来自知乎 你都用 Python 来做什么?的第一个高亮答案。

到上面去看了看,地址都是明文的,得,赶紧开始吧。

下载流式文件,requests库中请求的stream设为True就可以啦,文档在此。

先找一个视频地址试验一下:

# -*- coding: utf-8 -*-
import requests 
def download_file(url, path):
  with requests.get(url, stream=True) as r:
    chunk_size = 1024
    content_size = int(r.headers['content-length'])
    print '下载开始'
    with open(path, "wb") as f:
      for chunk in r.iter_content(chunk_size=chunk_size):
        f.write(chunk) 
if __name__ == '__main__':
  url = '就在原帖...'
  path = '想存哪都行'
  download_file(url, path)

遭遇当头一棒:

AttributeError: __exit__

这文档也会骗人的么!

看样子是没有实现上下文需要的__exit__方法。既然只是为了保证要让r最后close以释放连接池,那就使用contextlib的closing特性好了:

# -*- coding: utf-8 -*-
import requests
from contextlib import closing
 
def download_file(url, path):
  with closing(requests.get(url, stream=True)) as r:
    chunk_size = 1024
    content_size = int(r.headers['content-length'])
    print '下载开始'
    with open(path, "wb") as f:
      for chunk in r.iter_content(chunk_size=chunk_size):
        f.write(chunk)

程序正常运行了,不过我盯着这文件,怎么大小不见变啊,到底是完成了多少了呢?还是要让下好的内容及时存进硬盘,还能省点内存是不是:

# -*- coding: utf-8 -*-
import requests
from contextlib import closing
import os
 
def download_file(url, path):
  with closing(requests.get(url, stream=True)) as r:
    chunk_size = 1024
    content_size = int(r.headers['content-length'])
    print '下载开始'
    with open(path, "wb") as f:
      for chunk in r.iter_content(chunk_size=chunk_size):
        f.write(chunk)
        f.flush()
        os.fsync(f.fileno())

文件以肉眼可见的速度在增大,真心疼我的硬盘,还是最后一次写入硬盘吧,程序中记个数就好了:

def download_file(url, path):
  with closing(requests.get(url, stream=True)) as r:
    chunk_size = 1024
    content_size = int(r.headers['content-length'])
    print '下载开始'
    with open(path, "wb") as f:
      n = 1
      for chunk in r.iter_content(chunk_size=chunk_size):
        loaded = n*1024.0/content_size
        f.write(chunk)
        print '已下载{0:%}'.format(loaded)
        n += 1

结果就很直观了:

已下载2.579129%
已下载2.581255%
已下载2.583382%
已下载2.585508%

心怀远大理想的我怎么会只满足于这一个呢,写个类一起使用吧:

# -*- coding: utf-8 -*-
import requests
from contextlib import closing
import time 
def download_file(url, path):
  with closing(requests.get(url, stream=True)) as r:
    chunk_size = 1024*10
    content_size = int(r.headers['content-length'])
    print '下载开始'
    with open(path, "wb") as f:
      p = ProgressData(size = content_size, unit='Kb', block=chunk_size)
      for chunk in r.iter_content(chunk_size=chunk_size):
        f.write(chunk)
        p.output()
 
 
class ProgressData(object):
 
  def __init__(self, block,size, unit, file_name='', ):
    self.file_name = file_name
    self.block = block/1000.0
    self.size = size/1000.0
    self.unit = unit
    self.count = 0
    self.start = time.time()
  def output(self):
    self.end = time.time()
    self.count += 1
    speed = self.block/(self.end-self.start) if (self.end-self.start)>0 else 0
    self.start = time.time()
    loaded = self.count*self.block
    progress = round(loaded/self.size, 4)
    if loaded >= self.size:
      print u'%s下载完成\r\n'%self.file_name
    else:
      print u'{0}下载进度{1:.2f}{2}/{3:.2f}{4} 下载速度{5:.2%} {6:.2f}{7}/s'.\
         format(self.file_name, loaded, self.unit,\
         self.size, self.unit, progress, speed, self.unit)
      print '%50s'%('/'*int((1-progress)*50))

运行:

下载开始
下载进度10.24Kb/120174.05Kb 0.01% 下载速度4.75Kb/s
/////////////////////////////////////////////////
下载进度20.48Kb/120174.05Kb 0.02% 下载速度32.93Kb/s
/////////////////////////////////////////////////

看上去舒服多了。

下面要做的就是多线程同时下载了,主线程生产url放入队列,下载线程获取url:

# -*- coding: utf-8 -*-
import requests
from contextlib import closing
import time
import Queue
import hashlib
import threading
import os 
def download_file(url, path):
  with closing(requests.get(url, stream=True)) as r:
    chunk_size = 1024*10
    content_size = int(r.headers['content-length'])
    if os.path.exists(path) and os.path.getsize(path)>=content_size:
      print '已下载'
      return
    print '下载开始'
    with open(path, "wb") as f:
      p = ProgressData(size = content_size, unit='Kb', block=chunk_size, file_name=path)
      for chunk in r.iter_content(chunk_size=chunk_size):
        f.write(chunk)
        p.output()
 
class ProgressData(object):
 
  def __init__(self, block,size, unit, file_name='', ):
    self.file_name = file_name
    self.block = block/1000.0
    self.size = size/1000.0
    self.unit = unit
    self.count = 0
    self.start = time.time()
  def output(self):
    self.end = time.time()
    self.count += 1
    speed = self.block/(self.end-self.start) if (self.end-self.start)>0 else 0
    self.start = time.time()
    loaded = self.count*self.block
    progress = round(loaded/self.size, 4)
    if loaded >= self.size:
      print u'%s下载完成\r\n'%self.file_name
    else:
      print u'{0}下载进度{1:.2f}{2}/{3:.2f}{4} {5:.2%} 下载速度{6:.2f}{7}/s'.\
         format(self.file_name, loaded, self.unit,\
         self.size, self.unit, progress, speed, self.unit)
      print '%50s'%('/'*int((1-progress)*50))
 queue = Queue.Queue() 
def run():
  while True:
    url = queue.get(timeout=100)
    if url is None:
      print u'全下完啦'
      break
    h = hashlib.md5()
    h.update(url)
    name = h.hexdigest()
    path = 'e:/download/' + name + '.mp4'
    download_file(url, path) 
def get_url():
  queue.put(None)
if __name__ == '__main__':
  get_url()
  for i in xrange(4):
    t = threading.Thread(target=run)
    t.daemon = True
    t.start()

加了重复下载的判断,至于怎么源源不断的生产url,诸位摸索吧,保重身体!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用PDB简单调试Python程序简明指南
Apr 25 Python
Python Django给admin添加Action的方法实例详解
Apr 29 Python
Django重置migrations文件的方法步骤
May 01 Python
计算机二级python学习教程(2) python语言基本语法元素
May 16 Python
使用celery执行Django串行异步任务的方法步骤
Jun 06 Python
分析运行中的 Python 进程详细解析
Jun 22 Python
提升Python效率之使用循环机制代替递归函数
Jul 23 Python
ORM Django 终端打印 SQL 语句实现解析
Aug 09 Python
python框架django项目部署相关知识详解
Nov 04 Python
python实现同一局域网下传输图片
Mar 20 Python
pandas实现导出数据的四种方式
Dec 13 Python
AI:如何训练机器学习的模型
Apr 16 Python
flask框架jinja2模板与模板继承实例分析
Aug 01 #Python
Win10环境python3.7安装dlib模块趟过的坑
Aug 01 #Python
python爬虫解决验证码的思路及示例
Aug 01 #Python
Django多数据库的实现过程详解
Aug 01 #Python
Python解决pip install时出现的Could not fetch URL问题
Aug 01 #Python
numpy.meshgrid()理解(小结)
Aug 01 #Python
Python-接口开发入门解析
Aug 01 #Python
You might like
php实现上传图片生成缩略图示例
2014/04/13 PHP
PHP微信开发之二维码生成类
2015/06/26 PHP
php禁用函数设置及查看方法详解
2016/07/25 PHP
常见JS效果之图片减速度滚动实现代码
2011/12/08 Javascript
在JavaScript中构建ArrayList示例代码
2014/09/17 Javascript
moment.js轻松实现获取当前日期是当年的第几周
2015/02/05 Javascript
js实现不提交表单获取单选按钮值的方法
2015/08/21 Javascript
11种ASP连接数据库的方法
2015/09/18 Javascript
适用于javascript开发者的Processing.js入门教程
2016/02/24 Javascript
js实现页面跳转的五种方法推荐
2016/03/10 Javascript
基于jQuery实现多标签页切换的效果(web前端开发)
2016/07/24 Javascript
JS实现简单的tab切换选项卡效果
2016/09/21 Javascript
bootstrap侧边栏圆点导航
2017/01/11 Javascript
详解A标签中href=""的几种用法
2017/08/20 Javascript
React操作真实DOM实现动态吸底部的示例
2017/10/23 Javascript
QML实现圆环颜色选择器
2019/09/25 Javascript
vue-cli中实现响应式布局的方法
2021/03/02 Vue.js
[00:20]DOTA2荣耀之路7:-ah fu-抢盾
2018/05/31 DOTA
Mac下Supervisor进程监控管理工具的安装与配置
2014/12/16 Python
Python的Django框架中forms表单类的使用方法详解
2016/06/21 Python
Python变量赋值的秘密分享
2018/04/03 Python
Python3.6实现连接mysql或mariadb的方法分析
2018/05/18 Python
kaggle+mnist实现手写字体识别
2018/07/26 Python
python抓取京东小米8手机配置信息
2018/11/13 Python
解决django服务器重启端口被占用的问题
2019/07/26 Python
使用python实现滑动验证码功能
2019/08/05 Python
使用Keras加载含有自定义层或函数的模型操作
2020/06/10 Python
Spartoo比利时:欧洲时尚购物网站
2017/12/06 全球购物
Carolina Lemke Berlin澳大利亚官网:时尚太阳镜品牌
2019/09/17 全球购物
C#的几个面试问题
2016/05/22 面试题
某公司面试题
2012/03/05 面试题
初中考试作弊检讨书
2014/02/01 职场文书
葬礼司仪主持词
2014/03/31 职场文书
查摆问题自我剖析材料
2014/08/18 职场文书
2015年电工工作总结
2015/04/10 职场文书
旗帜观后感
2015/06/08 职场文书