短视频资讯平台
一、软件需求
-
有video.csv视频库文件,其中有999条短视频数据,格式:id, title, url
-
项目的核心功能有:查看、搜索、下载
-
分页看新闻(每页显示10条),提示用户输入页码,根据页码显示指定页面的数据。
- 提示用户输入页码,根据页码显示指定页面的数据。
- 当用户输入的页码不存在时,默认显示第1页。
-
搜索专区
- 用户输入关键字,根据关键词筛选出所有匹配成功的短视频资讯。
- 支持的搜索两种搜索格式:
id=1715025
,筛选出id等于1715025的视频(video.csv的第一列)。key=文本
,模糊搜索,筛选包含关键字的所有新闻(video.csv的第二列)。
-
下载专区
-
用户输入视频id,根据id找到对应的mp4视频下载地址,然后下载视频到项目的files目录。
-
视频的文件名为:
视频id-年-月-日-时-分-秒.mp4
-
视频下载代码示例
import requests res = requests.get( url='https://video.pearvideo.com/mp4/adshort/20210105/cont-1715046-15562045_adpkg-ad_hd.mp4' ) # 视频总大小(字节) file_size = int(res.headers['Content-Length']) download_size = 0 with open('xxx.mp4', mode='wb') as file_object: # 分块读取下载的视频文件(最多一次读128字节),并逐一写入到文件中。 len(chunk)表示实际读取到每块的视频文件大小。 for chunk in res.iter_content(128): download_size += len(chunk) file_object.write(chunk) file_object.flush() message = "视频总大小为:{}字节,已下载{}字节。".format(file_size, download_size) print(message) file_object.close() res.close()
-
下载的过程中,输出已下载的百分比,示例代码如下:
import time print("正在下载中...") for i in range(101): text = "\r{}%".format(i) # \r:覆盖显示 print(text, end="") time.sleep(0.2) print("\n下载完成")
-
-
-
二、目录结构
short_video
├── db 文件夹,存放数据库的数据
│ └── video.csv
│
├── files 文件夹,存放用户下载的文件
│ └── video_id-年-月-日-时-分-秒.mp4
│
├── src 包,业务代码
│ ├── service 包,三大功能
│ ├── download.py 下载新闻
│ ├── page.py 查看新闻
│ ├── search.py 搜索新闻
│ └── handler.py 逻辑处理文件
│
├── app.py 启动文件
│
└── config.py 配置文件
三、代码实现
1、config.py
"""
配置文件:保存一些全局变量(大写)
"""
import os
# 当前文件的上级目录:项目根目录
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 路径拼接:video.csv视频库文件
VIDEO_FILE_PATH = os.path.join(BASE_DIR, 'db', 'video.csv')
2、page.py
"""
分页看新闻(每页显示10条):
- 提示用户输入页码,根据页码显示指定页面的数据。
- 当用户输入的页码不存在时,默认显示第1页
"""
import config
def get_page_data(page_num, per_page_count):
"""
获取一页新闻:根据页码,获取对应页码的新闻列表(每页有10条新闻)
:param page_num: 页码
:param per_page_count: 每页数据量
:return: 数据列表
"""
# 每页的起始索引
start_index = (page_num - 1) * per_page_count
end_index = page_num * per_page_count
data_list = [] # 每页新闻的数据列表
read_row_count = 0 # 读取行数的计数:循环控制变量
with open(config.VIDEO_FILE_PATH, mode='r', encoding='utf-8') as file_object:
for line in file_object:
# line:1715046,河北大学取消考试学生紧急离校,老师:回不了家的到老师家过年,https://video...mp4
if start_index <= read_row_count < end_index:
data_list.append(line.strip()) # 保存本页数据
if read_row_count >= end_index:
break
read_row_count += 1
return data_list
def show_table(page_num, per_page_count, data_list):
"""
展示一页新闻
:param page_num:页码
:param per_page_count:每页数据量
:param data_list:一页的新闻列表
:return:None
"""
index = (page_num - 1) * per_page_count + 1 # 一页数据的起始索引
for num, line in enumerate(data_list, index):
# line:1715046,河北大学取消考试学生紧急离校,老师:回不了家的到老师家过年,https://video...mp4
row_list = line.split(',') # 英文逗号
# title = row_list[1] # 索引取值:若标题内有逗号,会显示不全
title = ",".join(row_list[1:-1]) # 列表切片:排除第一个元素和最后一个元素,剩下的列表元素都是标题,再做拼接
print(num, ")", title)
def execute():
"""
查看新闻的执行函数
"""
print('分页看新闻(每页显示10条新闻)'.center(24, '*'))
# 每页显示10条、总计999条
per_page_count, total_count = 10, 999
# 计算页码最大值(此处是100)
max_page_num, remainder = divmod(total_count, per_page_count)
if remainder:
max_page_num += 1
while True:
num = input('请输入页码[范围{}-{}](Q/q退出):'.format(1, max_page_num))
if num.upper() == 'Q':
break
if not num.isdecimal(): # 输入的页码得是十进制整数
print('页码格式错误,请重新输入!')
continue
num = int(num)
if num < 1 or num > max_page_num:
# 页码不存在,展示第1页
num = 1
page_string = '第{}页'.format(num).center(13, "*")
print(page_string)
data_list = get_page_data(num, per_page_count) # 调用获取一页新闻函数
show_table(num, per_page_count, data_list) # 调用展示一页新闻数据函数
3、search.py
"""
搜索专区:
- 用户输入关键字,根据关键字筛选出所有匹配成功的短视频资讯。
- 支持两种搜索格式:
- `id=1715025`,模糊搜索,筛选id等于1715025的新闻(video.csv的第一列)。
- `key=文本`,模糊搜索,筛选包含关键字的所有新闻(video.csv的第二列)。
"""
import re
import config
def search_by_id(value):
"""
根据ID精确搜索(一条新闻)
:param value: 新闻ID
:return: 一条新闻的数据列表
"""
datalist = [] # 一条新闻的数据列表
with open(config.VIDEO_FILE_PATH, mode='r', encoding='utf-8') as file_object:
for line in file_object:
# line:1715046,河北大学取消考试学生紧急离校,老师:回不了家的到老师家过年,https://video...mp4
line = line.strip()
if value == line.split(',')[0]: # 1715046
datalist.append(line)
return datalist
def search_by_text(value):
"""
根据文本模糊搜索(一条或多条)
:param value: 新闻的部分标题文本
:return: 一条或多条新闻的数据列表
"""
data_list = [] # 一条或多条新闻的数据列表
with open(config.VIDEO_FILE_PATH, mode='r', encoding='utf-8') as file_object:
for line in file_object:
# line:1715046,河北大学取消考试学生紧急离校,老师:回不了家的到老师家过年,https://video...mp4
line = line.strip()
# if value in line.split(',')[1]: # 索引取值
if value in ",".join(line.split(',')[1:-1]): # 列表切片
data_list.append(line)
return data_list
def show_table(data_list):
"""
展示搜索到的新闻
:param data_list: 新闻的数据列表
:return: None
"""
if not data_list:
print("未搜索到相关信息,换个关键词再试试看!")
for num, line in enumerate(data_list, 1):
# line:1715046,河北大学取消考试学生紧急离校,老师:回不了家的到老师家过年,https://video...mp4
row_list = line.split(',')
# title = row_list[1] # 索引取值
title = ",".join(row_list[1:-1]) # 列表切片
print(num, ")", title)
def execute():
"""
搜索新闻的执行函数
"""
print('搜索专区'.center(12, '*'))
while True:
text = input('请输入搜索条件,支持[id=1711349 或 key=文本](Q/q退出):')
if text.upper() == 'Q':
break
match_object = re.match("(id|key)=(\w+)", text.strip())
if not match_object:
print("输入格式错误,请重新输入!")
continue
name, value = match_object.groups()
# 函数名的字典:按键取值,先获取函数名,再加括号执行
mapping = {
"id": search_by_id,
"key": search_by_text,
}
data_list = mapping[name](value) # 调用搜索新闻函数
show_table(data_list) # 调用展示新闻函数
4、download.py
"""
下载专区:
- 用户输入视频id,根据id找到对应的mp4视频下载地址,然后下载视频到项目的files目录
- 视频的文件名为:`视频id-年-月-日-时-分-秒.mp4`
- 在下载视频的过程中,显示已下载的百分比
"""
import re
import os
import time
from datetime import datetime
import requests
import config
def get_mp4_url(video_id):
"""
根据视频ID,获取其视频链接
:param video_id: 视频ID
:return: 视频链接
"""
with open(config.VIDEO_FILE_PATH, mode='r', encoding='utf-8') as file_object:
for line in file_object:
row_list = line.strip().split(',')
vid = row_list[0] # 视频ID
url = row_list[-1] # 视频链接
if video_id == vid:
return url
def download_mp4(video_id, video_url):
"""
根据视频ID、视频链接,下载对应的视频
:param video_id: 视频ID
:param video_url: 视频链接
:return: None
"""
current_datetime = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
file_path = os.path.join(config.BASE_DIR, 'files', '{}-{}.mp4'.format(video_id, current_datetime))
res = requests.get(url=video_url)
print("正在下载中...")
# 视频总大小(字节)
file_size = int(res.headers['Content-Length'])
download_size = 0
with open(file_path, mode='wb') as file_object:
# 分块读取下载的视频文件(最多一次读128字节),并逐一写入到文件中。
for chunk in res.iter_content(128):
download_size += len(chunk)
file_object.write(chunk) # 写入内存
file_object.flush() # 刷到硬盘
# 已下载的百分比
percent = "\r{}%".format(int(round(download_size / file_size, 2) * 100)) # \r:回车
print(percent, end="") # end="":不换行
# time.sleep(0.2)
file_object.close()
print("\n下载完成")
res.close()
def execute():
"""
下载新闻的执行函数
"""
print("下载专区".center(12, '*'))
while True:
video_id = input("请输入要下载的视频ID(Q/q退出):")
if video_id.upper() == 'Q':
break
match_group = re.match("\d{7}", video_id.strip())
if not match_group:
print("ID格式错误,请重新输入!")
continue
video_url = get_mp4_url(video_id) # 调用获取新闻函数:url
if not video_url:
print('视频不存在,请重新输入!')
continue
download_mp4(video_id, video_url) # 调用下载新闻函数
5、handler.py
"""
逻辑处理文件
"""
from src.service import page, search, download # 导入功能模块
def run():
func_dict = {
'1': {'title': "分页看新闻", 'func': page.execute},
'2': {'title': "搜索专区", 'func': search.execute},
'3': {'title': "下载专区", 'func': download.execute},
}
# tips = ";".join(["{}.{}".format(k, v['title']) for k, v in func_dict.items()]).center(29, '*') # 功能菜单
tips = ["{}、{}".format(k, v['title']) for k, v in func_dict.items()]
while True:
print("******************************")
print("功能菜单:")
for tip in tips:
print(tip)
print("******************************")
choice = input("请输入序号(Q/q退出):")
if choice.upper() == 'Q':
break
data_dict = func_dict.get(choice) # {'title': "分页看新闻", 'func': page.execute}
if not data_dict:
print("序号不存在,请重新输入!")
continue
data_dict['func']() # page.execute():调用功能的执行函数
6、app.py
"""
项目启动文件
"""
from src.handler import run
if __name__ == '__main__':
run()