Add tiktok live record

This commit is contained in:
ihmily 2023-08-03 09:08:37 +08:00
parent 4bb0e6a776
commit 0acb7a5562
4 changed files with 260 additions and 214 deletions

View File

@ -1,5 +1,5 @@
# DouyinLiveRecorder
一个简易的可循环值守的抖音直播录制软件,支持同时录制多个直播间
一个可循环值守的抖音|国际版抖音Tiktok直播录制软件,支持同时录制多个直播间以及自定义配置
 
@ -26,29 +26,53 @@
- 在config文件夹内的配置文件中对录制进行配置以及添加录制直播间地址
- 录制需要使用到PC端直播间页面的Cookie请先在config.ini配置文件中添加后再进行录制
- 注意事项① 录制使用到了ffmpeg如果没有则无法进行录制
- 注意事项② 录制时不能使用vpn代理会被抖音禁止访问
- 抖音app端直播间分享地址和网页端长地址都能正常进行录制尽量用长链接避免因短链接转换失效导致不能正常录制
- 注意事项② 录制Tiktok时需要使用vpn代理请先在配置文件中设置开启代理并添加proxy_addr
- 注意事项③ 如果电脑开启了全局或者规则代理可不用添加proxy_addr参数值但仍需在config.ini配置文件中设置开启代理
- 抖音app端直播间分享地址和网页端长地址都能正常进行录制尽量用长链接避免因短链接转换失效导致不能正常录制Tiktok直播间地址目前只支持一种
 
抖音直播间链接示例:
```
https://live.douyin.com/745964462470
https://live.douyin.com/326500301367
https://v.douyin.com/iQFeBnt/
```
 
Tiktok直播间链接示例
```
https://www.tiktok.com/@pearlgaga88/live
https://www.tiktok.com/@bougiebulliesandbirds/live
```
 
