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 相关文章推荐
python django使用haystack:全文检索的框架(实例讲解)
Sep 27 Python
python web基础之加载静态文件实例
Mar 20 Python
使用pandas读取csv文件的指定列方法
Apr 21 Python
python多线程下信号处理程序示例
May 31 Python
python2 中 unicode 和 str 之间的转换及与python3 str 的区别
Jul 25 Python
tensorflow多维张量计算实例
Feb 11 Python
Pandas —— resample()重采样和asfreq()频度转换方式
Feb 26 Python
使用Django xadmin 实现修改时间选择器为不可输入状态
Mar 30 Python
pandas中的ExcelWriter和ExcelFile的实现方法
Apr 24 Python
Keras自定义IOU方式
Jun 10 Python
pycharm如何使用anaconda中的各种包(操作步骤)
Jul 31 Python
Python 操作SQLite数据库的示例
Oct 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
使用 MySQL 开始 PHP 会话
2006/12/21 PHP
PHP substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
PHP中使用register_shutdown_function函数截获fatal error示例
2015/04/21 PHP
php中file_exists函数使用详解
2015/05/08 PHP
PHP入门教程之上传文件实例详解
2016/09/11 PHP
PHP实现简单计算器小程序
2020/08/28 PHP
PHP过滤器 filter_has_var() 函数用法实例分析
2020/04/23 PHP
jquery 图片 上一张 下一张 链接效果(续篇)
2010/04/20 Javascript
判断JS对象是否拥有某种属性的两种方式
2013/12/02 Javascript
jQuery中insertBefore()方法用法实例
2015/01/08 Javascript
javascript+ajax实现产品页面加载信息
2015/07/09 Javascript
实例解析JS布尔对象的toString()方法和valueOf()方法
2015/10/25 Javascript
JavaScript控制浏览器全屏及各种浏览器全屏模式的方法、属性和事件
2015/12/20 Javascript
完美实现八种js焦点轮播图(上篇)
2016/07/18 Javascript
JS插件plupload.js实现多图上传并显示进度条
2016/11/29 Javascript
JS实现密码框的显示密码和隐藏密码功能示例
2016/12/26 Javascript
基于angular实现模拟微信小程序swiper组件
2017/06/11 Javascript
在小程序Canvas中使用measureText的方法示例
2018/10/19 Javascript
vue配置font-awesome5的方法步骤
2019/01/27 Javascript
layui(1.0.9)文件上传upload,前后端的实例代码
2019/09/26 Javascript
js实现开关灯效果
2020/03/30 Javascript
Vue快速实现通用表单验证功能
2019/12/05 Javascript
vue 如何从单页应用改造成多页应用
2020/10/23 Javascript
Python调用C语言开发的共享库方法实例
2015/03/18 Python
收藏整理的一些Python常用方法和技巧
2015/05/18 Python
Python实现的简单线性回归算法实例分析
2018/12/26 Python
在Python中使用turtle绘制多个同心圆示例
2019/11/23 Python
Python列表操作方法详解
2020/02/09 Python
Python下载的11种姿势(小结)
2020/11/18 Python
解决python3中os.popen()出错的问题
2020/11/19 Python
Python实现FTP文件定时自动下载的步骤
2020/12/19 Python
如何用Matlab和Python读取Netcdf文件
2021/02/19 Python
教你如何一步一步用Canvas写一个贪吃蛇
2018/10/22 HTML / CSS
实习证明模板
2015/06/16 职场文书
Arthas排查Kubernetes中应用频繁挂掉重启异常
2022/02/28 MySQL
vue实现列表垂直无缝滚动
2022/04/08 Vue.js