网站首页
手机版

Python实用案例编程入门:第九章 爬虫下载VOA每日广播英语MP3?python中的广播

更新时间:2024-03-30 13:55作者:小乐

本章的主题是介绍如何利用爬虫的方法下载VOA每日广播的英文MP3文件,解决我们生活中遇到的实际问题。

9.1 应解决什么问题?首先介绍一个练习英语听力的好网站:https://learningenglish.voanews.com/。它有满足不同聆听需求的部分。每天还有30分钟的广播,可以帮助我们练习沉浸式听力。是美式英语播音员,速度比较慢,适合学习者练习。还有一些视频教程,我个人觉得不错,推荐给大家(缺点是需要通过代理访问网站)。

我在使用它时遇到的一个问题是我每次都在线收听。在手机上听会比较麻烦,所以就想过下载,但又觉得一一点击下载太麻烦了。所以我决定写一个Python程序来帮助我下载它。这就是了解如何使用Python的好处。

在开始一个实际的程序之前,我们需要思考我们的程序想要实现什么功能?

从上述网站下载mp3文件;下载设定日期内所有播放的mp3文件;支持在命令行传递开始和结束日期,如20190101 20190530;如果在命令行上传递日期,则该日期将用作开始日期,当天的时间用作截止日期;如果命令行中没有传递日期,则将下载上次到当天之间的mp3文件。如果没有最后一次,则只下载当天的mp3文件;代理设置,我们如何使用代理呢?需要正确设置对应的url和端口,然后将其赋值给下载功能中使用的proxy参数;基于我们想要实现的功能,我们开始思考如何实现。

9.2 实现思路这里我们简单想象一下如何实现。总体思路如下:

解析参数,确定下载日期,存入列表;将下载日期和下载链接拼接成下载链接;如果链接合法,则下载,否则创建一个以日期为名称的txt文件,并将下载链接存储在其中;保存当天的日期;需要先解析下载地址,先手动找到下载地址。

http://av.voanews.com/clips/VLE/2019/06/15/20190615-003000-VLE122-program_hq.mp3 download=1,就是这个样子,可以看到里面包含了2019/06/15/20190615这样的字符string,我可以推断任意一天的下载地址只是另一个固定字符串加上日期。随机找了一天去验证一下,发现确实如此。那么就简单了,不需要爬行。

下载过程中,由于每个文件大约30MB,下载需要几秒钟的时间(当然也取决于网速),所以我们的下载必须使用流式下载,所以在下面的函数中,stream=True就是使用流式下载下载中。

def download_file(file_url, file_name):'''下载文件'''with requests.get(file_url, stream=True, proxies=None) 作为响应, open(file_name, 'wb') as local_file:shutil.copyfileobj(response.raw ,local_file)9.3 相关模块的安装及介绍本节将介绍程序中用到的相关模块。

9.3.1shutil模块shutil模块主要用于文件处理,比如最基本的文件操作、删除、移动、复制、压缩解压等,如果涉及到文件相关的操作,首先应该想到这个模块。

我们这里使用该模块的copyfileobject方法将流数据对象的内容复制到我们创建的MP3文件中。

def download_file(file_url, file_name):'''下载文件'''with requests.get(file_url, stream=True, proxies=None) 作为响应, open(file_name, 'wb') as local_file:shutil.copyfileobj(response.raw , local_file) 9.3.2 datetime 模块datetime 模块用于处理与日期相关的事务。这里我们将使用该模块的strptime() 接口来构造一个日期字符串。

def handle_parameters(parameters):end_date=datetime.datetime.strptime(time.strftime('%Y%m%d',time.localtime(time.time())), '%Y%m%d')begin_date=end_date #begin_date=datetime.datetime.strptime('20190701', '%Y%m%d')if len(parameters)=3:begin_date=datetime.datetime.strptime(parameters[1], '%Y%m%d') if is_valid_data(parameters[1]) else exit(-1)end_date=datetime.datetime.strptime(parameters[2], '%Y%m%d') if is_valid_data(parameters[2]) else exit(-1) elif len(parameters)=2:begin_date=datetime.datetime.strptime(parameters[1], '%Y%m%d') if is_valid_data(sys.argv[1]) else exit(-1)else:#if 有上次,我们使用它作为begin_date,否则我们使用end_date as begin_date.begin_date=get_last_date(last_date_file, begin_date)return begin_date, end_date 在我们的程序中,我们需要获取起始日期范围内的所有日期字符串。如果人工构造起来非常困难,但是基于datetime的timedelta()就可以相对容易地完成。同时,您也可以根据自己的需要设置间隔时间。这里我们需要每天,所以设置days=1。

