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之从if开始语句的征程
Sep 14 Python
在Python中利用Into包整洁地进行数据迁移的教程
Mar 30 Python
详解在Python程序中自定义异常的方法
Oct 16 Python
Python中Selenium模拟JQuery滑动解锁实例
Jul 26 Python
Python实现PS滤镜碎片特效功能示例
Jan 24 Python
python实现简易版计算器
Jun 22 Python
Python实现抢购IPhone手机
Feb 07 Python
几种实用的pythonic语法实例代码
Feb 24 Python
详解numpy.meshgrid()方法使用
Aug 01 Python
python处理document文档保留原样式
Sep 23 Python
Python实现投影法分割图像示例(二)
Jan 17 Python
Python 实现Mac 屏幕截图详解
Oct 05 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
yii框架中的Url生产问题小结
2012/01/16 PHP
浅析php变量修饰符static的使用
2013/06/28 PHP
Swoole-1.7.22 版本已发布,修复PHP7相关问题
2015/12/31 PHP
PHP给源代码加密的几种方法汇总(推荐)
2018/02/06 PHP
ThinkPHP类似AOP思想的参数验证的实现方法
2019/12/18 PHP
Google Suggest ;-) 基于js的动态下拉菜单
2006/10/11 Javascript
不用ajax实现点击文字即可编辑的方法
2007/12/16 Javascript
JQuery for与each性能比较分析
2013/05/14 Javascript
jQuery对下拉框,单选框,多选框的操作
2014/02/21 Javascript
Eclipse引入jquery报错如何解决
2015/12/01 Javascript
用 js 的 selection range 操作选择区域内容和图片
2017/04/18 Javascript
Angularjs 实现动态添加控件功能
2017/05/25 Javascript
详解Vue路由开启keep-alive时的注意点
2017/06/20 Javascript
JS判断数组里是否有重复元素的方法小结
2019/05/21 Javascript
微信小程序解析富文本过程详解
2019/07/13 Javascript
[04:07]显微镜下的DOTA2第八期——英雄复活动作
2014/06/24 DOTA
python调用java的Webservice示例
2014/03/10 Python
python 实现自动远程登陆scp文件实例代码
2017/03/13 Python
Python实现二维数组输出为图片
2018/04/03 Python
使用python实现http及ftp服务进行数据传输的方法
2018/10/26 Python
python 字符串常用函数详解
2019/09/11 Python
postman和python mock测试过程图解
2020/02/22 Python
python3 re返回形式总结
2020/11/20 Python
基于django和dropzone.js实现上传文件
2020/11/24 Python
Perfume’s Club澳大利亚官网:西班牙领先的在线美容店
2021/02/01 全球购物
品质口号大全
2014/06/17 职场文书
舞蹈教育学专业求职信
2014/06/29 职场文书
领导干部作风建设工作总结
2014/10/23 职场文书
2014年高三班主任工作总结
2014/12/05 职场文书
伏羲庙导游词
2015/02/09 职场文书
干货:如何写好工作总结报告!
2019/05/10 职场文书
导游词之无锡唐城
2019/12/12 职场文书
浅谈Nginx 中的两种限流方式
2021/03/31 Servers
sql查询结果列拼接成逗号分隔的字符串方法
2021/05/25 SQL Server
java固定大小队列的几种实现方式详解
2021/07/15 Java/Android
css中有哪些方式可以隐藏页面元素及区别
2022/06/16 HTML / CSS