测试APIhttps://hmily.vip/api/dy/live/?url=
参数url抖音直播间地址
请求示例https://hmily.vip/api/dy/live/?url=https://live.douyin.com/573716250978
https://hmily.vip/api/dy/live/?url=https://live.douyin.com/573716250978
```
(只支持抖音Tiktok的没写)
 
## 提交日志
 
- 20230803
- 通宵更新 一直熬夜一直爽
- 新增了国际版抖音Tiktok的直播录制去除冗余 简化了部分代码
- 20230724
- 新增了一个通过抖音直播间地址获取直播视频流链接的API接口使用php写的 上传即可用
 
## 有人感兴趣的话,后续我会在这里不断更新其他直播平台的录制 欢迎给个star
## 有问题可以提issue 或 参与pr,后续我会在这里不断更新其他直播平台的录制 欢迎给个star
####

View File

@ -1,11 +1,12 @@
[1]
直播地址 =
直播保存路径(不填则默认) =
视频保存格式ts|mkv|flv|mp4|ts音频|mkv音频 = MP4
原画|超清|高清|标清 = 原画
是否使用代理ip是/否) =
代理地址 =
同一时间访问网络的线程数 = 3
循环时间(秒) = 60
排队读取网址时间(秒) = 0
直播保存路径 =
视频保存格式ts|mkv|flv|mp4|ts音频|mkv音频 = mp4
原画|超清|高清|标清 = 原画
是否显示直播地址 =
是否显示循环秒数 =
ts格式分段录制是否开启 =
@ -15,7 +16,6 @@ ts录制完成后自动增加生成m4a格式 = 否
追加格式后删除原文件 =
生成时间文件 =
是否显示浏览器 =
短链接自动转换为长连接 =
仅用浏览器录制 =
cookies = ttwid=1^%^7CIkooT8SJQrpeYtHlSALuhz9BdcHpaaf9tHQRKHuDaYE^%^7C1687785070^%^7C6690250483b63b6482128174d0f93bd879614d76f1b6e03ca52e032cf7fbaafd; passport_csrf_token=52bece134ac246c81163cc93b72f86a6; passport_csrf_token_default=52bece134ac246c81163cc93b72f86a6; d_ticket=2b9e3eb3626216c0122f0d980f867deb7b414; odin_tt=36dcfa4bd95b1edf0e6445dc9584a7371b4b90a2a8c390292b9be44af8161e06d10e34e6e575bf43c45c0a5871229533; passport_assist_user=CjxqZLHwcDmhxMjoh8u0-Jz4Zmhcq9VzKQqpAjK3P9Ve87i4njjbsai_u6NAI6MebG4KH4QXtbfNRbRFWRgaSAo8oBvHC_eXhqSGSo1uWPz4KBGu9uo-UdwM-5ynbDuDQRybofj0pQlPexcYmVoryiYqpC7yMdKO4rSVclgNEJbxtA0Yia_WVCIBA78Lg84^%^3D; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; passport_auth_status=a74f300f376940d65914eb148d55ca96^%^2C9ca487aea255972120d502f736c5dd7b; passport_auth_status_ss=a74f300f376940d65914eb148d55ca96^%^2C9ca487aea255972120d502f736c5dd7b; sso_auth_status=52ecac30d95890cc7896c880366aa21a; sso_auth_status_ss=52ecac30d95890cc7896c880366aa21a; sso_uid_tt=a71f659bc416b890eb13929ee82a4340; sso_uid_tt_ss=a71f659bc416b890eb13929ee82a4340; toutiao_sso_user=da6c1f563492114cf1ad2dac7617e3f4; toutiao_sso_user_ss=da6c1f563492114cf1ad2dac7617e3f4; sid_ucp_sso_v1=1.0.0-KDNhOTk2NzYzZjkzMGZkZDdlNDU4ODE3YjczNGE3MGExYTBhOGQ0OGIKHQjRpfzk8AEQn6LmpAYY7zEgDDCRxffLBTgCQPEHGgJsZiIgZGE2YzFmNTYzNDkyMTE0Y2YxYWQyZGFjNzYxN2UzZjQ; ssid_ucp_sso_v1=1.0.0-KDNhOTk2NzYzZjkzMGZkZDdlNDU4ODE3YjczNGE3MGExYTBhOGQ0OGIKHQjRpfzk8AEQn6LmpAYY7zEgDDCRxffLBTgCQPEHGgJsZiIgZGE2YzFmNTYzNDkyMTE0Y2YxYWQyZGFjNzYxN2UzZjQ; sid_guard=219f9f2d8838724307589f32debd7d62^%^7C1687785774^%^7C5183988^%^7CFri^%^2C+25-Aug-2023+13^%^3A22^%^3A42+GMT; uid_tt=9f33ebaf781ad75909d6d4edb0b46b86; uid_tt_ss=9f33ebaf781ad75909d6d4edb0b46b86; sid_tt=219f9f2d8838724307589f32debd7d62; sessionid=219f9f2d8838724307589f32debd7d62; sessionid_ss=219f9f2d8838724307589f32debd7d62; sid_ucp_v1=1.0.0-KGZkMzE1MGRhMTZiOTI4M2ZkOTkzMjIwNjQ5NmJhMjhjMzlhZmFmMGYKGQjRpfzk8AEQrqLmpAYY7zEgDDgCQPEHSAQaAmxmIiAyMTlmOWYyZDg4Mzg3MjQzMDc1ODlmMzJkZWJkN2Q2Mg; ssid_ucp_v1=1.0.0-KGZkMzE1MGRhMTZiOTI4M2ZkOTkzMjIwNjQ5NmJhMjhjMzlhZmFmMGYKGQjRpfzk8AEQrqLmpAYY7zEgDDgCQPEHSAQaAmxmIiAyMTlmOWYyZDg4Mzg3MjQzMDc1ODlmMzJkZWJkN2Q2Mg; LOGIN_STATUS=1; store-region=cn-fj; store-region-src=uid; __security_server_data_status=1; __live_version__=^%^221.1.1.1250^%^22; live_can_add_dy_2_desktop=^%^220^%^22; xgplayer_user_id=528819598596; publish_badge_show_info=^%^220^%^2C0^%^2C0^%^2C1689195658062^%^22; msToken=OqErXe8WFLs1OjcJCNX2pNWTvnMSJIKjUXQRa74H4uQo8Ea5hpI8L3zlpK2eq5GUyrHcnTbQ-Kw4UgEntfb9dXu4Ijfowp_LSMxN6AISaoDf-OZCFv2IFS1P4UY2DHYG; ttcid=b8ea42abb1954fd2a913086620d2447f25; download_guide=^%^223^%^2F20230713^%^2F0^%^22; home_can_add_dy_2_desktop=^%^220^%^22; strategyABtestKey=^%^221689299939.836^%^22; FOLLOW_LIVE_POINT_INFO=^%^22MS4wLjABAAAAf6aekfyBsc4u8jMkeYbgnkFa0ksIWKWpGOywuyHXyo4^%^2F1689350400000^%^2F0^%^2F1689299940112^%^2F0^%^22; VIDEO_FILTER_MEMO_SELECT=^%^7B^%^22expireTime^%^22^%^3A1689904741009^%^2C^%^22type^%^22^%^3Anull^%^7D; device_web_cpu_core=8; device_web_memory_size=-1; webcast_local_quality=origin; csrf_session_id=ce1e9bc7ba69feec32889636027ce79a; passport_fe_beating_status=false; webcast_leading_last_show_time=1689300023141; webcast_leading_total_show_times=1; tt_scid=PgQO8z6aK2zeofuxFGselkNp2kSbPFCuPmOMwU9ih.SXbBp8iA7l2EiqrurdBuMWf204; msToken=T5-gdGi61ytJtas2VgUQAogURSJT2pQDWT5E_TPGT9paO9OtCuHOGAl72YAxHhrkKJdNhx6O1JgGn0uzdkfHw_a_MWJS-OJlgroduiLAcJzzSCmib6GfWqIgWT8m2qA3; __ac_nonce=064b0be450072f85a1215; __ac_signature=_02B4Z6wo00f01eL3lWgAAIDA.rQr1mcyUx3ixpHAABwPwM4wuxfDlht-h5js2proCQ84O246NPRhh7I6Pv0tJwHeCCotGZpibWazzH51O7kDcl18lUJy6o8VYLGIG..jFlP-zEpspGjM6AZO34; __ac_referer=https://live.douyin.com/
cookies(不可为空) = ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986

368
main.py
View File

@ -2,9 +2,11 @@
"""
Author: Hmily
Github:https://github.com/ihmily
Github: https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2023-08-03 08:06:43
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Record douyin and titok live stream.
"""
import random
@ -22,7 +24,7 @@ from spider import *
from web_rid import *
# 版本号
version = 20230714.19
version = 202300803.08
# --------------------------log日志-------------------------------------
# 创建一个logger
@ -54,6 +56,7 @@ firstRunOtherLine = True
live_list = []
not_record_list = []
start5 = datetime.datetime.now()
global_proxy = False
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36'
}
@ -62,7 +65,7 @@ url_config_file = './config/URL_config.ini'
backup_dir = './backup_config'
encoding = 'utf-8-sig'
rstr = r"[\/\\\:\*\?\"\<\>\|&u]"
ffmpeg_path = "ffmpeg" # ffmpeg文件路径
# --------------------------用到的函数-------------------------------------
@ -120,8 +123,7 @@ def display_info():
else:
start5 = datetime.datetime.now()
except Exception as e:
print("错误信息644:" + str(e) + "\r\n读取的地址为: " + str(live_link) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
# print(live_link+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败')
print("错误信息644:" + str(e) + "\r\n发生错误的行数: "+str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
@ -235,7 +237,7 @@ def change_max_connect():
print("同一时间访问网络的线程数动态改为", max_request)
def get_stream_url2(json_data):
def get_douyin_stream_url(json_data):
# TODO: 获取直播源地址
data = [] # 定义一个返回数据列表
@ -255,6 +257,8 @@ def get_stream_url2(json_data):
flv_url_list = stream_url['flv_pull_url']
# m3u8视频流链接
m3u8_url_list = stream_url['hls_pull_url_map']
# origin蓝光1080P、720超清hd、720高清sd、540标清ld
if video_quality == "原画" or video_quality == "蓝光":
m3u8_url = m3u8_url_list["FULL_HD1"]
flv_url = flv_url_list["FULL_HD1"]
@ -265,18 +269,55 @@ def get_stream_url2(json_data):
m3u8_url = m3u8_url_list["SD1"]
flv_url = flv_url_list["SD1"]
elif video_quality == "标清":
m3u8_url = m3u8_url_list["SD1"]
flv_url = flv_url_list["SD1"]
m3u8_url = m3u8_url_list["SD2"]
flv_url = flv_url_list["SD2"]
data = [anchor_name, status, m3u8_url, flv_url]
return data
def get_tiktok_stream_url(json_data):
# TODO: 获取Tiktok直播源地址
data = [] # 定义一个返回数据列表
LiveRoom = json_data['LiveRoom']['liveRoomUserInfo']
anchor_name = LiveRoom['user']['nickname']
data.append(anchor_name)
# 获取直播间状态
status = LiveRoom['user']["status"] # 直播状态2是正在直播.4是未开播
if status == 4:
data = [anchor_name, status, '', '']
else:
# 画质从高到低origin>uhd>sd>sd>ld
# {origin:'原画质或蓝光',uhd:'1080P或720P',sd:'540P或480P',ld:'360P标清'}
# 上面画质对应只是一般情况,具体情况有可能不一样 可以看对应画质的sdk_params参数里面有如1080P等参数
stream_data = LiveRoom['liveRoom']['streamData']['pull_data']['stream_data']
stream_data = json.loads(stream_data)['data']
if video_quality == "原画" or video_quality == "蓝光":
m3u8_url = stream_data["origin"]['main']['hls']
flv_url = stream_data["origin"]['main']['flv']
elif video_quality == "超清":
m3u8_url = stream_data["uhd"]['main']['hls']
flv_url = stream_data["uhd"]['main']['flv']
elif video_quality == "高清":
m3u8_url = stream_data["sd"]['main']['hls']
flv_url = stream_data["sd"]['main']['flv']
elif video_quality == "标清":
m3u8_url = stream_data["ld"]['main']['hls']
flv_url = stream_data["ld"]['main']['flv']
# 注意这里要将链接改为http协议否则无法使用ffmpeg录制原因是代理大都是http
m3u8_url = re.sub("https", "http", m3u8_url)
flv_url = re.sub("https", "http", flv_url)
data = [anchor_name, status, m3u8_url, flv_url]
return data
def start_record(line, count_variable=-1):
global warning_count
global video_save_path
global live_list
global not_record_list
global need_proxy_url_list
while True:
try:
record_finished = False
@ -287,7 +328,7 @@ def start_record(line, count_variable=-1):
url_tuple = line
record_url = url_tuple[0]
anchor_name = url_tuple[1]
print("运行新线程,传入地址 " + url_tuple[0] + " 序号" + str(count_variable))
print("\r运行新线程,传入地址 " + record_url)
while True:
try:
@ -296,8 +337,8 @@ def start_record(line, count_variable=-1):
# 判断如果是浏览器长链接
with semaphore:
# 使用semaphore来控制同时访问资源的线程数量
json_data = get_json_data(record_url,cookies_set) # 注意这里需要配置文件中的cookie
port_info = get_stream_url2(json_data)
json_data = get_douyin_stream_data(record_url,cookies_set) # 注意这里需要配置文件中的cookie
port_info = get_douyin_stream_url(json_data)
elif record_url.find("https://v.douyin.com/") > -1:
# 判断如果是app分享链接
is_long_url = True
@ -308,13 +349,19 @@ def start_record(line, count_variable=-1):
new_record_url = "https://live.douyin.com/" + str(web_rid)
not_record_list.append(new_record_url)
with semaphore:
json_data = get_json_data(new_record_url)
port_info = get_stream_url2(json_data)
json_data = get_douyin_stream_data(new_record_url, cookies_set)
port_info = get_douyin_stream_url(json_data)
elif record_url.find("https://www.tiktok.com/") > -1:
with semaphore:
if use_proxy or (global_proxy and proxy_addr !=''):
json_data = get_tiktok_stream_data(record_url,proxy_addr)
port_info = get_tiktok_stream_url(json_data)
# print("端口信息:" + str(port_info))
# port_info=['主播名','状态码','m3u8地址','flv地址']
if len(port_info) != 4:
print(f'序号{count_variable} 网址内容获取失败,进行重试中...获取失败的地址是:{line} 主播为:{anchor_name}')
print(f'序号{count_variable} 网址内容获取失败,进行重试中...获取失败的地址是:{line}')
warning_count += 1
else:
anchor_name = port_info[0]
@ -334,10 +381,10 @@ def start_record(line, count_variable=-1):
# 判断状态码 如果是2则正在直播如果是4则未在直播
if port_info[1] != 2:
print(f"序号{count_variable} {port_info[0]} 等待直播.. ")
print(f"序号{count_variable} {port_info[0]} 等待直播... ")
anchor_name = port_info[0]
else:
print(f"序号{count_variable} {port_info[0]} 正在直播中")
print(f"序号{count_variable} {port_info[0]} 正在直播中...")
# 是否显示直播地址
if video_m3u8:
@ -347,6 +394,7 @@ def start_record(line, count_variable=-1):
print(f"{port_info[0]} 直播地址为:{port_info[2]}")
real_url = port_info[2] # 默认使用m3u8地址进行下载
if real_url == "":
print('解析错误,直播间视频流未找到...')
pass
@ -370,7 +418,48 @@ def start_record(line, count_variable=-1):
if not os.path.exists(video_save_path + anchor_name):
print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置")
video_save_path = ""
print("因为配置文件的路径错误,本次录制在程序目录")
default_path = os.getcwd()
print(f"因为配置文件的路径错误,本次录制在程序目录 {default_path}")
ffmpeg_command = [
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
# "-map", "0", # 不同点
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy", # 直接用copy的话体积特别大.
# "-c:v","libx264", # 后期可以用crf来控制大小
"-reconnect_delay_max", "30",
"-reconnect_streamed", "-reconnect_at_eof",
# "-c:v", "copy", # 不同点
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
# "-f", "matroska", # 不同点
# "{path}".format(path=file), # 不同点
]
# 添加代理参数
if 'douyin' not in real_url:
if use_proxy and proxy_addr != '':
# os.environ["http_proxy"] = proxy_addr
ffmpeg_command.insert(1, "-http_proxy")
ffmpeg_command.insert(2, proxy_addr)
recording.add(f'序号{count_variable} ' + anchor_name)
if video_save_type == "FLV":
filename = anchor_name + '_' + now + '.flv'
@ -395,8 +484,6 @@ def start_record(line, count_variable=-1):
try:
# “port_info[3]”对应的是flv地址使用老方法下载直接请求下载flv只能是下载flv流的。
real_url = port_info[3]
recording.add(f'序号{count_variable} ' + anchor_name)
_filepath, _ = urllib.request.urlretrieve(real_url,video_save_path + anchor_name + '/' + filename)
record_finished = True
record_finished_2 = True
@ -418,9 +505,7 @@ def start_record(line, count_variable=-1):
else:
print("\r" + anchor_name + " 录制视频中: " + video_save_path + anchor_name + '/' + filename)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
filename_short = video_save_path + anchor_name + '/' + anchor_name + '_' + now
if create_time_file:
@ -431,35 +516,15 @@ def start_record(line, count_variable=-1):
create_var[str(filename_short)].start()
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
command=[
"-map", "0",
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy",
# "-c:v","libx264", #后期可以用crf来控制大小
"-reconnect_delay_max", "30", "-reconnect_streamed", "-reconnect_at_eof",
"-c:v", "copy", # 直接用copy的话体积特别大.
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
"-f", "matroska",
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
record_finished = True
record_finished_2 = True
@ -483,7 +548,6 @@ def start_record(line, count_variable=-1):
print(
"\r" + anchor_name + " 录制视频中: " + video_save_path + anchor_name + '/' + filename)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
filename_short = video_save_path + anchor_name + '/' + anchor_name + '_' + now
@ -496,35 +560,18 @@ def start_record(line, count_variable=-1):
create_var[str(filename_short)].start()
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
command = [
"-map", "0",
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy",
# "-c:v","libx264", #后期可以用crf来控制大小
"-reconnect_delay_max", "30", "-reconnect_streamed", "-reconnect_at_eof",
"-c:v", "copy", # 直接用copy的话体积特别大.
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
"-f", "mp4",
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
# 取消http_proxy环境变量设置
# if proxy_addr:
# del os.environ["http_proxy"]
record_finished = True
record_finished_2 = True
@ -549,34 +596,15 @@ def start_record(line, count_variable=-1):
print(
"\r" + anchor_name + " 录制MKV音频中: " + video_save_path + anchor_name + '/' + filename)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
"-map", "0:a",
"-sn", "-dn",
"-reconnect_delay_max", "30", "-reconnect_streamed", "-reconnect_at_eof",
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
"-f", "matroska",
command = [
"-map", "0:a", # 不同点
"-f", "matroska", # 不同点
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
record_finished = True
record_finished_2 = True
@ -603,34 +631,15 @@ def start_record(line, count_variable=-1):
print(
"\r" + anchor_name + " 录制TS音频中: " + video_save_path + anchor_name + '/' + filename)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
"-map", "0:a",
"-sn", "-dn",
"-reconnect_delay_max", "30", "-reconnect_streamed", "-reconnect_at_eof",
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
command = [
"-map", "0:a", # 不同点
"-f", "mpegts",
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command, stderr=subprocess.STDOUT)
record_finished = True
record_finished_2 = True
@ -661,7 +670,6 @@ def start_record(line, count_variable=-1):
print(
"\r" + anchor_name + " 分段录制视频中: " + video_save_path + anchor_name + '/' + filename + " 每录满: %d M 存一个视频" % Splitsize)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
filename_short = video_save_path + anchor_name + '/' + anchor_name + '_' + now
@ -674,37 +682,17 @@ def start_record(line, count_variable=-1):
create_var[str(filename_short)].start()
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist",
"rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
"-map", "0",
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy",
"-reconnect_delay_max", "30", "-reconnect_streamed",
"-reconnect_at_eof",
command = [
"-c:v", "copy",
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
"-f", "mpegts",
"-fs", str(Splitsizes),
"-map", "0", # 不同点
"-f", "mpegts",
"-fs", str(Splitsizes), # 不同点
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command,
stderr=subprocess.STDOUT)
record_finished = True # 这里表示正常录制成功一次
record_finished_2 = True
@ -719,9 +707,7 @@ def start_record(line, count_variable=-1):
if anchor_name in unrecording:
unrecording.add(anchor_name)
except subprocess.CalledProcessError as e:
# 这是里分段 如果录制错误会跳转到这里来
# logging.warning(str(e.output))
# print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) )
# logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno))
if anchor_name in recording:
recording.remove(anchor_name)
@ -739,7 +725,6 @@ def start_record(line, count_variable=-1):
print(
"\r" + anchor_name + " 录制视频中: " + video_save_path + anchor_name + '/' + filename)
ffmpeg_path = "ffmpeg"
file = video_save_path + anchor_name + '/' + filename
filename_short = video_save_path + anchor_name + '/' + anchor_name + '_' + now
@ -751,35 +736,16 @@ def start_record(line, count_variable=-1):
create_var[str(filename_short)].start()
try:
recording.add(f'序号{count_variable} ' + anchor_name)
_output = subprocess.check_output([
ffmpeg_path, "-y",
"-v", "verbose",
"-rw_timeout", "10000000", # 10s
"-loglevel", "error",
"-hide_banner",
"-user_agent", headers["User-Agent"],
"-protocol_whitelist", "rtmp,crypto,file,http,https,tcp,tls,udp,rtp",
"-thread_queue_size", "1024",
"-analyzeduration", "2147483647",
"-probesize", "2147483647",
"-fflags", "+discardcorrupt",
"-i", real_url,
"-bufsize", "5000k",
"-map", "0",
"-sn", "-dn",
# "-bsf:v","h264_mp4toannexb",
# "-c","copy",
"-reconnect_delay_max", "30", "-reconnect_streamed",
"-reconnect_at_eof",
command = [
"-c:v", "copy",
"-c:a", "copy",
"-max_muxing_queue_size", "64",
"-correct_ts_overflow", "1",
"-map", "0", # 不同点
"-f", "mpegts",
"{path}".format(path=file),
], stderr=subprocess.STDOUT)
]
ffmpeg_command.extend(command)
_output = subprocess.check_output(ffmpeg_command,
stderr=subprocess.STDOUT)
record_finished = True
record_finished_2 = True
count_time = time.time()
@ -810,8 +776,7 @@ def start_record(line, count_variable=-1):
except Exception as e:
print(
"错误信息644:" + str(e) + "\r\n读取的地址为: " + str(live_link) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
# print(live_link+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败')
"错误信息644:" + str(e) + "\r\n读取的地址为: " + str(record_url) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
warning_count += 1
@ -846,8 +811,7 @@ def start_record(line, count_variable=-1):
if loop_time:
print('\r检测直播间中...', end="")
except Exception as e:
print("错误信息644:" + str(e) + "\r\n读取的地址为: " + str(live_link) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
# print(live_link+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败')
print("错误信息644:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("线程崩溃2秒后重试.错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
warning_count += 1
@ -876,9 +840,9 @@ def backup_file(file_path, backup_dir):
# 拷贝文件到备份目录
backup_file_path = os.path.join(backup_dir, backup_file_name)
shutil.copy2(file_path, backup_file_path)
print(f'已备份配置文件 {file_path}{backup_file_path}')
print(f'\r已备份配置文件 {file_path}{backup_file_path}')
except Exception as e:
print(f'备份配置文件 {file_path} 失败:{str(e)}')
print(f'\r备份配置文件 {file_path} 失败:{str(e)}')
def backup_file_start():
@ -927,6 +891,17 @@ t3 = threading.Thread(target=backup_file_start, args=(), daemon=True)
t3.start()
Monitoring = 0
# 如果开启了电脑全局/规则代理,可以不用再在配置文件中填写代理地址
try:
# 检测电脑是否开启了全局/规则代理
print('系统代理检测中...')
response_g = urllib.request.urlopen("https://www.tiktok.com/", timeout=5)
global_proxy = True
print('系统代理已开启√ 注意:配置文件中的代理设置也要开启才会生效哦!')
except Exception as e:
print('INFO未检测到网络代理请检查代理是否生效若无需录制Tiktok直播请忽略此条提示')
def read_config_value(config, section, option, default_value):
try:
@ -963,14 +938,15 @@ while True:
with open(url_config_file, 'a+', encoding=encoding) as f:
f.write(inurl)
live_link = read_config_value(config, '1', '直播地址', "") # 暂时没有用到
video_save_path = read_config_value(config, '1', '直播保存路径(不填则默认)', "")
video_save_type = read_config_value(config, '1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频', "MP4")
video_quality = read_config_value(config, '1', '原画|超清|高清|标清', "原画")
use_proxy = read_config_value(config, '1', '是否使用代理ip是/否)', "")
proxy_addr = read_config_value(config, '1', '代理地址', "") # 暂时没有用到
max_request = int(read_config_value(config, '1', '同一时间访问网络的线程数', 3))
semaphore = threading.Semaphore(max_request)
delay_default = int(read_config_value(config, '1', '循环时间(秒)', 60))
local_delay_default = int(read_config_value(config, '1', '排队读取网址时间(秒)', 0))
video_save_path = read_config_value(config, '1', '直播保存路径', "")
video_save_type = read_config_value(config, '1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频', "TS")
video_quality = read_config_value(config, '1', '原画|超清|高清|标清', "原画")
video_m3u8 = read_config_value(config, '1', '是否显示直播地址', "")
loop_time = read_config_value(config, '1', '是否显示循环秒数', "")
Splitvideobysize = read_config_value(config, '1', 'TS格式分段录制是否开启', "")
@ -980,9 +956,8 @@ while True:
delFilebeforeconversion = read_config_value(config, '1', '追加格式后删除原文件', "")
create_time_file = read_config_value(config, '1', '生成时间文件', "")
display_chrome = read_config_value(config, '1', '是否显示浏览器', "")
cover_long_url = read_config_value(config, '1', '短链接自动转换为长连接', "")
only_browser = read_config_value(config, '1', '仅用浏览器录制', "")
cookies_set = read_config_value(config, '1', 'cookies', '')
cookies_set = read_config_value(config, '1', 'cookies(不可为空)', '')
if len(video_save_type) > 0:
if video_save_type.upper().lower() == "FLV".lower():
@ -1025,18 +1000,19 @@ while True:
"": True,
"": False
}
use_proxy = options.get(use_proxy, False) # 是否使用代理ip
video_m3u8 = options.get(video_m3u8, False) # 是否显示直播地址
loop_time = options.get(loop_time, False) # 是否显示循环秒数
Splitvideobysize = options.get(Splitvideobysize, False) # 这里是控制TS是否分段
create_time_file = options.get(create_time_file, False) # 这里控制是否生成时间文件
display_chrome = options.get(display_chrome, False) # 这里控制是否生显示浏览器
cover_long_url = options.get(cover_long_url, False) # 这里是控制是否转换短链接
only_browser = options.get(only_browser, False) # 这里是控制采用浏览器录制
delFilebeforeconversion = options.get(delFilebeforeconversion, False) # 追加格式后,是否删除原文件
tsconvert_to_m4a = options.get(tsconvert_to_m4a, False) # 这里是控制TS是否追加m4a格式
tsconvert_to_mp4 = options.get(tsconvert_to_mp4, False) # 这里是控制TS是否追加mp4格式
# 读取url_config.ini文件
try:
with open(url_config_file, "r", encoding=encoding) as file:
@ -1050,7 +1026,9 @@ while True:
else:
split_line = [line, '']
url = split_line[0]
if "https://live.douyin.com/" in url or "https://v.douyin.com/" in url:
url_host=url.split('/')[2]
host_list=['live.douyin.com','v.douyin.com','www.tiktok.com']
if url_host in host_list:
new_line = (url, split_line[1])
url_tuples_list.append(new_line)
else:
@ -1068,8 +1046,8 @@ while True:
if len(textNoRepeatUrl) > 0:
for url_tuple in textNoRepeatUrl:
if url_tuple[0] in not_record_list:
print('hhhh')
continue
if url_tuple[0] not in runing_list:
if first_start == False:
print("新增链接: " + url_tuple[0])
@ -1086,7 +1064,7 @@ while True:
first_start = False
except Exception as e:
print("错误信息644:" + str(e) + "\r\n读取的地址为: " + str(live_link) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
print("错误信息644:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno))
logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno))
# 这个是第一次运行其他线程.因为有变量前后顺序的问题,这里等全局变量完成后再运行def函数

View File

@ -4,25 +4,33 @@
Author: Hmily
Github:https://github.com/ihmily
Date: 2023-07-15 23:15:00
Update: 2023-08-03 08:06:43
Copyright (c) 2023 by Hmily, All Rights Reserved.
Function: Get douyin and titok live stream.
"""
import urllib
import urllib.parse
import requests
import re
import json
# 直接选择从网页HTML中获取直播间数据
def get_json_data(url,cookies):
def get_douyin_stream_data(url,cookies):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Referer': 'https://live.douyin.com/',
# 'Cookie':'ttwid=1^%^7CIkooT8SJQrpeYtHlSALuhz9BdcHpaaf9tHQRKHuDaYE^%^7C1687785070^%^7C6690250483b63b6482128174d0f93bd879614d76f1b6e03ca52e032cf7fbaafd; passport_csrf_token=52bece134ac246c81163cc93b72f86a6; passport_csrf_token_default=52bece134ac246c81163cc93b72f86a6; d_ticket=2b9e3eb3626216c0122f0d980f867deb7b414; odin_tt=36dcfa4bd95b1edf0e6445dc9584a7371b4b90a2a8c390292b9be44af8161e06d10e34e6e575bf43c45c0a5871229533; passport_assist_user=CjxqZLHwcDmhxMjoh8u0-Jz4Zmhcq9VzKQqpAjK3P9Ve87i4njjbsai_u6NAI6MebG4KH4QXtbfNRbRFWRgaSAo8oBvHC_eXhqSGSo1uWPz4KBGu9uo-UdwM-5ynbDuDQRybofj0pQlPexcYmVoryiYqpC7yMdKO4rSVclgNEJbxtA0Yia_WVCIBA78Lg84^%^3D; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; passport_auth_status=a74f300f376940d65914eb148d55ca96^%^2C9ca487aea255972120d502f736c5dd7b; passport_auth_status_ss=a74f300f376940d65914eb148d55ca96^%^2C9ca487aea255972120d502f736c5dd7b; sso_auth_status=52ecac30d95890cc7896c880366aa21a; sso_auth_status_ss=52ecac30d95890cc7896c880366aa21a; sso_uid_tt=a71f659bc416b890eb13929ee82a4340; sso_uid_tt_ss=a71f659bc416b890eb13929ee82a4340; toutiao_sso_user=da6c1f563492114cf1ad2dac7617e3f4; toutiao_sso_user_ss=da6c1f563492114cf1ad2dac7617e3f4; sid_ucp_sso_v1=1.0.0-KDNhOTk2NzYzZjkzMGZkZDdlNDU4ODE3YjczNGE3MGExYTBhOGQ0OGIKHQjRpfzk8AEQn6LmpAYY7zEgDDCRxffLBTgCQPEHGgJsZiIgZGE2YzFmNTYzNDkyMTE0Y2YxYWQyZGFjNzYxN2UzZjQ; ssid_ucp_sso_v1=1.0.0-KDNhOTk2NzYzZjkzMGZkZDdlNDU4ODE3YjczNGE3MGExYTBhOGQ0OGIKHQjRpfzk8AEQn6LmpAYY7zEgDDCRxffLBTgCQPEHGgJsZiIgZGE2YzFmNTYzNDkyMTE0Y2YxYWQyZGFjNzYxN2UzZjQ; sid_guard=219f9f2d8838724307589f32debd7d62^%^7C1687785774^%^7C5183988^%^7CFri^%^2C+25-Aug-2023+13^%^3A22^%^3A42+GMT; uid_tt=9f33ebaf781ad75909d6d4edb0b46b86; uid_tt_ss=9f33ebaf781ad75909d6d4edb0b46b86; sid_tt=219f9f2d8838724307589f32debd7d62; sessionid=219f9f2d8838724307589f32debd7d62; sessionid_ss=219f9f2d8838724307589f32debd7d62; sid_ucp_v1=1.0.0-KGZkMzE1MGRhMTZiOTI4M2ZkOTkzMjIwNjQ5NmJhMjhjMzlhZmFmMGYKGQjRpfzk8AEQrqLmpAYY7zEgDDgCQPEHSAQaAmxmIiAyMTlmOWYyZDg4Mzg3MjQzMDc1ODlmMzJkZWJkN2Q2Mg; ssid_ucp_v1=1.0.0-KGZkMzE1MGRhMTZiOTI4M2ZkOTkzMjIwNjQ5NmJhMjhjMzlhZmFmMGYKGQjRpfzk8AEQrqLmpAYY7zEgDDgCQPEHSAQaAmxmIiAyMTlmOWYyZDg4Mzg3MjQzMDc1ODlmMzJkZWJkN2Q2Mg; LOGIN_STATUS=1; store-region=cn-fj; store-region-src=uid; __security_server_data_status=1; __live_version__=^%^221.1.1.1250^%^22; live_can_add_dy_2_desktop=^%^220^%^22; xgplayer_user_id=528819598596; publish_badge_show_info=^%^220^%^2C0^%^2C0^%^2C1689195658062^%^22; msToken=OqErXe8WFLs1OjcJCNX2pNWTvnMSJIKjUXQRa74H4uQo8Ea5hpI8L3zlpK2eq5GUyrHcnTbQ-Kw4UgEntfb9dXu4Ijfowp_LSMxN6AISaoDf-OZCFv2IFS1P4UY2DHYG; ttcid=b8ea42abb1954fd2a913086620d2447f25; download_guide=^%^223^%^2F20230713^%^2F0^%^22; home_can_add_dy_2_desktop=^%^220^%^22; strategyABtestKey=^%^221689299939.836^%^22; FOLLOW_LIVE_POINT_INFO=^%^22MS4wLjABAAAAf6aekfyBsc4u8jMkeYbgnkFa0ksIWKWpGOywuyHXyo4^%^2F1689350400000^%^2F0^%^2F1689299940112^%^2F0^%^22; VIDEO_FILTER_MEMO_SELECT=^%^7B^%^22expireTime^%^22^%^3A1689904741009^%^2C^%^22type^%^22^%^3Anull^%^7D; device_web_cpu_core=8; device_web_memory_size=-1; webcast_local_quality=origin; csrf_session_id=ce1e9bc7ba69feec32889636027ce79a; passport_fe_beating_status=false; webcast_leading_last_show_time=1689300023141; webcast_leading_total_show_times=1; tt_scid=PgQO8z6aK2zeofuxFGselkNp2kSbPFCuPmOMwU9ih.SXbBp8iA7l2EiqrurdBuMWf204; msToken=T5-gdGi61ytJtas2VgUQAogURSJT2pQDWT5E_TPGT9paO9OtCuHOGAl72YAxHhrkKJdNhx6O1JgGn0uzdkfHw_a_MWJS-OJlgroduiLAcJzzSCmib6GfWqIgWT8m2qA3; __ac_nonce=064b0be450072f85a1215; __ac_signature=_02B4Z6wo00f01eL3lWgAAIDA.rQr1mcyUx3ixpHAABwPwM4wuxfDlht-h5js2proCQ84O246NPRhh7I6Pv0tJwHeCCotGZpibWazzH51O7kDcl18lUJy6o8VYLGIG..jFlP-zEpspGjM6AZO34; __ac_referer=https://live.douyin.com/',
'Cookie': cookies}
import urllib.parse
response = requests.get(url, headers=headers)
html_str = response.text
'Cookie': cookies
}
# response = requests.get(url, headers=headers)
# html_str = response.text
# 使用更底层的urllib内置库防止开启代理时导致的抖音录制SSL 443报错
request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request, timeout=10)
html_str = response.read().decode('utf-8')
quote_json_str = re.search('<script id="RENDER_DATA" type="application\/json">(.*?)<\/script><script type=',
html_str).group(1)
unquote_json_str = urllib.parse.unquote(quote_json_str)
@ -30,3 +38,39 @@ def get_json_data(url,cookies):
return json_data
def get_tiktok_stream_data(url,proxy_addr):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.79'
}
if proxy_addr !='':
# 设置代理
proxies = {
'http': proxy_addr,
'https': proxy_addr
}
html = requests.get(url, headers=headers, proxies=proxies)
html_str = html.text
else:
request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request, timeout=10)
html_str = response.read().decode('utf-8')
json_str = re.findall(
'<script id="SIGI_STATE" type="application/json">(.*?)<\/script><script id="SIGI_RETRY" type="application\/json">',
html_str)[0]
# print(json_str)
json_data = json.loads(json_str)
return json_data
if __name__ == '__main__':
Cookie='ttwid=1%7CB1qls3GdnZhUov9o2NxOMxxYS2ff6OSvEWbv0ytbES4%7C1680522049%7C280d802d6d478e3e78d0c807f7c487e7ffec0ae4e5fdd6a0fe74c3c6af149511; my_rd=1; passport_csrf_token=3ab34460fa656183fccfb904b16ff742; passport_csrf_token_default=3ab34460fa656183fccfb904b16ff742; d_ticket=9f562383ac0547d0b561904513229d76c9c21; n_mh=hvnJEQ4Q5eiH74-84kTFUyv4VK8xtSrpRZG1AhCeFNI; store-region=cn-fj; store-region-src=uid; LOGIN_STATUS=1; __security_server_data_status=1; FORCE_LOGIN=%7B%22videoConsumedRemainSeconds%22%3A180%7D; pwa2=%223%7C0%7C3%7C0%22; download_guide=%223%2F20230729%2F0%22; volume_info=%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.6%7D; strategyABtestKey=%221690824679.923%22; stream_recommend_feed_params=%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1536%2C%5C%22screen_height%5C%22%3A864%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A8%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A150%7D%22; VIDEO_FILTER_MEMO_SELECT=%7B%22expireTime%22%3A1691443863751%2C%22type%22%3Anull%7D; home_can_add_dy_2_desktop=%221%22; __live_version__=%221.1.1.2169%22; device_web_cpu_core=8; device_web_memory_size=8; xgplayer_user_id=346045893336; csrf_session_id=2e00356b5cd8544d17a0e66484946f28; odin_tt=724eb4dd23bc6ffaed9a1571ac4c757ef597768a70c75fef695b95845b7ffcd8b1524278c2ac31c2587996d058e03414595f0a4e856c53bd0d5e5f56dc6d82e24004dc77773e6b83ced6f80f1bb70627; __ac_nonce=064caded4009deafd8b89; __ac_signature=_02B4Z6wo00f01HLUuwwAAIDBh6tRkVLvBQBy9L-AAHiHf7; ttcid=2e9619ebbb8449eaa3d5a42d8ce88ec835; webcast_leading_last_show_time=1691016922379; webcast_leading_total_show_times=1; webcast_local_quality=sd; live_can_add_dy_2_desktop=%221%22; msToken=1JDHnVPw_9yTvzIrwb7cQj8dCMNOoesXbA_IooV8cezcOdpe4pzusZE7NB7tZn9TBXPr0ylxmv-KMs5rqbNUBHP4P7VBFUu0ZAht_BEylqrLpzgt3y5ne_38hXDOX8o=; msToken=jV_yeN1IQKUd9PlNtpL7k5vthGKcHo0dEh_QPUQhr8G3cuYv-Jbb4NnIxGDmhVOkZOCSihNpA2kvYtHiTW25XNNX_yrsv5FN8O6zm3qmCIXcEe0LywLn7oBO2gITEeg=; tt_scid=mYfqpfbDjqXrIGJuQ7q-DlQJfUSG51qG.KUdzztuGP83OjuVLXnQHjsz-BRHRJu4e986'
url="https://live.douyin.com/803023607352" # douyin live
url="https://live.douyin.com/745964462470" # douyin live
# url = "https://www.tiktok.com/@pearlgaga88/live" # tiktok live
print(get_douyin_stream_data(url,Cookie))
# print(get_tiktok_stream_data(url,''))