已离职,无任何利益相关,请放心食用 公司具体网址均已作脱敏处理,防止恶意爬虫攻击 (我都被自己感动了55555) ?
前言
搞社会实践(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等爬虫框架再开发。
|