def get_file_list(begin_date, end_date):'''获取我们要下载的所有文件''' date_list=[] while begin_date=end_date: date_dir=begin_date.strftime('%Y/%m/%d/') date_str=begin_date.strftime('%Y%m%d') date_list.append(date_dir+date_str) begin_date +=datetime.timedelta(days=1)return date_list9.3.3 shelve 模块shelve 模块是一个比较实用的模块,我们使用它打开文件并像字典一样读写数据。如果有程序需要保存一些临时数据,或者数据不大,可以使用这个模块。例如,我们在程序中使用此模块来存储最后的日期,以防止用户再次重新输入。

def store_current_date(file, date):'''将当前日期存储为下一个开始日期''' s=shelve.open(file, writeback=True) s[last_date_key]=date s.close() 上面的代码是什么我们使用来存储日期,下面看看当我们需要使用日期时如何获取日期。

def get_last_date(file, default_date):'''从文件中获取最后日期''' date=default_date if os.path.exists(file): s=shelve.open(file, writeback=True) last_date=s[last_date_key] s.close() date=datetime.datetime.strptime(last_date, '%Y%m%d')return date9.3.4 时间模块时间模块提供了各种与时间相关的函数。

time.asctime() 函数可以将结构体struct_time 表示的时间转换为类似'Sun Jun 19 13:31:15 1994' 的字符串。我们可以通过time.localtime()函数获取结构体struct_time。

time.sleep()函数的输入参数单位为秒。如果需要挂起线程,可以通过调用该函数来达到目的。这里的输入参数还可以是小数,表示更精确的睡眠时间。我们将使用此函数进行必要的等待,以确保另一件事完成。

time.strftime() 函数也用于格式化时间。

导入时间time.strftime('%Y%m%d',time.localtime(time.time()))'20190714' time.strftime('%Y-%m-%d')'2019-07-14 '另一个常见的操作是使用time.strptime() 接口来确定给定的字符串是否是有效的日期。

def is_valid_data(date_str):'''检查日期字符串是否有效'''try: time.strptime(date_str, '%Y%m%d') return True except:return False9.3.5 sys 模块sys 模块是内置模块,不需要单独安装。

sys模块提供了对Python解释器使用的一些变量的访问,并且可以进行一些修改,比如读取和修改环境变量PATH,并提供了一定的与解释器交互的函数,以便我们的程序可以与解释器进行交互。

例如sys.argv会将命令行参数以列表的形式传递给Python脚本,sys.argv[0]是脚本的名称,sys.argv[1]是第一个参数,依此类推。

sys.exit()表示退出程序,也可以带参数表示退出码。如果有其他程序调用该程序,则可以通过返回的数字确定被调用程序的退出原因。

sys.implementation 查看当前运行的Python解释器的版本信息。

sys.implementationnamespace(cache_tag='cpython-36', hexversion=50726384, name='cpython', version=sys.version_info(major=3,minor=6,micro=5,releaselevel='final',serial=0) )sys.stdin、sys.stddout、sys.stderr 标准输入、标准输出和错误的解释器。

9.3.6 os 模块os 模块是一个比较常用的模块。从名字就可以看出它与操作系统有关。例如,当需要获取当前工作目录时,可以使用getcwd()接口。您还可以轻松分离路径的文件夹部分和文件名部分。

import os os.getcwd()'C:\\Users\\just right' os.path.abspath('.')'C:\\Users\\just right' path=r'E:\第09章爬虫每日下载voa广播英文MP3文件\auto-download-voa-broadcast.py' os.path.split(path)('E: \\第09章爬虫下载voa每日广播英文MP3文件', 'auto-download-voa-broadcast.py ') 我们这里的程序将使用os 模块来判断文件是否存在。代码如下。

path=r'E:\第09章爬虫下载voa每日广播英文MP3文件\auto-download-voa-broadcast.py' os.path.exists(path)True9.3.7 requests模块requests模块是用于访问网络模块,我们这里的程序需要先进行用户认证。通过用户名和密码的认证后,运行我们的程序来访问数据库,并对数据库进行相应的读写操作。

