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 相关文章推荐
Pycharm学习教程(1) 定制外观
May 02 Python
基于python3 类的属性、方法、封装、继承实例讲解
Sep 19 Python
matplotlib调整子图间距,调整整体空白的方法
Aug 03 Python
利用Python将每日一句定时推送至微信的实现方法
Aug 13 Python
python实现ID3决策树算法
Aug 29 Python
python3+selenium实现qq邮箱登陆并发送邮件功能
Jan 23 Python
代码详解django中数据库设置
Jan 28 Python
详解python中的模块及包导入
Aug 30 Python
通过 Django Pagination 实现简单分页功能
Nov 11 Python
jupyter notebook运行命令显示[*](解决办法)
May 18 Python
windows+vscode安装paddleOCR运行环境的步骤
Nov 11 Python
python中__slots__节约内存的具体做法
Jul 04 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 图像尺寸调整代码
2010/05/26 PHP
PHP 在5.1.* 和5.2.*之间 PDO数据库操作中的不同之处小结
2012/03/07 PHP
php通过字符串调用函数示例
2014/03/02 PHP
基于php的CMS中展示文章类实例分析
2015/06/18 PHP
php使用ffmpeg向视频中添加文字字幕的实现方法
2016/05/23 PHP
php unlink()函数使用教程
2018/07/12 PHP
JavaScript使用cookie
2007/02/02 Javascript
javascript与CSS复习(《精通javascript》)
2010/06/29 Javascript
JS的事件绑定深入认识
2014/06/26 Javascript
jQuery处理图片加载失败的常用方法
2015/06/08 Javascript
JavaScript中的fontsize()方法使用详解
2015/06/08 Javascript
javascript实现动态导入js与css等静态资源文件的方法
2015/07/25 Javascript
jQuery实现简洁的导航菜单效果
2015/11/23 Javascript
JQuery实现Ajax加载图片的方法
2015/12/24 Javascript
JS加载iFrame出现空白问题的解决办法
2016/05/13 Javascript
javascript回调函数的概念理解与用法分析
2017/05/27 Javascript
详解在 Angular 项目中添加 clean-blog 模板
2017/07/04 Javascript
jQuery上传插件webupload使用方法
2017/08/01 jQuery
js 提取某()特殊字符串长度的实例
2017/12/06 Javascript
VUEJS 2.0 子组件访问/调用父组件的实例
2018/02/10 Javascript
apicloud拉起小程序并传递参数的方法示例
2018/11/21 Javascript
Cookbook组件形式:优化 Vue 组件的运行时性能
2018/11/25 Javascript
javascript定时器的简单应用示例【控制方块移动】
2019/06/17 Javascript
SpringBoot+Vue 前后端合并部署的配置方法
2020/12/30 Vue.js
[04:03]2014DOTA2西雅图国际邀请赛 LGD战队巡礼
2014/07/07 DOTA
Python3中的2to3转换工具使用示例
2015/06/12 Python
python3.7.0的安装步骤
2018/08/27 Python
python 删除字符串中连续多个空格并保留一个的方法
2018/12/22 Python
我喜欢你 抖音表白程序python版
2019/04/07 Python
Python队列RabbitMQ 使用方法实例记录
2019/08/05 Python
matplotlib基础绘图命令之bar的使用方法
2020/08/13 Python
CSS3 实现雷达扫描图的示例代码
2020/09/21 HTML / CSS
德国旅行、体验和活动的预订平台:Watado
2019/12/04 全球购物
村官学习十八大感想
2014/01/15 职场文书
党支部遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
《蚂蚁和蝈蝈》教学反思
2016/02/22 职场文书