抖音里面点赞和收藏了很多视频,但是有害怕时间久了,作者将视频删除,因此就想把视频下载下来保存到本地或云盘
但是在抖音里手动下载,操作不是很方便,下载完需要很长时间
- 但是有些视频因为作者限制不能手动下载
并没有使用Fiddler等抓包工具原因是
- 还不是很会使用Fiddler工具
- 抖音做了一些反抓包手段
- 点赞和收藏列表不像用户主页有分享链接
思路
- 每个视频都可以复制如下的分享口令,口令中包含是个短链接
1.0 QX:/ 原来一度电可以做那么多事情%萌知计划 %抖来涨知识 @抖音青少年 %物理 @抖音小助手 https://v.douyin.com/eUfrM2s/ 复制此鏈接,打鐦Dou姻搜索,値接观kan视频!
- 截取短链接到浏览器打开,会重定向到一个如下的播放链接(还不是视频的真实链接)
https://www.iesdouyin.com/share/video/6960618997804977445/?region=CN&mid=6960619064465083144&u_code=10aca5g48&titleType=title&did=MS4wLjABAAAAfyZTr5Gy3ryqXurligO0IZJtULc2TgWWbyRBerWy0Ww&iid=MS4wLjABAAAABiuIM3QYe76mDTtLSVIEbX6oeWoXcr2e48l5skLmHo9P5KXXDUIcbnx5ogZVpQIe&with_sec_did=1&utm_source=copy_link&utm_campaign=client_share&utm_medium=android&app=aweme&scheme_type=1
其实这串链接只需要问号之前就可以了(这一点后面用到)
-
用浏览器的开发者工具定位到播放位置,可以在源码中看到视频的真实链接
- 根据这个链接下载视频
动手做
截取短链接
用正则表达式截取短链接
def get_douyin_url():
with open('./douyin_link.txt', 'r', encoding='utf-8') as f:
kouling = f.read()
pattern = re.compile(' (https://v.douyin.com/.*?/) ')
urls = re.findall(pattern, kouling)
return urls
下载网页源码
获取重定向后的页面链接,并下载页面源码
def get_douyin_page(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57"
}
resp = requests.get(url, headers=headers)
redirect_url = resp.history[0].headers['location']
page_resp = requests.get(redirect_url, headers=headers)
html = page_resp.content
with open('./douyin.html', 'w', encoding='utf-8') as f:
f.write(html)
return html
打开保存的网页源码,发现并没有视频可播放,用编辑器打开源码,以及使用浏览器的开发者工具,对比浏览器请求的源码和选然后的代码,可以发现视频原链接和视频作者等信息都是浏览器通过JS渲染生成的
分析JS代码是不可能的
使用Selenium+Webdriver打开一个浏览器抓取渲染之后的页面源码
首先想到的是无界面的PhantomJS,因其无界面,运行时占用的内存则很低
def get_douyin_page(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57"
}
resp = requests.get(url, headers=headers)
redirect_url = resp.history[0].headers['location']
driver = webdriver.PhantomJS()
driver.get(redirect_url)
sleep(10)
html = driver.page_source
with open('./douyin.html', 'w', encoding='utf-8') as f:
f.write(html)
return html
发现还是没有抓取到有渲染之后视频原链接的网页源码,不管休眠多长时间都不行
以为此路行不通,不能爬取抖音的时候,发现运行PhantomJS的提示信息
提示说Selenium对PhantomJS的支持已经被抛弃,请使用无头浏览器
原来Chrome和Firefox也有这种无界面的无头浏览器
从网上抄一个Selenium运行Chrome浏览器的方法
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
url = '...'
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver(chrome_options=chrome_options)
driver.get(url)
...
由于我从去年把主要使用的浏览器换成了微软最新的Edge浏览器,电脑里没有安装Chrome,也不太喜欢用Firefox
网上说不能用配置Chrome的方法配置Edge,给出的方法是先安装一个Python库msedge-selenium-tools
$ pip install msedge-selenium-tools
在这里的使用,创建一个无头浏览器对象
from time import sleep
from msedge.selenium_tools import EdgeOptions
from msedge.selenium_tools import Edge
def get_douyin_page(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57"
}
resp = requests.get(url, headers=headers)
redirect_url = resp.history[0].headers['location']
edge_options = EdgeOptions()
edge_options.use_chromium = True
edge_options.add_argument('headless')
driver = Edge(options=edge_options)
driver.get(redirect_url)
sleep(10)
html = driver.page_source
return driver, html
启动是并没有浏览器的窗口
这里睡眠10秒是为了把所有源码渲染完之后在获取源码,将浏览器driver
对象返回的作用后面再说
获取到页面渲染后的源码后就要从源码中提取视频原链接
解析源码 获取视频链接
使用正则表达式提取视频链接
def parse_video_url(html):
url_pattern = re.compile('')
video_url = re.findall(url_pattern, html)[0]
video_url = video_url.replace('&', '&')
print('\t', video_url)
id_pattern = re.compile('id=(.*?)&ratio')
video_id = re.findall(id_pattern, video_url)[0]
print('\t', video_id)
return video_url, video_id
下载源码的时候,Python会自动把链接中的参数连接符(&
)替换为对应的字符实体(&
),先需要还原
将链接中长度为32的参数video_id作为下载视频的文件名
下载视频
def download_video(url, id):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.57"
}
resp = requests.get(url, headers=headers)
with open(f'./douyin/{id}.mp4', 'wb') as f:
f.write(resp.content)
如果在下载网页源码时使用driver.quit()
关闭了浏览器,现在下载视频时就会提示ConnectionAbortedError: [WinError 10053] 您的主机中的软件中止了一个已建立的连接
,所以需要get_douyin_page()
函数中返回浏览器对象driver
,在调用download_video()
函数下载完视频之后再关闭浏览器
补充
有时候从抖音复制的口令里包含的链接直接就是重定向之后的播放页面链接(域名为www.iesdouyin.com
),就需要修改截取短链接的正则表达式匹配模式(https://www.iesdouyin.com/share/video/.*?/)?
,在下载网页源码的函数中就不再需要前面寻找重定向链接的部分
这样就与前面使用域名为'v.douyin.com'的短链接的代码不同,难道要使用两套代码吗?
试一下,如果不先寻找重定向链接,而是直接给无头浏览器传原来这种短链接能不能获取有视频原链接的网页源码
试了之后,可行.那么改为两种链接都可用的代码
def get_douyin_url():
with open('./douyin_link.txt', 'r', encoding='utf-8') as f:
kouling = f.read()
pattern = re.compile('https://v.douyin.com/.*?/|https://www.iesdouyin.com/share/video/.*?/')
urls = re.findall(pattern, kouling)
return urls
def get_douyin_page(url):
edge_options = EdgeOptions()
edge_options.use_chromium = True
edge_options.add_argument('headless')
driver = Edge(options=edge_options)
driver.get(url)
sleep(10)
html = driver.page_source
return driver, html
获取短链接函数返回的是一个链接列表,可使用循环多次下载视频
再补充
熟悉多线程和异步的朋友可以试一下,在评论区补充