requests 模块还可以用于登录网页。这方面将在其他相关程序示例中介绍。这里我们只关注当前示例程序需要使用的内容。

以下代码片段是我们程序中用于确定链接有效性的函数。我们将链接和代理参数传递给get接口,然后使用接口返回的文本来判断链接是否有效。在我们的示例中,如果链接无效,页面文本将包含字符串“404 - 未找到文件或目录”。

def is_valid_link(file_url, invalid_msg):'''检查mp3_url 是否有效''' text='' return True with requests.get(file_url, proxies=http_proxies) as response: print(response.text) text=response.textreturn True if invalid_msg in text else False 以下函数展示了如何通过requests 模块下载文件,将Stream 参数设置为True,并在必要时为您自己的代理配置设置代理参数proxies。

def download_file(file_url, file_name):'''下载文件'''with requests.get(file_url, stream=True, proxies=None) 作为响应, open(file_name, 'wb') as local_file:shutil.copyfileobj(response.raw ,本地文件)

9.4 代码实现介绍完了相关模块,我们来看看代码是如何实现的。

9.4.1 编写伪代码我们首先编写必要的伪代码来了解程序的整体结构。

# 定义函数is_valid_link 来判断链接是否有效# 定义函数is_valid_date 来判断给定的字符是否是有效日期# 定义函数download() 来下载文件# 定义函数get_file_list() 来获取一天内的每一天给定日期范围日期字符串# 定义函数get_last_date() 用于从配置文件中获取最后日期# 定义函数store_current_date() 用于存储日期# 定义函数handle_parameters() 用于处理程序的参数if '__main__'==__name__: 利用handle_parameters()处理程序参数通过get_file_list()获取日期字符串列表,遍历日期字符串列表,并将其转换为url链接。如果url有效,则下载,否则创建txt文件,记录失败原因。 9.4.2 Python代码Python代码实现如下。

# -*-coding: utf-8 -*-'''此工具帮助每天自动下载voa广播。1。下载从上次到当天的所有mp3 文件。2. export http_proxy=''如何实现:#获取最后一次下载的日期#准备下载该时间段内的所有mp3文件#检查mp3的每个链接是否有效#如果有效则下载,或者使用txt文件代替#保存当前日期创建于Fri Jun 15 14:17:40 2019 @author: ggang.liu'''import Shutilimport datetimeimport shelveimport timeimport sysimport osimport requestsserver_error='404 - 文件或目录未找到'last_date_file='last_date'last_date_key='date'url_template='http://av .voanews.com/clips/VLE/{ 0}-003000-VLE122-program_hq.mp3 download=1'def is_valid_link(file_url, invalid_msg):'''检查mp3_url 是否有效'''text=''return Truewith requests .get(file_url, proxies=http_proxies) as response:print(response.text)text=response.textreturn True if invalid_msg in text else Falsedef is_valid_data(date_str):'''检查日期字符串是否有效'''try:time.strptime(date_str , '%Y%m%d') return True except:return Falsedef download_file(file_url, file_name):'''下载文件'''with requests.get(file_url, stream=True, proxies=None) 作为响应, open(file_name, 'wb') as local_file:shutil.copyfileobj(response .raw, local_file)def get_file_list(begin_date, end_date):'''获取我们要下载的所有文件'''date_list=[]while begin_date=end_date:date_dir=begin_date.strftime( '%Y/%m/%d/')date_str=begin_date.strftime('%Y%m%d')date_list.append(date_dir+date_str)begin_date +=datetime.timedelta(days=1)return date_listdef get_last_date( file, default_date):'''从文件中获取最后日期'''date=default_dateif os.path.exists(file):s=shelve.open(file, writeback=True)last_date=s[last_date_key]s.close() date=datetime.datetime.strptime(last_date, '%Y%m%d')return datedef store_current_date(file, date):'''将当前日期存储为下一个开始日期''s=shelve.open(file, writeback=True)s[last_date_key]=日期.close()def handle_parameters(parameters):end_date=datetime.datetime.strptime(time.strftime('%Y%m%d',time.localtime(time.time())) ), '%Y%m%d' )begin_date=end_date#begin_date=datetime.datetime.strptime('20190701', '%Y%m%d')if len(参数)=3:begin_date=datetime.datetime.strptime(参数[1], '%Y%m %d') if is_valid_data(parameters[1]) else exit(-1)end_date=datetime.datetime.strptime(parameters[2], '%Y%m%d') if is_valid_data(parameters[2]) else exit (-1)elif len(parameters)=2:begin_date=datetime.datetime.strptime(parameters[1], '%Y%m%d') if is_valid_data(sys.argv[1] ]) else exit(-1)else:# 如果有最后一次,则将其用作begin_date,否则将end_date 用作begin_date.begin_date=get_last_date(last_date_file, begin_date)return begin_date, end_dateif '__main__'==__name__:begin_date, end_date=handle_parameters(sys.argv)#获取日期listdate_list=get_file_list(begin_date, end_date)print(date_list)for date_list中的日期:file_url=url_template.format(date)if is_valid_link(file_url, server_error):print('正在下载:' + file_url)download_file( file_url, 'voa_broadcast_'+date[-8: ]+'.mp3')else:with open(date[-8:]+'.txt', 'w') as f:f.write(file_url)9.5 本章小结一般来说,思路比较简单,就是将要下载的日期构造成下载链接,然后一一下载。 Python是一门非常好的语言,可以极大的帮助我们节省开发时间。

