diff --git a/README.md b/README.md index f0697c5..3fc403f 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ ## 🌱使用说明 - 运行主文件main.py启动程序 -- 在config文件夹内的配置文件中对录制进行配置以及添加录制直播间地址 -- 抖音录制需要使用到PC网页端直播间页面的Cookie,请先在config.ini配置文件中添加后再进行抖音录制 +- 在 `config` 文件夹内的配置文件中对录制进行配置,并在 `URL_config.ini` 中添加录制直播间地址。 +- 抖音录制需要使用到PC网页端直播间页面的Cookie,请先在config.ini配置文件中添加后再进行抖音录制(有默认的cookie,但最好还是自己添加自己的) - 注意事项① 录制使用到了ffmpeg,如果没有则无法进行录制,请将ffmpeg.exe放置运行文件同个文件夹 - 注意事项② 录制Tiktok时需要使用vpn代理,请先在配置文件中设置开启代理并添加proxy_addr链接 - 注意事项③ 如果电脑开启了`全局或者规则代理`,可不用添加proxy_addr参数值但仍需在config.ini配置文件中设置开启代理 (最好还是设置一下代理地址,否则很容易请求出错) @@ -121,10 +121,17 @@ GET https://hmily.vip/api/jx/live/convert.php?url=https://v.douyin.com/iQLgKSj/ ## ⏳提交日志 -- 20230919 - - 修复了快手版本更新后录制出错的问题,增加了其自动获取cookie(稳定性未知) - - 修复了Tiktok显示正在直播但不进行录制的问题 +- 20230930 + - 新增抖音从接口获取直播流,增强稳定性 + - 修改快手获取直播流的方式,改用从官方接口获取 + + - 祝大家中秋节快乐! + +- 20230919 + - 修复了快手版本更新后录制出错的问题,增加了其自动获取cookie(~~稳定性未知~~) + - 修复了Tiktok显示正在直播但不进行录制的问题 + - 20230907 - 修复了因抖音官方更新了版本导致的录制出错以及短链接转换出错 diff --git a/main.py b/main.py index 546f07b..45d66c5 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,7 @@ Author: Hmily Github: https://github.com/ihmily Date: 2023-07-17 23:52:05 -Update: 2023-09-19 00:16:38 +Update: 2023-09-30 00:39:17 Copyright (c) 2023 by Hmily, All Rights Reserved. Function: Record live stream video. """ @@ -26,7 +26,7 @@ from web_rid import * from msg_push import * # 版本号 -version = "v1.0.9" +version = "v2.0.1" platforms = "抖音|Tiktok|快手|虎牙|斗鱼|YY|B站" # --------------------------log日志------------------------------------- @@ -135,8 +135,8 @@ def display_info(): else: start5_time = now_time except Exception as e: - print("错误信息:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno)) - logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) + print(f"错误信息:{e}\r\n发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") def update_file(file, old_str, new_str): @@ -250,18 +250,17 @@ def change_max_connect(): @trace_error_decorator def get_douyin_stream_url(json_data): - room_info = json_data.get('roomInfo', {}) - anchor_name = room_info.get('anchor', {}).get('nickname', '') + anchor_name = json_data.get('anchor_name', None) result = { "anchor_name": anchor_name, "is_live": False, } - status = room_info.get('room', {}).get("status", 4) # 直播状态 2 是正在直播、4 是未开播 + status = json_data.get("status", 4) # 直播状态 2 是正在直播、4 是未开播 if status == 2: - stream_url = room_info['room']['stream_url'] + stream_url = json_data['stream_url'] flv_url_list = stream_url['flv_pull_url'] m3u8_url_list = stream_url['hls_pull_url_map'] @@ -331,42 +330,39 @@ def get_tiktok_stream_url(json_data): @trace_error_decorator def get_kuaishou_stream_url(json_data): - live_room = json_data.get('liveroom', {}).get('playList')[0] - anchor_name = live_room.get('author', {}).get('name', '') + anchor_name = json_data.get('user', {}).get('user_name', '') result = { "anchor_name": anchor_name, "is_live": False, } - status = live_room.get('isLiving', False) + status = json_data.get('living', False) if status: - stream_data = live_room.get('liveStream', {}).get('playUrls', [{}])[0].get('adaptationSet', {}).get( - 'representation', []) + m3u8_url_list = json_data.get('multiResolutionHlsPlayUrls', {})[::-1] + while len(m3u8_url_list) < 4: + m3u8_url_list.append(m3u8_url_list[-1]) + flv_url_list = json_data.get('multiResolutionPlayUrls', {})[::-1] + while len(flv_url_list) < 4: + flv_url_list.append(flv_url_list[-1]) - if stream_data: - quality_list = [i for i in range(len(stream_data))][::-1] - - while len(quality_list) < 4: - quality_list.append(quality_list[-1]) - - video_quality_options = { - "原画": quality_list[0], - "蓝光": quality_list[0], - "超清": quality_list[1], - "高清": quality_list[2], - "标清": quality_list[3] - } - - if video_quality not in video_quality_options: - raise ValueError( - f"Invalid video quality. Available options are: {', '.join(video_quality_options.keys())}") - - flv_url = stream_data[video_quality_options[video_quality]]['url'] + quality_mapping = { + '原画': 0, + '蓝光': 0, + '超清': 1, + '高清': 2, + '标清': 3, + } + if video_quality in quality_mapping: + quality_index = quality_mapping[video_quality] + m3u8_url = m3u8_url_list[quality_index]['urls'][0]['url'] + flv_url = flv_url_list[quality_index]['urls'][0]['url'] + result['m3u8_url'] = m3u8_url result['flv_url'] = flv_url result['is_live'] = True - result['record_url'] = flv_url # 快手只有flv视频流 + result['record_url'] = flv_url + return result @@ -383,17 +379,17 @@ def get_huya_stream_url(json_data): if stream_info_list: select_cdn = stream_info_list[0] - s_flv_url = select_cdn.get('sFlvUrl') - s_stream_name = select_cdn.get('sStreamName') - s_flv_url_suffix = select_cdn.get('sFlvUrlSuffix') - s_hls_url = select_cdn.get('sHlsUrl') - s_hls_url_suffix = select_cdn.get('sHlsUrlSuffix') - s_flv_anti_code = select_cdn.get('sFlvAntiCode') + flv_url = select_cdn.get('sFlvUrl') + stream_name = select_cdn.get('sStreamName') + flv_url_suffix = select_cdn.get('sFlvUrlSuffix') + hls_url = select_cdn.get('sHlsUrl') + hls_url_suffix = select_cdn.get('sHlsUrlSuffix') + flv_anti_code = select_cdn.get('sFlvAntiCode') - flv_url = f'{s_flv_url}/{s_stream_name}.{s_flv_url_suffix}?{s_flv_anti_code}&ratio=' - m3u8_url = f'{s_hls_url}/{s_stream_name}.{s_hls_url_suffix}?{s_flv_anti_code}&ratio=' + flv_url = f'{flv_url}/{stream_name}.{flv_url_suffix}?{flv_anti_code}&ratio=' + m3u8_url = f'{hls_url}/{stream_name}.{hls_url_suffix}?{flv_anti_code}&ratio=' - quality_list = s_flv_anti_code.split('&exsphd=') + quality_list = flv_anti_code.split('&exsphd=') if len(quality_list) > 1: pattern = r"(?<=264_)\d+" quality_list = [x for x in re.findall(pattern, quality_list[1])][::-1] @@ -578,7 +574,7 @@ def start_record(url_tuple, count_variable=-1): elif record_url.find("https://live.kuaishou.com/") > -1: with semaphore: - json_data = get_kuaishou_stream_data(record_url, ks_cookie) + json_data = get_kuaishou_stream_data2(record_url, ks_cookie) port_info = get_kuaishou_stream_url(json_data) elif record_url.find("https://www.huya.com/") > -1: @@ -651,8 +647,8 @@ def start_record(url_tuple, count_variable=-1): os.makedirs('./' + anchor_name) except Exception as e: - print("路径错误信息708: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) - logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) + print(f"路径错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") if not os.path.exists(full_path): print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置") @@ -712,18 +708,19 @@ def start_record(url_tuple, count_variable=-1): create_var[str(filename_short)].start() try: - # “port_info[3]”对应的是flv地址,使用老方法下载(直接请求下载flv)只能是下载flv流的。 - real_url = port_info['flv_url'] - _filepath, _ = urllib.request.urlretrieve(real_url, full_path + '/' + filename) - record_finished = True - record_finished_2 = True - count_time = time.time() - + flv_url = port_info.get('flv_url', None) + if flv_url: + _filepath, _ = urllib.request.urlretrieve(real_url, + full_path + '/' + filename) + record_finished = True + record_finished_2 = True + count_time = time.time() + else: + raise Exception('该直播无flv直播流,请切换视频保存类型') except Exception as e: - print('\r' + time.strftime('%Y-%m-%d %H:%M:%S ') + anchor_name + ' 未开播') - logger.warning( - "错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) + print(f"\r{time.strftime('%Y-%m-%d %H:%M:%S')} {anchor_name} 未开播") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") elif video_save_type == "MKV": @@ -753,9 +750,8 @@ def start_record(url_tuple, count_variable=-1): count_time = time.time() 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)) + print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") elif video_save_type == "MP4": @@ -791,9 +787,8 @@ def start_record(url_tuple, count_variable=-1): 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)) + print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") elif video_save_type == "MKV音频": @@ -818,10 +813,8 @@ def start_record(url_tuple, count_variable=-1): threading.Thread(target=converts_m4a, args=(file,)).start() 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)) - + print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") elif video_save_type == "TS音频": filename = anchor_name + '_' + now + ".ts" @@ -845,10 +838,8 @@ def start_record(url_tuple, count_variable=-1): threading.Thread(target=converts_m4a, args=(file,)).start() 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)) - + print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") else: @@ -891,7 +882,7 @@ def start_record(url_tuple, count_variable=-1): except subprocess.CalledProcessError as e: logging.warning(str(e.output)) - logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") break @@ -931,28 +922,19 @@ def start_record(url_tuple, count_variable=-1): 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 record_name in recording: - recording.remove(record_name) - if anchor_name in unrecording: - unrecording.add(anchor_name) + print(f"{e.output} 发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") if record_finished_2 == True: if record_name in recording: recording.remove(record_name) if anchor_name in unrecording: unrecording.add(anchor_name) - print('\n' + anchor_name + " " + time.strftime('%Y-%m-%d %H:%M:%S ') + '直播录制完成\n') + print(f"\n{anchor_name} {time.strftime('%Y-%m-%d %H:%M:%S')} 直播录制完成\n") record_finished_2 = False except Exception as e: - print( - "错误信息:" + str(e) + "\r\n读取的地址为: " + str(record_url) + " 发生错误的行数: " + str( - e.__traceback__.tb_lineno)) + print(f"错误信息:{e}\r\n读取的地址为: {record_url} 发生错误的行数: {e.__traceback__.tb_lineno}") logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) warning_count += 1 @@ -986,9 +968,9 @@ def start_record(url_tuple, count_variable=-1): if loop_time: print('\r检测直播间中...', end="") except Exception as e: - print("错误信息:" + 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)) + print(f"错误信息:{e}\r\n发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") + print(f"线程崩溃2秒后重试.错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") warning_count += 1 time.sleep(2) @@ -1206,6 +1188,7 @@ while True: else: split_line = [line, ''] url = split_line[0] + if ('http://' not in url) and ('https://' not in url): url = 'https://' + url @@ -1254,8 +1237,8 @@ while True: first_start = False except Exception as e: - print("错误信息:" + str(e) + "\r\n发生错误的行数: " + str(e.__traceback__.tb_lineno)) - logger.warning("错误信息: " + str(e) + " 发生错误的行数: " + str(e.__traceback__.tb_lineno)) + print(f"错误信息:{e}\r\n发生错误的行数: {e.__traceback__.tb_lineno}") + logger.warning(f"错误信息: {e} 发生错误的行数: {e.__traceback__.tb_lineno}") if firstRunOtherLine: t = threading.Thread(target=display_info, args=(), daemon=True) @@ -1265,8 +1248,6 @@ while True: firstRunOtherLine = False - # 总体循环3s time.sleep(3) - diff --git a/spider.py b/spider.py index 6b32d15..2db1693 100644 --- a/spider.py +++ b/spider.py @@ -4,7 +4,7 @@ Author: Hmily Github:https://github.com/ihmily Date: 2023-07-15 23:15:00 -Update: 2023-09-17 16:59:01 +Update: 2023-09-30 00:39:17 Copyright (c) 2023 by Hmily, All Rights Reserved. Function: Get live stream data. """ @@ -23,45 +23,56 @@ no_proxy_handler = urllib.request.ProxyHandler({}) opener = urllib.request.build_opener(no_proxy_handler) -# 直接选择从网页HTML中获取直播间数据 -def get_douyin_stream_data(url, cookies=''): - if cookies == '': - 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' - +def get_douyin_stream_data(url, cookies=None): 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': cookies + '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' } + if cookies: + headers['Cookie'] = cookies - # response = requests.get(url, headers=headers) - # html_str = response.text - # 使用更底层的urllib内置库,防止开启代理时导致的抖音录制SSL 443报错 - req = urllib.request.Request(url, headers=headers) - response = opener.open(req, timeout=15) - html_str = response.read().decode('utf-8') - match_json_str = re.search(r'(\{\\\"state\\\"\:.*?)\]\\n\"\]\)', html_str) - if not match_json_str: - match_json_str = re.search(r'(\{\\\"common\\\"\:.*?)\]\\n\"\]\)\<\/script\>\