摘要
delphi 通过TNetHTTPClient解析抖音无水印高清视频及图文原理及解决X-Bogus签名验证2023-5-1
一、杂谈
最近有很多热心网友反馈抖音去水印又不行了,之前是时不时被blocked,现在直接连内容都没有了,返回直接就是空了,我们今天简要给大家分析一下请求过程,附上delphi 源码,及生成签名验证,成功请求到json数据的解决方法。
二、请求过程分析
我们还是先获取一个抖音链接
https://v.douyin.com/A2VSVxc/
通过访问重定向
https://www.douyin.com/video/7065264218437717285
然后提取到其中的视频ID
7065264218437717285
如果是之前,我们会直接GET请求
https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285
然后就能得到响应内容了。
但是这种方法已经失效了,今天我们会讲解如何在增加一些请求头参数以及X-Bogus后,可以仍然获取到JSON格式的数据。如:
{"aweme_detail":{"admire_auth":{"admire_button":0,"is_admire":0,"is_click_admire_icon_recently":0,"is_fifty_admire_author_stable_fans":0,"is_show_admire_button":0,"is_show_admire_tab":0},"anchors":null,"authentication_token":"MS4wLjAAAAAAywpT7ak4-F6DYZWfd4BIufeG4pLAg5m3WBrbovLaZO-2zVbqglAdnNUFVE3E02CfcFld_1optjyrCbs8-kQr3UTxkhjoqvasOtfKnwuOJyetvxQyF_3L-YOZKpy7gscEhco574O9_zOY9kMOC7UzL1bbRUFx7J3AdEQ3XEIqHCb8_7-ouclds25P5Wub0Oig","author":{"avatar_thumb":{"height":720,"uri":"100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p26.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"cf_list":null,"close_friend_type":0,"contacts_status":2,"contrail_list":null,"cover_url":[{"height":720,"uri":"c8510002be9a3a61aad2","url_list":["https://p6-sign.douyinpic.com/obj/c8510002be9a3a61aad2?x-expires=1683961200\&x-signature=wRVgF%2FCk8DacRxlvfZSL%2BOssaHs%3D\&from=116350172","https://p95-sign.douyinpic.com/obj/c8510002be9a3a61aad2?x-expires=1683961200\&x-signature=e1WSVCYLVV6HZIueDfgBBWlbz08%3D\&from=116350172","https://p26-sign.douyinpic.com/obj/c8510002be9a3a61aad2?x-expires=1683961200\&x-signature=IBYuV82UtqUPopV50ILeGhCsSdY%3D\&from=116350172"],"width":720}],"create_time":0,"custom_verify":"","data_label_list":null,"endorsement_info_list":null,"enterprise_verify_reason":"","familiar_visitor_user":null,"favoriting_count":8,"follow_status":0,"follower_count":0,"follower_list_secondary_information_struct":null,"follower_status":0,"following_count":0,"im_role_ids":null,"is_ad_fake":false,"is_blocked_v2":false,"is_blocking_v2":false,"is_cf":0,"max_follower_count":0,"nickname":"唯美音乐","not_seen_item_id_list":null,"not_seen_item_id_list_v2":null,"offline_info_list":null,"personal_tag_list":null,"prevent_download":false,"risk_notice_text":"","sec_uid":"MS4wLjABAAAAQFBkJ8nzjmCDRJjeB8ltsYaRu_-BCqhPzgnsQFGg8pYilpgHEu1b03TXST-yXQ9H","secret":0,"share_info":{"share_desc":"","share_desc_info":"","share_qrcode_url":{"height":720,"uri":"","url_list":[],"width":720},"share_title":"","share_title_myself":"","share_title_other":"","share_url":"","share_weibo_desc":""},"short_id":"82168160565","signature":"喜欢音乐!","signature_extra":null,"special_follow_status":0,"special_people_labels":null,"status":1,"text_extra":null,"total_favorited":5,"uid":"3580451906133656","unique_id":"82168160565","user_age":23,"user_canceled":false,"user_permissions":null,"verification_type":1},"author_mask_tag":0,"author_user_id":3580451906133656,"aweme_acl":{"download_mask_panel":{"code":1,"show_type":0}},"aweme_control":{"can_comment":true,"can_forward":true,"can_share":true,"can_show_comment":true},"aweme_id":"7216976368037367078","aweme_type":0,"book_bar":{},"challenge_position":null,"chapter_list":null,"collect_stat":0,"collection_corner_mark":0,"comment_gid":7216976368037367078,"comment_list":null,"comment_permission_info":{"can_comment":true,"comment_permission_status":0,"item_detail_entry":true,"press_entry":true,"toast_guide":false},"commerce_config_data":null,"common_bar_info":"[]","component_info_v2":"{\\"desc_lines_limit\\":0,\\"hide_marquee\\":false}","cover_labels":null,"create_time":1680333310,"desc":"治愈系风景()阳春三月#唯美意境#乡村美景 #自然美景#人间仙境美如画 #住进风景里 #慢下来看世界 #大自然天然氧吧 #云雾缭绕人间仙境 #身边的风景 #唯美意境 #聆听大自然的旋律 #一起享受大自然的美","digg_lottie":{"can_bomb":0,"lottie_id":""},"disable_relation_bar":0,"dislike_dimension_list":null,"dislike_dimension_list_v2":null,"duet_aggregate_in_music_tab":false,"duration":221667,"feed_comment_config":{"author_audit_status":0,"input_config_text":"善语结善缘,恶言伤人心"},"geofencing":[],"geofencing_regions":null,"group_id":"7209289132470701368","guide_scene_info":{"diamond_expose_info_str":"","feed_origin_gid_info_str":"","guide_scene_type":0},"hybrid_label":null,"image_album_music_info":{"begin_time":-1,"end_time":-1,"volume":-1},"image_comment":{},"image_infos":null,"image_list":null,"images":null,"img_bitrate":null,"impression_data":{"group_id_list_a":[7209289132470701368],"group_id_list_b":[7216976368037367078],"group_id_list_c":[],"similar_id_list_a":null,"similar_id_list_b":null},"interaction_stickers":null,"is_ads":false,"is_collects_selected":0,"is_duet_sing":false,"is_image_beat":false,"is_life_item":false,"is_share_post":false,"is_story":0,"is_top":0,"item_warn_notification":{"content":"","show":false,"type":0},"label_top_text":null,"long_video":null,"music":{"album":"","artist_user_infos":null,"artists":[],"audition_duration":221,"author":"唯美音乐","author_deleted":false,"author_position":null,"author_status":1,"avatar_large":{"height":720,"uri":"1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"avatar_medium":{"height":720,"uri":"720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p11.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"avatar_thumb":{"height":720,"uri":"100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p6.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p6.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"binded_challenge_id":0,"can_background_play":true,"collect_stat":0,"cover_hd":{"height":720,"uri":"1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"cover_large":{"height":720,"uri":"1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/1080x1080/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"cover_medium":{"height":720,"uri":"720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p11.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p26.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"cover_thumb":{"height":720,"uri":"100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec","url_list":["https://p6.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p11.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.webp?from=116350172","https://p6.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_2bd8adf9dcbb48fc8400270971097bec.jpeg?from=116350172"],"width":720},"dmv_auto_show":false,"dsp_status":10,"duration":221,"end_time":0,"external_song_info":[],"extra":"{\\"is_red\\":0,\\"is_subsidy_exp\\":false,\\"with_aed_model\\":1,\\"has_edited\\":0,\\"hotsoon_review_time\\":-1,\\"cover_colors\\":null,\\"music_label_id\\":null,\\"dsp_switch\\":0,\\"reviewed\\":0,\\"review_unshelve_reason\\":0,\\"is_aed_music\\":1,\\"activities\\":[10006],\\"schedule_search_time\\":0,\\"aggregate_exempt_conf\\":[],\\"extract_item_id\\":7216976368037367078,\\"beats\\":{},\\"douyin_beats_info\\":{},\\"music_tagging\\":{\\"Languages\\":[\\"Chinese\\"],\\"Moods\\":[\\"Chill\\"],\\"Genres\\":[\\"Pop\\",\\"Chinese Pop\\",\\"Chinese Style\\",\\"China-Wave\\"],\\"Themes\\":[\\"Spring\\",\\"Sunny\\"],\\"AEDs\\":[\\"Vocal\\",\\"Female\\"],\\"SingingVersions\\":[\\"Original\\"],\\"Instruments\\":null}}","id":7216976648506362683,"id_str":"7216976648506362683","is_audio_url_with_cookie":false,"is_commerce_music":false,"is_del_video":false,"is_matched_metadata":false,"is_original":false,"is_original_sound":true,"is_pgc":false,"is_restricted":false,"is_video_self_see":false,"luna_info":{"has_copyright":false,"is_luna_user":false},"lyric_short_position":null,"matched_pgc_sound":{"author":"司南","cover_medium":{"height":720,"uri":"tos-cn-v-2774c002/bc6b2b6404de4d778329ae9377065177","url_list":["https://p26.douyinpic.com/aweme/200x200/tos-cn-v-2774c002/bc6b2b6404de4d778329ae9377065177.jpeg","https://p6.douyinpic.com/aweme/200x200/tos-cn-v-2774c002/bc6b2b6404de4d778329ae9377065177.jpeg","https://p3.douyinpic.com/aweme/200x200/tos-cn-v-2774c002/bc6b2b6404de4d778329ae9377065177.jpeg"],"width":720},"mixed_author":"","mixed_title":"","title":"春三月"},"mid":"7216976648506362683","music_chart_ranks":null,"music_collect_count":0,"music_cover_atmosphere_color_value":"","music_status":1,"musician_user_infos":null,"mute_share":false,"offline_desc":"","owner_handle":"82168160565","owner_id":"3580451906133656","owner_nickname":"唯美音乐","pgc_music_type":2,"play_url":{"height":720,"uri":"https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7216976633079827255.mp3","url_key":"7216976648506362683","url_list":["https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7216976633079827255.mp3","https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/7216976633079827255.mp3"],"width":720},"position":null,"prevent_download":false,"prevent_item_download_status":0,"preview_end_time":0,"preview_start_time":0,"reason_type":0,"redirect":false,"schema_url":"","search_impr":{"entity_id":"7216976648506362683"},"sec_uid":"MS4wLjABAAAAQFBkJ8nzjmCDRJjeB8ltsYaRu_-BCqhPzgnsQFGg8pYilpgHEu1b03TXST-yXQ9H","shoot_duration":221,"song":{"artists":null,"chorus_v3_infos":null,"id":6819973422223738881,"id_str":"6819973422223738881"},"source_platform":23,"start_time":0,"status":1,"tag_list":null,"title":"@唯美音乐创作的原声","unshelve_countries":null,"user_count":0,"video_duration":221},"nickname_position":null,"origin_comment_ids":null,"origin_text_extra":[],"original_images":null,"packed_clips":null,"photo_search_entrance":{"ecom_type":0},"position":null,"press_panel_info":"[{\\"interactive\\":[\\"2_story\\",\\"2_friend\\"]},{\\"feedback\\":[\\"rr_feedback\\",\\"dislike\\",\\"ignore\\",\\"block\\",\\"unfollow\\",\\"sever\\",\\"dislike_collect\\"]},{\\"control\\":[\\"speed\\",\\"auth\\",\\"delete\\",\\"save\\",\\"collect\\",\\"reward\\",\\"bg_play\\",\\"duet\\",\\"together\\"]}]","preview_title":"治愈系风景()阳春三月#唯美意境#乡村美景 #自然美景#人间仙境美如画 #住进风景里 #慢下来看世界 #大自然天然氧吧 #云雾缭绕人间仙境 #身边的风景 #唯美意境 #聆听大自然的旋律 #一起享受大自然的美","preview_video_status":1,"promotions":[],"rate":10,"ref_tts_id_list":null,"ref_voice_modify_id_list":null,"region":"","relation_labels":null,"search_impr":{"entity_id":"7216976368037367078","entity_type":"GENERAL"},"series_paid_info":{"item_price":0,"series_paid_status":0},"share_info":{"share_desc":"在抖音,记录美好生活","share_desc_info":"#在抖音,记录美好生活#治愈系风景()阳春三月#唯美意境#乡村美景 #自然美景#人间仙境美如画 #住进风景里 #慢下来看世界 #大自然天然氧吧 #云雾缭绕人间仙境 #身边的风景 #唯美意境 #聆听大自然的旋律 #一起享受大自然的美","share_link_desc":"7.97 QkP:/ 复制打开抖音,看看【唯美音乐的作品】治愈系风景()阳春三月# 唯美意境# 乡村美景 #... %s","share_url":"https://www.iesdouyin.com/share/video/7216976368037367078/?region=CN\&mid=7216976648506362683\&u_code=-1\&did=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ\&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ\&with_sec_did=1\&titleType=title\&from_ssr=1"},"share_url":"https://www.iesdouyin.com/share/video/7216976368037367078/?region=CN\&mid=7216976648506362683\&u_code=-1\&did=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ\&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ\&with_sec_did=1\&titleType=title\&from_ssr=1","should_open_ad_report":false,"show_follow_button":{},"social_tag_list":null,"standard_bar_info_list":null,"statistics":{"admire_count":0,"aweme_id":"7216976368037367078","collect_count":0,"comment_count":0,"digg_count":0,"play_count":0,"share_count":0},"status":{"allow_share":true,"aweme_id":"7216976368037367078","in_reviewing":false,"is_delete":false,"is_prohibited":false,"listen_video_status":2,"part_see":0,"private_status":0,"review_result":{"review_status":0}},"text_extra":[],"tts_id_list":null,"uniqid_position":null,"user_digged":0,"user_recommend_status":0,"video":{"big_thumbs":[{"duration":221.616667,"fext":"jpg","img_num":222,"img_url":"https://p6-sign.douyinpic.com/tos-cn-p-0015/e23534e35e3a4085a998836f8918a28b_1680333327~tplv-noop.image?x-expires=1682758812\&x-signature=PBhy%2BaJXNobFabDaTjnxNUmG030%3D","img_x_len":10,"img_x_size":134,"img_y_len":23,"img_y_size":240,"interval":1,"uri":"tos-cn-p-0015/e23534e35e3a4085a998836f8918a28b_1680333327"}],"bit_rate":[{"FPS":30,"HDR_bit":"","HDR_type":"","bit_rate":2034433,"gear_name":"normal_720_0","is_bytevc1":0,"is_h265":0,"play_addr":{"data_size":56370837,"file_cs":"c:0-241805-0222|a:v0200fg10000cgjtjijc77ufd9v44mug","file_hash":"b48e194ade984fa9eb1ec9f48e443c88","height":1280,"uri":"v0200fg10000cgjtjijc77ufd9v44mug","url_key":"v0200fg10000cgjtjijc77ufd9v44mug_h264_720p_2034433","url_list":["https://v3-cold-src.douyinvod.com/eca3583b64e7b13bd40550a157a0a820/644cdc9c/video/tos/cn/tos-cn-ve-15c001-alinc2/ogkIAwX9hfAaqJQgU9tzCl5CeW21zPknzAnNBj/?a=1128\&ch=26\&cr=3\&dr=0\&lr=all\&cd=0%7C0%7C0%7C3\&br=1986\&bt=1986\&cs=0\&ds=3\&ft=7nqWZmWwwZR.0sCao4PDS6kFgAX1tG.dV1q9eFQImgCr12nz\&mime_type=video_mp4\&qs=0\&rc=OjhnOzdmNzpoNzlkNDk3NkBpanA3PGk6Zmx3ajMzNGkzM0A1XjUuMWNeX2ExXzUyMDExYSNyMXNhcjRnZ2dgLS1kLS9zcw%3D%3D\&l=202304291556306B5EBC5212311C800137\&btag=e000a8000","https://v3-ec-or.douyinvod.com/eca3583b64e7b13bd40550a157a0a820/644cdc9c/video/tos/cn/tos-cn-ve-15c001-alinc2/ogkIAwX9hfAaqJQgU9tzCl5CeW21zPknzAnNBj/?a=1128\&ch=26\&cr=3\&dr=0\&lr=all\&cd=0%7C0%7C0%7C3\&br=1986\&bt=1986\&cs=0\&ds=3\&ft=7nqWZmWwwZR.0sCao4PDS6kFgAX1tG.dV1q9eFQImgCr12nz\&mime_type=video_mp4\&qs=0\&rc=OjhnOzdmNzpoNzlkNDk3NkBpanA3PGk6Zmx3ajMzNGkzM0A1XjUuMWNeX2ExXzUyMDExYSNyMXNhcjRnZ2dgLS1kLS9zcw%3D%3D\&l=202304291556306B5EBC5212311C800137\&btag=e000a8000","https://api-play-hl.amemv.com/aweme/v1/play/?video_id=v0200fg10000cgjtjijc77ufd9v44mug\&line=0\&file_id=434436518ab644deb7a4fc44188da587\&sign=b48e194ade984fa9eb1ec9f48e443c88\&is_play_url=1\&source=PackSourceEnum_AWEME_DETAIL","https://api-hl.amemv.com/aweme/v1/play/?video_id=v0200fg10000cgjtjijc77ufd9v44mug\&line=1\&file_id=434436518ab644deb7a4fc44188da587\&sign=b48e194ade984fa9eb1ec9f48e443c88\&is_play_url=1\&source=PackSourceEnum_AWEME_DETAIL"],"width":720},"quality_type":10,"video_extra":"{\\"PktOffsetMap\\":\\"\\"}"}],"bit_rate_audio":null,"cover":{"height":720,"uri":"tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a","url_list":["https://p9-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~c5_300x400.webp?x-expires=1683961200\&x-signature=oQnNx%2FrgqIJLvcU8tDpxUGJo7Qk%3D\&from=3213915784_large\&s=PackSourceEnum_AWEME_DETAIL\&se=false\&sc=cover\&l=202304291556306B5EBC5212311C800137","https://p3-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~c5_300x400.webp?x-expires=1683961200\&x-signature=l6mvnOvth8X0ZjshX2YpMfLfmlM%3D\&from=3213915784_large\&s=PackSourceEnum_AWEME_DETAIL\&se=false\&sc=cover\&l=202304291556306B5EBC5212311C800137","https://p26-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~c5_300x400.webp?x-expires=1683961200\&x-signature=MLku3rXSyOddP%2FnDGyES7Ox8KNM%3D\&from=3213915784_large\&s=PackSourceEnum_AWEME_DETAIL\&se=false\&sc=cover\&l=202304291556306B5EBC5212311C800137","https://p9-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~c5_300x400.jpeg?x-expires=1683961200\&x-signature=zjkW%2FT2NvO2KWe57jhDnxxXKzak%3D\&from=3213915784_large\&s=PackSourceEnum_AWEME_DETAIL\&se=false\&sc=cover\&l=202304291556306B5EBC5212311C800137"],"width":720},"cover_original_scale":{"height":720,"uri":"tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a","url_list":["https://p9-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=oFfihOxEIehze%2FVVpHbfTGBvGx8%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p3-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=Ta1epkLIyjZPdn1D3hT9m6IWWIA%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p26-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=vu5gNLbW335ReVWfLqpnh9xtjlY%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p9-sign.douyinpic.com/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a~tplv-dy-360p.jpeg?x-expires=1683961200\&x-signature=mFc3GUkuXfwlp70pvHUiUs%2FNsWQ%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137"],"width":720},"duration":221667,"dynamic_cover":{"height":720,"uri":"tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a","url_list":["https://p9-sign.douyinpic.com/obj/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a?x-expires=1683961200\&x-signature=gTbtJy72RkKmntQdWyzaETwKkaA%3D\&from=3213915784_large","https://p3-sign.douyinpic.com/obj/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a?x-expires=1683961200\&x-signature=rVfh2i8u7Ebb%2BLOipl7Yo4NNdxo%3D\&from=3213915784_large","https://p26-sign.douyinpic.com/obj/tos-cn-i-dy/d01da746d8e7436cae3b5303e67b8a0a?x-expires=1683961200\&x-signature=4s11bNz2jTU5D2UQcodm4XBgbwE%3D\&from=3213915784_large"],"width":720},"height":1282,"is_h265":0,"is_long_video":1,"is_source_HDR":0,"meta":"{\\"loudness\\":\\"-11.9\\",\\"peak\\":\\"1\\",\\"qprf\\":\\"1.000\\",\\"sr_score\\":\\"0.000\\"}","origin_cover":{"height":720,"uri":"tos-cn-p-0015/089b2db182db46cda8bc70cd876af2c8_1680333314","url_list":["https://p26-sign.douyinpic.com/tos-cn-p-0015/089b2db182db46cda8bc70cd876af2c8_1680333314~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=LrifHeSzrjkWzmkVIeMut62AN6w%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p3-sign.douyinpic.com/tos-cn-p-0015/089b2db182db46cda8bc70cd876af2c8_1680333314~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=19eEyekeWbqnwcjSsbHUSryttYI%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p9-sign.douyinpic.com/tos-cn-p-0015/089b2db182db46cda8bc70cd876af2c8_1680333314~tplv-dy-360p.webp?x-expires=1683961200\&x-signature=jmNBvpLrXF61exqBQ16NPeCUVwo%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137","https://p26-sign.douyinpic.com/tos-cn-p-0015/089b2db182db46cda8bc70cd876af2c8_1680333314~tplv-dy-360p.jpeg?x-expires=1683961200\&x-signature=WWpiqBvYF5%2FOAiwqftW7Ye2zdLY%3D\&from=3213915784\&se=false\&biz_tag=feed_cover\&l=202304291556306B5EBC5212311C800137"],"width":720},"play_addr":{"data_size":56370837,"file_cs":"c:0-241805-0222|a:v0200fg10000cgjtjijc77ufd9v44mug","file_hash":"b48e194ade984fa9eb1ec9f48e443c88","height":1280,"uri":"v0200fg10000cgjtjijc77ufd9v44mug","url_key":"v0200fg10000cgjtjijc77ufd9v44mug_h264_720p_2034433","url_list":["https://v3-cold-src.douyinvod.com/eca3583b64e7b13bd40550a157a0a820/644cdc9c/video/tos/cn/tos-cn-ve-15c001-alinc2/ogkIAwX9hfAaqJQgU9tzCl5CeW21zPknzAnNBj/?a=1128\&ch=26\&cr=3\&dr=0\&lr=all\&cd=0%7C0%7C0%7C3\&br=1986\&bt=1986\&cs=0\&ds=3\&ft=7nqWZmWwwZR.0sCao4PDS6kFgAX1tG.dV1q9eFQImgCr12nz\&mime_type=video_mp4\&qs=0\&rc=OjhnOzdmNzpoNzlkNDk3NkBpanA3PGk6Zmx3ajMzNGkzM0A1XjUuMWNeX2ExXzUyMDExYSNyMXNhcjRnZ2dgLS1kLS9zcw%3D%3D\&l=202304291556306B5EBC5212311C800137\&btag=e000a8000","https://v3-ec-or.douyinvod.com/eca3583b64e7b13bd40550a157a0a820/644cdc9c/video/tos/cn/tos-cn-ve-15c001-alinc2/ogkIAwX9hfAaqJQgU9tzCl5CeW21zPknzAnNBj/?a=1128\&ch=26\&cr=3\&dr=0\&lr=all\&cd=0%7C0%7C0%7C3\&br=1986\&bt=1986\&cs=0\&ds=3\&ft=7nqWZmWwwZR.0sCao4PDS6kFgAX1tG.dV1q9eFQImgCr12nz\&mime_type=video_mp4\&qs=0\&rc=OjhnOzdmNzpoNzlkNDk3NkBpanA3PGk6Zmx3ajMzNGkzM0A1XjUuMWNeX2ExXzUyMDExYSNyMXNhcjRnZ2dgLS1kLS9zcw%3D%3D\&l=202304291556306B5EBC5212311C800137\&btag=e000a8000","https://api-play-hl.amemv.com/aweme/v1/play/?video_id=v0200fg10000cgjtjijc77ufd9v44mug\&line=0\&file_id=434436518ab644deb7a4fc44188da587\&sign=b48e194ade984fa9eb1ec9f48e443c88\&is_play_url=1\&source=PackSourceEnum_AWEME_DETAIL","https://api-hl.amemv.com/aweme/v1/play/?video_id=v0200fg10000cgjtjijc77ufd9v44mug\&line=1\&file_id=434436518ab644deb7a4fc44188da587\&sign=b48e194ade984fa9eb1ec9f48e443c88\&is_play_url=1\&source=PackSourceEnum_AWEME_DETAIL"],"width":720},"ratio":"720p","video_model":"","width":720},"video_game_data_channel_config":{},"video_labels":null,"video_tag":[{"level":1,"tag_id":2028,"tag_name":"旅游"},{"level":2,"tag_id":2028003,"tag_name":"旅行摄影"},{"level":3,"tag_id":2028003003,"tag_name":"平原公园"}],"video_text":[],"voice_modify_id_list":null},"log_pb":{"impr_id":"202304291556306B5EBC5212311C800137"},"status_code":0}
可以看到,获取到的aweme_detail json数据和以前一样。
三、URL参数X-Bogus(全站通用)
X-Bogus你可以理解为是一个根据视频ID及user-agent通过JS生成的用户信息参数,它可以用于校验。
详细的一篇分析可以参考Freebuf上的《【JS 逆向百例】某音 X-Bogus 逆向分析,JSVMP 纯算法还原》。
四、解析原理及流程
下面是完整的delphi 源码解析类,主要流程如下:
1.传入抖音分享链接:
https://v.douyin.com/A2VSVxc/
重定向得到:
https://www.douyin.com/video/7065264218437717285
2.提取到其中的视频ID:
7065264218437717285
3.无水印视频接口不变:
https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285
4.(增加步骤4)根据X-Bogus 算法,传入url链接及USER_AGENT数据,生成一个形如:
https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285&X-Bogus=DFSzswSL2MtANHxFtG3DB09WcBjv
一个携带X-Bogus签名验证字段的请求链接。使用这个链接发送GET请求,就能得到aweme_detail 的json 数据了。不信大家可以试试。不过,这个链接是不能
在浏览器直接访问的,还必须加上cookie,refer等请求头数据,详情看下面的Tdouyin解析类。aweme_detail 数据如下:
(aweme_detail )json
5.关于高清无水印视频链接的获取方法
从"aweme_detail" json数据解析出视频的Uri项,带入高清视频接口:
https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200fg10000c86doo3c77uai4m711qg&ratio=1080p&line=0
执行重定向getRedirectedUrl()得到高清无水印链接:
6、区分视频与图文
返回的信息中:
如果aweme_detail.aweme_type=68,该信息为图文数据;
如果aweme_detail.aweme_type=0,该信息为视频数据;
详细步骤看以下TDouyin解析类,关键代码处都有注解:
五、源代码
1、解析类
unit uDouyin;
interface
uses
windows,classes,System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent,
System.SysUtils,strutils,uLog,System.RegularExpressions,uFuncs,system.JSON,uConfig,
uVideoInfo,uDownVideo;
const
wm_user=$0400;
wm_downfile=wm_user+100+1; //消息参数;
//USER_AGENT标识客户端的类型,这儿是电脑浏览器端。
USER_AGENT:string='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36';
//USER_AGENT标识客户端的类型,这儿是手机APP端。
USER_AGENT_PHONE:string='Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Mobile/15E148 Safari/604.1';
USER_AGENT_PHONE_2:string='TikTok 26.2.0 rv:262018 (iPhone; iOS 14.4.2; en_US) Cronet';
//无水印视频接口,跟以前一样。
DOUYIN_API_URL:string='https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=' ;
DOUYIN_API_URL_2:string='https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=%s&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333' ;
//高清无水印视频接口:
DOUYIN_API_URL_1080='https://aweme.snssdk.com/aweme/v1/play/?video_id=%s&ratio=1080p&line=0';
type
TDouyin=class(TThread) //支持多线程下载;
private
FId:cardinal; //线程标识;
Furl:string; //分享的链接;;
FRedirectedUrl:string; //重定向后的链接;
Fvideourl:string; //解析后得到的无水印视频链接;
FvideoId:string; //视频id 如:7065264218437717285
FvideoTitle:string; //视频标题
Fnickname:string; //作者昵称
FcoverUrl:string; //视频封面链接
Fmsg:string; //线程消息
Fsavedir:string; //保存视频文件及封面图片的目录
Furl_1080:string; //高清视频链接
Furi_1080:string; //高清视频uri参数 不懂的+v:metabycf
Fphotos:string;
class var Fcookie: string; //cookie参数 ,可从浏览器获取 静态类成员
class var Fform: HWND; //接收消息的窗体句柄 类成员
procedure SetId(id:cardinal); //设置线程id
procedure SetSaveDir(dir:string); //设置保存目录
class procedure SetForm(const hForm: HWND); static; //设置窗体句柄 静态方法
class procedure SetCookie(const cookie: string); static; //设置cookie 静态方法
protected
procedure Execute; override;
public
constructor Create(id:cardinal;url:string);
destructor Destroy;
property id:cardinal read FId write SetId; //id属性
property url:string read Furl; //分享链接 属性
property msg:string read Fmsg; //线程消息 属性
property videourl:string read Fvideourl; //无水印视频链接 属性
property videoTitle:string read FvideoTitle; //视频标题 属性
property nickname:string read Fnickname; //用户昵称
property RedirectedUrl:string read FRedirectedUrl; //重定向链接 属性
property videoId:string read FvideoId; //视频id 属性 如:7065264218437717285
property coverUrl:string read FcoverUrl; //封面链接 属性
property url_1080:string read Furl_1080; //高清视频链接 属性
property photos:string read Fphotos;
property savedir:string read Fsavedir write setSaveDir; //保存目录 属性
function getRedirectedUrl(url:string):string;overload; //获取重定向链接
function getRedirectedUrl(url,refer,user_agent:string):string;overload; //获取重定向链接
function getVideoId(txt:string):string; //解析出视频id 如:7065264218437717285
function getVideoUrl():string; //解析无水印视频地址,封面链接,视频标题 工作流程方法在这儿:
function parseJson(jo:string):string; //解析aweme_detail json数据
class property form: HWND read Fform write SetForm; //窗体句柄 类属性
class property cookie: string read Fcookie write SetCookie; //cookie 类属性
function getPostResult(data:string):string; //post 请求
function getRequestResult2(apiurl:string;Cookie:string):string; //GET 请求
function getBogusUrl(url:string):string; ////X-Bogus 算法 不明白的+v:metabycf
end;
implementation
//解析无水印视频地址,封面链接,视频标题 工作流程方法在这儿:
function TDouyin.getVideoUrl():string;
var
apiurl,apiurl2,jo:string;
i:integer;
video:TvideoInfo;
down:TdownVideo;
begin
result:='';
FcoverUrl:='';
FvideoUrl:='';
Fphotos:='';
try
//第一步:执行重定向,从而获取到视频id
//如:https://www.douyin.com/video/7065264218437717285
FRedirectedUrl:=getRedirectedUrl(Furl,Furl,USER_AGENT);
log('FRedirectedUrl='+FRedirectedUrl); //日志记录
if(FRedirectedUrl)='' then exit;
//第二步:分析出视频id,如:7065264218437717285
FvideoId:=getVideoId(FRedirectedUrl);
log('FvideoId='+FvideoId); //日志记录
if(FvideoId)='' then exit;
//apiurl:=DOUYIN_API_URL+FvideoId; //视频接口
apiurl:=format(DOUYIN_API_URL_2,[FvideoId]); //视频接口
//第三步:计算X-Bogus验证,加到视频接口上。得到新的请求链接 多了这一步骤。
//如:https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7065264218437717285&X-Bogus=DFSzswSL2MtANHxFtG3DB09WcBjv
//不明白的+v:metabycf
apiurl2:=getBogusUrl(apiurl); //具有X-Bogus验证的视频接口 多了这一步骤。
log(apiurl2); //日志记录
if(apiurl2='')then begin log('apiurl2=k');exit;end;
//第四步:发送GET请求,带上cookie,refer参数;到这一步,已经能拿到"aweme_detail" json数据了。
jo:=getRequestResult2(apiurl2,Fcookie);
Fmsg:=jo;
log(jo); //日志记录
if(pos('aweme_detail',jo)<=0)then begin
log('aweme_detail=k');
exit;
end;
if(pos('"aweme_detail":null',jo)>0)then exit;
//第五步: 解析 "aweme_detail" json数据
parseJson(jo);
//第六步: 1解析 图文
if(Fphotos<>'')then
begin
video:=TvideoInfo.Create(Fvideotitle,coverUrl,'',Fphotos);
down:=TdownVideo.Create(Fid,video,Fsavedir,false);
//down.form:=Fform;
//down.cookie:=Fcookie;
//if(DEBUG=true)then downvideo.process else
down.Start;
exit;
end;
//第六步: 2解析 高清视频地址
if(Furi_1080<>'')then //Furi_1080为视频 uri
begin
Furl_1080:=format(DOUYIN_API_URL_1080,[Furi_1080]);
log(Furl_1080); //日志记录
Furl_1080:=getRedirectedUrl(Furl_1080,FRedirectedUrl, USER_AGENT_PHONE); //重定向
log('Furl_1080='+Furl_1080); //日志记录
end;
//第七步: 启动下载线程,下载视频文件和封面图片。
if(Fvideotitle<>'')and(Furl_1080<>'')and(FcoverUrl<>'')then
begin
video:=TvideoInfo.Create(Fvideotitle,coverUrl,Furl_1080,'');
down:=TdownVideo.Create(Fid,video,Fsavedir,false);
//down.form:=Fform;
//down.cookie:=Fcookie;
down.Start;
end;
finally
//第八步: 发送解析完成消息。
Fmsg:='complete';
SendMessage(Fform,wm_downfile,2,integer(self));
end;
end;
//第四步:发送GET请求,带上cookie,refer参数;到这一步,已经能拿到"aweme_detail" json数据了。
function TDouyin.getRequestResult2(apiurl:string;Cookie:string):string;
var
client: TNetHTTPClient;
ss: TStringStream;
s,id:string;
AResponse:IHTTPResponse;
i:integer;
begin
try
client :=TNetHTTPClient.Create(nil);
SS :=TStringStream.Create('', TEncoding.UTF8);
ss.Clear;
with client do
begin
ConnectionTimeout :=10000; // 10秒
ResponseTimeout :=10000; // 10秒
AcceptCharSet :='utf-8';
UserAgent :=USER_AGENT; //1 USER_AGENT USER_AGENT_PHONE_2
client.AllowCookies:=true;
client.HandleRedirects:=true;
Accept:='application/json'; //'*/*'
client.ContentType:='application/json'; //2
client.AcceptLanguage:='zh-CN';
client.CustomHeaders['Cookie']:=cookie;
client.CustomHeaders['Referer']:=Furl;
try
AResponse:=Get(apiurl, ss);
result:=ss.DataString;
except
on E: Exception do
Log(e.Message);
end;
end;
finally
ss.Free;
client.Free;
end;
end;
//第五步: 解析 "aweme_detail" json数据 :分为视频和图文两类
function TDouyin.parseJson(jo:string):string;
var
json,jroot,jvideo,j1,j2: TJSONObject;
arr,arr1:TJSONARRAY;
uri,aweme_type,photo:string;
i:integer;
begin
result:='';
try
json :=TJSONObject.ParseJSONValue(jo) as TJSONObject;
if json=nil then exit;
jroot:=json.GetValue('aweme_detail') as TJSONObject;
FvideoTitle:=trim(jroot.GetValue('desc').Value);
aweme_type:=jroot.GetValue('aweme_type').Value;
if(aweme_type='68')then //图文
begin
arr:=jroot.GetValue('images') as TJSONARRAY;
for I :=0 to arr.Size-1 do
begin
j1:=arr.Get(i) as TJSONObject;
arr1:=j1.GetValue('url_list') as TJSONARRAY;
photo:=arr1.Items[0].Value;
Fphotos:=Fphotos+photo+#13#10;
end;
result:='#100#'+Fphotos+'#'+FcoverUrl+'#'+FvideoTitle;
exit;
end;
jvideo:=jroot.GetValue('video') as TJSONObject;
j1:=jvideo.GetValue('cover') as TJSONObject; //cover origin_cover
arr:=j1.GetValue('url_list') as TJSONARRAY;
FcoverUrl:=arr[0].Value;
j1:=jvideo.GetValue('play_addr') as TJSONObject;
arr:=j1.GetValue('url_list') as TJSONARRAY;
FvideoUrl:=arr[0].Value;
FvideoUrl:=stringreplace(FvideoUrl,'playwm','play',[rfReplaceAll]);
Furi_1080:=j1.GetValue('uri').Value;
result:='#100#'+FvideoUrl+'#'+FcoverUrl+'#'+FvideoTitle;
finally
if json <> nil then json.Free;
end;
end;
//第二步:分析出视频id,如:7065264218437717285
function TDouyin.getVideoId(txt:string):string;
var
m:TMatch;
i:integer;
begin
result:='';
m :=TRegEx.Match(txt,'/video/([^/?]+)/');
if(m.Groups[1].Success=false) or (length(m.Groups[1].Value)<>19)then exit;
result:=m.Groups[1].Value;
end;
//X-Bogus 算法 不明白的+v:metabycf
function TDouyin.getBogusUrl(url:string):string;
var
json:TJSONObject;
data:string;
begin
result:='';
try
json:=TJSONObject.Create;
json.AddPair('url',url);
json.AddPair('user_agent',USER_AGENT);
data:=json.ToString;
log(data);
data:=getPostResult(data);
log(data);
if(data='')then exit;
json:=TJSONObject.ParseJSONValue(data) as TJSONObject;
result:=json.GetValue('param').Value;
finally
json.Free;
end;
end;
//第一步:执行重定向,从而获取到视频id
function TDouyin.getRedirectedUrl(url,refer,user_agent:string):string;
var
client: TNetHTTPClient;
ss: TStringStream;
s,id:string;
AResponse:IHTTPResponse;
i:integer;
begin
try
client :=TNetHTTPClient.Create(nil);
SS :=TStringStream.Create('', TEncoding.UTF8);
ss.Clear;
with client do
begin
ConnectionTimeout :=2000; // 2秒
ResponseTimeout :=2000; // 10秒
AcceptCharSet :='utf-8';
UserAgent :=user_agent;
client.AllowCookies:=true;
client.HandleRedirects:=false;
Accept:='*/*';
client.CustomHeaders['Referer']:=refer;
try
AResponse:=Get(url, ss);
Log('getRedirectedUrl AResponse='+ss.DataString);
s:=AResponse.HeaderValue['Location'];
if(s='')then exit;
result:=s;
except
on E: Exception do
Log(e.Message);
end;
end;
finally
ss.Free;
client.Free;
end;
end;
constructor TDouyin.Create(id:cardinal;url:string);
begin
//inherited;
//FreeOnTerminate :=True;
inherited Create(True);
FId:=id;
Furl:=url; //分享链接
Furi_1080:=''; //视频uri
Fphotos:='';
end;
destructor TDouyin.Destroy;
begin
inherited Destroy;
end;
//工作线程
procedure TDouyin.Execute;
begin
try
getVideoUrl();
finally
end;
end;
//------------------------------------------属性方法-------------------------------------
procedure TDouyin.SetId(Id:cardinal);
begin
FId:=Id;
end;
class procedure TDouyin.SetForm(const hForm: HWND);
begin
Fform:=hForm;
end;
procedure TDouyin.SetSaveDir(dir:string);
begin
Fsavedir:=dir;
end;
class procedure TDouyin.SetCookie(const cookie: string);
begin
Fcookie:=cookie;
end;
end.
2、视频信息
unit uVideoInfo;
interface
type
TVideoInfo=class
private
Ftitle:string; //标题
FcoverUrl:string; //封面地址
FvideoUrl:string; //视频地址
Fphotos:string; //图片地址
procedure SetTitle(title:string);
public
property title:string read Ftitle write Settitle;
property coverUrl:string read FcoverUrl;
property videoUrl:string read FvideoUrl;
property photos:string read Fphotos;
constructor Create(title,coverUrl,videoUrl,photos:string);
end;
implementation
constructor TVideoInfo.Create(title,coverUrl,videoUrl,photos:string);
begin
Ftitle:=title;
FcoverUrl:=coverUrl;
FvideoUrl:=videoUrl;
Fphotos:=photos;
end;
procedure TVideoInfo.Settitle(title:string);
begin
Ftitle:=title;
end;
end.
3、下载
unit uDownVideo;
interface
uses
windows,classes,System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent,
System.SysUtils,strutils,uLog,System.RegularExpressions,uFuncs,system.JSON,uConfig,
uVideoInfo,WinInet,urlmon,shlobj,ioutils;
const
wm_user=$0400;
wm_downfile=wm_user+100+1;
USER_AGENT:string='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36';
type
TDownVideo=class(TThread)
private
FId:cardinal;
Fvideo:TvideoInfo;
Fsavedir:string;
FvideoFilename:string;
FcoverFilename:string;
FphotoFilename:string; //图片文件名
Fmsg:string;
FSerial:boolean; //是否在文件名前面加入序号;
Fsuccess:boolean;
Frefer:string;
class var Fcookie: string;
procedure SetId(id:cardinal);
procedure SetSavedir(dir:string);
procedure SetRefer(refer:string);
class var Fform: HWND;
class procedure SetForm(const hForm: HWND); static;
class procedure SetCookie(const cookie: string); static;
function DownloadFile(SourceFile, DestFile: string): Boolean;
protected
procedure Execute; override;
public
constructor Create(id:cardinal;video:TvideoInfo;savedir:string;bSerial:boolean);
destructor Destroy;
property id:cardinal read FId write SetId;
property savedir:string read Fsavedir write SetSavedir;
property video:TvideoInfo read Fvideo;
property msg:string read Fmsg;
property videoFilename:string read FvideoFilename;
property coverFilename:string read FcoverFilename;
property photoFilename:string read FphotoFilename;
property serial:boolean read Fserial;
property success:boolean read Fsuccess;
property refer:string read Frefer write SetRefer;
class property cookie: string read Fcookie write SetCookie;
class property form: HWND read Fform write SetForm;
function formatFilename(caption:string):string;
function formatDir(dir:string):string;
function GetValidName(s:string):string;
procedure downloadFileLog(SourceFile, DestFile: string);
procedure process();
end;
implementation
//bSerial为是否给文件名加上序号
constructor TDownVideo.Create(id:cardinal;video:TvideoInfo;savedir:string;bSerial:boolean);
begin
//inherited;
//FreeOnTerminate :=True;
inherited Create(True);
FId:=id;
Fvideo:=video;
Fsavedir:=formatdir(savedir);
Fmsg:='';
FvideoFilename:='';
FcoverFilename:='';
FphotoFilename:='';
Fsuccess:=false;
Fserial:=bSerial;
if(not directoryexists(Fsavedir))then
forcedirectories(Fsavedir);
end;
destructor TDownVideo.Destroy;
begin
inherited Destroy;
end;
//下载流程
procedure TDownVideo.process();
var
dir,title,photoUrl,photoname:string;
b:boolean;
photolist:tstrings;
i:integer;
begin
photolist:=nil;
try
title:=formatfilename(trim(video.title));
if(Fvideo.photos='')then //下载视频
begin
if(Fserial=true)then
begin
FcoverFilename:=Fsavedir+'\\'+inttostr(Fid)+'.'+title+'.webp';
FvideoFilename:=Fsavedir+'\\'+inttostr(Fid)+'.'+title+'.mp4';
end else begin
FcoverFilename:=Fsavedir+'\\'+title+'.webp';
FvideoFilename:=Fsavedir+'\\'+title+'.mp4';
end;
if(video.coverUrl<>'')then downloadFileLog(Fvideo.coverUrl,FcoverFilename);
if(video.videoUrl<>'')then downloadFileLog(Fvideo.videoUrl,FvideoFilename);
end else begin //下载图片
photolist:=tstringlist.Create;
photolist.Text:=video.photos;
if(Fserial=true)then
dir:=Fsavedir+'\\'+inttostr(Fid)+'.'+title
else
dir:=Fsavedir+'\\'+title;
forcedirectories(dir);
if(Fvideo.coverUrl<>'')then
begin
FcoverFilename:=dir+'\\0.封面 '+title+'.webp';
downloadFileLog(Fvideo.coverUrl,FcoverFilename);
end;
for I :=0 to photolist.Count-1 do
begin
photoUrl:=photolist[i];
if(trim(photoUrl)='')then continue;
photoname:=dir+'\\'+inttostr(i+1)+'.'+title+'.webp';
downloadFileLog(photoUrl,photoname);
FphotoFilename:=FphotoFilename+photoname+#13#10;
end;
end;
finally
Fmsg:='complete';
if(photolist<>nil)then photolist.Free;
SendMessage(Fform,wm_downfile,1,integer(self));
end;
end;
//线程中执行下载
procedure TDownVideo.Execute;
begin
process();
end;
function TDownVideo.GetValidName(s:string):string;
var
c:char;
txt:string;
begin
txt:=s;
for c in TPath.GetInvalidFileNameChars() do
begin
txt:=stringreplace(txt,c,'',[rfReplaceAll]);
end;
result:=txt;
end;
//去除文件名中的非法字符
function TDownVideo.formatFilename(caption:string):string;
var
s:string;
begin
s:=caption;
if(length(s)>72)then s:=leftstr(s,72);
result:=GetValidName(s);
end;
//去除路径中的非法字符
function TDownVideo.formatDir(dir:string):string;
var
caption:string;
begin
caption:=trim(extractfilename(dir));
if(length(caption)>72)then caption:=leftstr(caption,72);
caption:=GetValidName(caption);
result:=extractfilepath(dir)+caption;
end;
//下载文件
procedure TDownVideo.downloadFileLog(SourceFile, DestFile: string);
begin
if(fileexists(DestFile))then deletefile(DestFile);
if(Downloadfile(SourceFile,DestFile))then
begin
log('成功:'+DestFile);
Fsuccess:=true;
end else begin
log('失败:'+DestFile+' '+SourceFile);
Fsuccess:=false;
end;
end;
//下载文件
function TDownVideo.DownloadFile(SourceFile, DestFile: string): Boolean;
begin
try
DeleteUrlCacheEntry(pchar(SourceFile));
Result :=UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil)=0;
except
Result :=False;
end;
end;
//------------------------------------------属性方法-------------------------------------
procedure TDownVideo.SetSavedir(dir:string);
begin
Fsavedir:=dir;
end;
procedure TDownVideo.SetRefer(refer:string);
begin
Frefer:=refer;
end;
class procedure TDownVideo.SetCookie(const cookie: string);
begin
Fcookie:=cookie;
end;
procedure TDownVideo.SetId(Id:cardinal);
begin
FId:=Id;
end;
class procedure TDownVideo.SetForm(const hForm: HWND);
begin
Fform:=hForm;
end;
end.
至此,解析下载无水印视频及图文的程序代码就结束了,另外需要下载用户作品、评论等数据的+v。