学以致用才是正确的出路。这也是编程的乐趣所在。

欢迎关注、转发、点赞

Python编程入门实践案例:第一章Python概述以及为什么学习Python

Python编程入门实践案例:第二章字符串

Python实用案例编程简介:第3章列表和元组

Python实用案例编程入门:第4章字典和文件

Python实用案例编程入门:第6章控制流语句

Python实用案例编程入门:第五章函数和类

Python编程入门实战案例:第七章调试方法

Python实用案例编程入门:第八章如何自动连接WIFI

为您推荐

解读信用卡背后神秘数字的含义——安全码?信用卡安全吗

信用卡的安全码你知道多少?640 wx_fmt=png.jpg (13.02 KB, 下载次数: 18)下载附件6 天前 上传知识 信用卡安全码,是信用卡在进行网络或电话交易时的一个安全代码。它通常是印刷在信用卡上面的3或4位数字,不同类别

2024-03-30 13:46

新兴支付技术将彻底变革传统支付方式?注重安全性的Visa可不这么看

全球最安全的支付系统是什么?5月,在新加坡举行的2018 VISA亚太区支付安全峰会 (2018 Visa Asia Pacific Security Summit)上,超过60岁的Visa与其合作方们,针对全球支付领域最先进的物联网支付技

2024-03-30 13:32

支付数据安全是Visa推动亚太地区数字经济发展的主要驱动力(支付卡行业数据安全标准)

第15届Visa亚太区安全峰会在中国上海举行,围绕网络安全、生物识别技术和更安全的电子商务支付展开交流上海2019年6月20日 /美通社/ -- 当下,电子商务正以更加安全、先进和便捷的趋势迅猛发展。面对这一趋势,Visa 承诺将持续致力于

2024-03-30 13:20

信用卡的安全码是什么?有什么用?(行用卡的安全码)

信用卡是当今社会不可或缺的一种消费工具,信用卡上有一串神秘的数字,这串数字就是安全码,那么信用卡的安全码是什么?有什么用?一、信用卡安全码信用卡安全码,是信用卡在进行网络或电话交易时的一个安全代码。它通常是印刷在信用卡上面的3或4位数字,不

2024-03-30 13:14

信用卡安全码知多少?信用卡背后的密码了解一下

除了我们日常所知的信用卡交易密码、查询密码,信用卡其实还有一个密码叫安全码,也叫CVV码、后三码,这个码相当于我们的交易密码,很多盗刷都是通过这个密码完成的,所以日常要注意保护安全码避免泄露。信用卡安全码是什么信用卡安全码,是信用卡在进行网

2024-03-30 13:06

你的芯片卡安全吗?这可能主要取决于你的银行账户,芯片卡的安全性

知名安全网站 KrebsOnSecurity近日发文称,以芯片为基础的信用卡和借记卡的设计,是为了让盗刷设备或恶意软件无法在你通过蘸取芯片而非刷卡条付款时克隆你的卡。但最近一系列针对美国商户的恶意软件攻击表明,盗贼正在利用某些金融机构实施该

2024-03-30 12:53

加载中...

热门文章

SQL Error: select * from ***_ecms_news where titlepic<>'' order by onclick desc limit 1
    SQL Error: select * from ***_ecms_news order by onclick desc limit 1,7

热门推荐

SQL Error: select * from ***_ecms_news where titlepic<>'' and istop=1 order by newstime desc limit 5