前言
搞社会实践(da gong)的地方是做网站的,公司把视频放到了*拍短视频的服务器上,通过在自有的页面中引用链接来给别人看。然而,在前几天,*拍把公司的视频引用链接给ban了,客户无法在浏览器上看网页中的视频,但是单独通过视频链接还是可以看的。为了客户能正常观看视频,公司决定将视频手动下载,转移到自有的服务器上。
我说停停,能不能给我二十几首歌的时间,我来整一个自动下载。
技术路线:python3.8; requests, re.
import requests
import re
效果图:
实现方案
大概要做这么一些事:
获取文章页面,分析文章页面的源代码,从源代码中找出视频链接,通过视频链接下载视频,循环以上过程。
1、获取文章源代码
通过观察文章列表,发现每个文章有个唯一的id,那就是说,有可能可以通过id来获取文章链接,从而下载文章源代码。
公司在公网上的url:
https://域名/yushi/manao/10000.html
https://域名/shuijing/caomeijing/10086.html
文章链接中确实有文章的id。然而,文章是分类生成的,被系统放到了不同的目录中,这给单独使用id获取页面源代码设置了障碍。
一上来找不到链接,卡在了第一步属实难受,二十几首歌的时间有限,我慌张地后台胡乱翻找翻找,直到发现了这个:
内部预览链接
https://域名/管理系统/plus/view.php?aid=10000
https://域名/管理系统/plus/view.php?aid=10086
这个链接就很整齐美观,非常适合爬虫的胃口。这样可以确定文章链接了,获取文章页面及其源代码了:
url = f'https://域名/管理系统/plus/view.php?aid={id}'
2、获取文章页面
人在江湖飘,不得不给自己套上一层伪装,网上冲浪也是。让机器人去做事总有点不放心,需要让它装成一个用浏览器的人。
设置全局User Agent :
userAgent = {
'User-Agent':
'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:91.0) \
Gecko/20100101 Firefox/91.0'
}
创建一个获取页面文本内容的函数,获取并返回url链接的网页文本的内容,并输出正在获取的url对应的文章id,如遇错误,返回空字符串,输出错误信息。
def getPage(url , id):
print(f"Geting '{id}'.")
try:
response = requests.get(url, headers= userAgent)
response.encoding = 'utf-8'
response = response.text
return response
except:
print(f"'{id}' get error!")
return ''
@url: 字符串,待爬取的网页链接
@id: 字符串,所爬取的文章id
3、分析源码获取视频链接
我html不怎么会,标签辣么多我也没时间看。既然是找视频那就直接冲mp4吧,一搜索还真有两个,比对了一下两个是一样的。
正则表达式直接匹配不用分析了
r'http.+?mp4'
http开头,mp4结尾,中间要有东西,如果匹配到了多个,取最短的。
创建一个获取视频链接的函数,从text文本内容中获取并返回视频链接,如遇错误,返回空字符串,输出错误信息。
def getUrl(id, text):
try:
pat = re.compile(r'http.+?mp4')
url = re.findall(pat, text)
url = url[0]
return url
except:
print(f"'{id}' re match error!")
return ''
@id: 字符串,所爬取的文章id
@text: 字符串,用于查找视频链接的网页源码
4、下载和保存视频
视频和一般网页内容不一样,是个二进制文件。区别于页面源代码的text属性,在从服务器响应中获取时,requests库中,需要用到Response类的content属性。在保存文件时,也不能单独使用‘w'写入模式,需要使用’wb'这个二进制写入模式。
创建一个下载和保存视频的函数,将在url链接的视频以'name.mp4'的名字保存在videos文件夹,如遇错误,记录错误后输出错误信息。
def saveVideo(url, name):
try:
response = requests.get(url, stream= 1, headers= userAgent)
response = response.content # 二进制访问视频
file = open(f'./videos/{name}.mp4', 'wb') # 二进制文件模式
file.write(response)
file.close()
except:
errorIDs.append(name)
print(f"{name} save video error!")
@url: 字符串,视频链接
@name:字符串,视频的名称
5、完善逻辑
剩下完善整个程序的逻辑,把函数放在一起就好了。
file = open('ids.txt', 'r', encoding= 'utf-8')
ids = file.readlines()
file.close()
将要下载的文章id放在一个文件里,通过文件的方式输入,更稳健。遍历所有文章id,逐个下载。
for id in ids:
id = id.strip()
url = f'https://域名/管理系统/plus/view.php?aid={id}'
webPage = getPage(url , id)
videoUrl = getUrl(id, webPage)
saveVideo(videoUrl, id)
print(f'{id} completed!')
print("Finish!")
展示运行的结果,并保持结果持续显示:
if len(errorIDs) != 0:
print("error IDs:")
for id in errorIDs:
print(id, end=' ')
print('\n')
else :
print('No error.')
Ispause = 1
while 1 :
Ispause = input()
if Ispause == 'exit' :
break
完整代码,3kB不到,自动爬~
import requests
import re
userAgent = {
'User-Agent':
'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
}
errorIDs = []
def getPage(url , id):
'''
获取并返回url链接的网页文本的内容,
并输出正在获取的url对应的文章id,
如遇错误,返回空字符串,输出错误信息。
@url: 字符串,待爬取的网页链接
@id: 字符串,所爬取的文章id
'''
print(f"Geting '{id}'.")
try:
response = requests.get(url, headers= userAgent)
response.encoding = 'utf-8'
response = response.text
return response
except:
print(f"'{id}' get error!")
return ''
def getUrl(id, text):
'''
从text文本内容中获取并返回视频链接,
如遇错误,返回空字符串,输出错误信息。
@id: 字符串,所爬取的文章id
@text: 字符串,匹配视频链接的网页内容
'''
try:
pat = re.compile(r'http.+?mp4')
url = re.findall(pat, text)
url = url[0]
return url
except:
print(f"'{id}' re match error!")
return ''
def saveVideo(url, name):
'''
将在url的视频以'name.mp4'的名字保存在videos文件夹,
如遇错误,记录错误后输出错误信息。
@url: 字符串,视频链接
@name:字符串,视频的名称
'''
try:
response = requests.get(url, stream= 1, headers= userAgent)
response = response.content
file = open(f'./videos/{name}.mp4', 'wb')
file.write(response)
file.close()
except:
errorIDs.append(name)
print(f"{name} save video error!")
if __name__=='__main__':
# 读取需要获取视频的文章id
file = open('ids.txt', 'r', encoding= 'utf-8')
ids = file.readlines()
file.close()
# 获取视频
for id in ids:
id = id.strip()
url = f'https://域名/管理系统/plus/view.php?aid={id}'
webPage = getPage(url , id)
videoUrl = getUrl(id, webPage)
saveVideo(videoUrl, id)
print(f'{id} completed!')
print("Finish!")
# 展示运行结果
if len(errorIDs) != 0:
print("error IDs:")
for id in errorIDs:
print(id, end=' ')
print('\n')
else :
print('No error.')
# 保持运行结果持续显示
Ispause = 0
while 1 :
Ispause = input()
if Ispause == 'exit' :
break
反思
真正运用到工作中的时候,此程序并不高效。对于没有参与编写的人员,这个小程序不太友好,使用需要建文件夹、创建文件手动输入id,过于麻烦。以至于公司把我作为一个下载器,我一个人处理下载,其他人负责转移,虽提高了一定效率,但仍不是最优解。
需要改进的地方:
1、不能多个下载任务并行处理;
2、不能灵活调整下载位置;
3、没有友好易上手的用户图形界面;
4、不能自动获取文章id;
5、下载失败不会自动重试;
6、没有进度条。
可以尝试一下:
1、不使用正则表达式,使用bs库解析html;
2、使用成熟的scrapy等爬虫框架再开发。