From c87f6978c5c3efcc42580a6b2c604a6e1009a0ad Mon Sep 17 00:00:00 2001 From: Evil0ctal Date: Sun, 13 Nov 2022 23:04:06 -0800 Subject: [PATCH] Delete PyPi directory --- PyPi/LICENSE | 21 - PyPi/README.md | 502 ---------------- PyPi/dist/DT_Scraper-1.0.0.tar.gz | Bin 15323 -> 0 bytes PyPi/dist/DT_Scraper-1.0.1.tar.gz | Bin 15018 -> 0 bytes PyPi/pyproject.toml | 6 - PyPi/setup.py | 53 -- PyPi/src/DT_Scraper.egg-info/PKG-INFO | 27 - PyPi/src/DT_Scraper.egg-info/SOURCES.txt | 13 - .../DT_Scraper.egg-info/dependency_links.txt | 1 - PyPi/src/DT_Scraper.egg-info/requires.txt | 2 - PyPi/src/DT_Scraper.egg-info/top_level.txt | 1 - PyPi/src/DT_Scraper.egg-info/zip-safe | 1 - PyPi/src/DT_scraper/__init__.py | 0 PyPi/src/DT_scraper/requirements.txt | 2 - PyPi/src/DT_scraper/scraper.py | 552 ------------------ 15 files changed, 1181 deletions(-) delete mode 100644 PyPi/LICENSE delete mode 100644 PyPi/README.md delete mode 100644 PyPi/dist/DT_Scraper-1.0.0.tar.gz delete mode 100644 PyPi/dist/DT_Scraper-1.0.1.tar.gz delete mode 100644 PyPi/pyproject.toml delete mode 100644 PyPi/setup.py delete mode 100644 PyPi/src/DT_Scraper.egg-info/PKG-INFO delete mode 100644 PyPi/src/DT_Scraper.egg-info/SOURCES.txt delete mode 100644 PyPi/src/DT_Scraper.egg-info/dependency_links.txt delete mode 100644 PyPi/src/DT_Scraper.egg-info/requires.txt delete mode 100644 PyPi/src/DT_Scraper.egg-info/top_level.txt delete mode 100644 PyPi/src/DT_Scraper.egg-info/zip-safe delete mode 100644 PyPi/src/DT_scraper/__init__.py delete mode 100644 PyPi/src/DT_scraper/requirements.txt delete mode 100644 PyPi/src/DT_scraper/scraper.py diff --git a/PyPi/LICENSE b/PyPi/LICENSE deleted file mode 100644 index fd91e63..0000000 --- a/PyPi/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Evil0ctal - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/PyPi/README.md b/PyPi/README.md deleted file mode 100644 index dca615b..0000000 --- a/PyPi/README.md +++ /dev/null @@ -1,502 +0,0 @@ -

-
- -
- Douyin_TikTok_Download_API(抖音/TikTok无水印解析API) -
-

- -

- 运行说明 • - API使用 • - 手动部署 • - Docker部署 • - Docker镜像 • - 贡献者 -

- -
- -![](https://views.whatilearened.today/views/github/Evil0ctal/TikTokDownloader_PyWebIO.svg) -[![GitHub license](https://img.shields.io/github/license/Evil0ctal/TikTokDownloader_PyWebIO)](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/LICENSE) -[![GitHub issues](https://img.shields.io/github/issues/Evil0ctal/TikTokDownloader_PyWebIO)](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/issues) -[![GitHub forks](https://img.shields.io/github/forks/Evil0ctal/TikTokDownloader_PyWebIO)](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/network) -[![GitHub stars](https://img.shields.io/github/stars/Evil0ctal/TikTokDownloader_PyWebIO)](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/stargazers) -[![Docker Image size](https://img.shields.io/docker/image-size/evil0ctal/douyin_tiktok_download_api?style=flat-square)](https://hub.docker.com/repository/docker/evil0ctal/douyin_tiktok_download_api) - -Language: [[English](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/README.en.md)] [[简体中文](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/README.md)] [[繁体中文](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/README.zh-TW.md)] - -> Note: This API is applicable to Douyin and TikTok. Douyin is TikTok in China. You can distribute or modify the code at -> will, but please mark the original author. - -## 👻介绍 - -> 出于稳定性的考虑,暂时关闭演示站的/video(返回mp4文件)和/music(返回mp3文件) -> 这两个功能,同时结果页面的批量下载功能也暂时不可用,如有需求请自行部署,其他功能在演示站上仍正常使用,API服务器保证99%的时间正常运行,但不保证解析100%成功,如果解析失败请等一两分钟后重试。 - -🚀演示地址:[https://douyin.wtf/](https://douyin.wtf/) - -🛰API演示:[https://api.douyin.wtf/](https://api.douyin.wtf/) - -💾iOS快捷指令(中文): [点击获取](https://www.icloud.com/shortcuts/331073aca78345cf9ab4f73b6a457f97) ( -更新于2022/07/18,快捷指令可自动检查更新,安装一次即可。) - -🌎iOS Shortcut(English): [Click to get](https://www.icloud.com/shortcuts/83548306bc0c4f8ea563108f79c73f8d) (Updated on -2022/07/18, this shortcut will automatically check for updates, only need to install it once.) - -🗂快捷指令历史版本: [Shortcuts release](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/issues/53) - -📦️Tiktok/抖音下载器(桌面应用):[TikDown](https://github.com/Tairraos/TikDown/) - -本项目使用 [PyWebIO](https://github.com/pywebio/PyWebIO)、[Flask](https://github.com/pallets/flask) -,利用Python实现在线批量解析抖音的无水印视频/图集。 - -可用于下载作者禁止下载的视频,或者进行数据爬取等等,同时可搭配[iOS自带的快捷指令APP](https://apps.apple.com/cn/app/%E5%BF%AB%E6%8D%B7%E6%8C%87%E4%BB%A4/id915249334) -配合本项目API实现应用内下载。 - -快捷指令需要在抖音或TikTok的APP内,选择你想要保存的视频,点击分享按钮,然后找到 "抖音TikTok无水印下载" -这个选项,如遇到通知询问是否允许快捷指令访问xxxx (域名或服务器),需要点击允许才可以正常使用,下载成功的视频或图集会保存在一个专门的相册中以方便浏览。 - -## 💡项目文件结构 - -``` -# 请根据需要自行修改config.ini中的内容 -. -└── Douyin_TikTok_Download_API/ - ├── /static(静态前端资源) - ├── web_zh.py(网页入口) - ├── web_api.py(API) - ├── scraper.py(解析库) - ├── config.ini(所有项目的配置文件,包含端口及代理等,如需请自行修改该文件。) - ├── logs.txt(错误日志,自动生成。) - └── API_logs.txt(API调用日志,自动生成。) -``` - -## 💯已支持功能: - -- 支持抖音视频/图集解析 -- 支持海外TikTok视频解析 -- 支持批量解析(支持抖音/TikTok混合解析) -- 解析结果页批量下载无水印视频 -- 制作[pip包](https://pypi.org/project/DT-Scraper/)方便使用 -- 支持API调用 -- 支持使用代理解析 -- 支持[iOS快捷指令](https://apps.apple.com/cn/app/%E5%BF%AB%E6%8D%B7%E6%8C%87%E4%BB%A4/id915249334)实现应用内下载无水印视频/图集 - ---- - -## 🤦‍后续功能: - -- [ ] 支持输入(抖音/TikTok)作者主页链接实现批量解析 - ---- - -## 🧭运行说明(经过测试过的Python版本为3.8): -> 🚨如果你要部署本项目,请参考部署方式([Docker部署](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/README.md#%E9%83%A8%E7%BD%B2%E6%96%B9%E5%BC%8F%E4%BA%8C-docker "Docker部署"), [手动部署](https://github.com/Evil0ctal/Douyin_TikTok_Download_API/blob/main/README.md#%E9%83%A8%E7%BD%B2%E6%96%B9%E5%BC%8F%E4%B8%80-%E6%89%8B%E5%8A%A8%E9%83%A8%E7%BD%B2 "手动部署")) - -- 克隆本仓库: - -```console -git clone https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git -``` - -- 移动至仓库目录: - -```console -cd Douyin_TikTok_Download_API -``` - -- 安装依赖库: - -```console -pip install -r requirements.txt -``` - -- 修改config.ini(可选): - -```console -vim config.ini -``` - -- 网页解析 - -```console -# 运行web_zh.py -python3 web_zh.py -``` - -- API - -```console -# 运行web_api.py -python3 web_api.py -``` - -- 调用解析库 - -```python -# pip install DT-Scraper -from DT_scraper.scraper import Scraper - -api = Scraper() - -# 解析Douyin视频/图集 -douyin_data = api.douyin(input('抖音视频链接:')) -# 返回字典 -print(douyin_data) - -# Parsing TikTok Videos/Galleries -tiktok_data = api.tiktok(input('TikTok video URL:')) -# return dictionary -print(tiktok_data) - -# 使用代理进行解析(Parse using a proxy) -api.tiktok(input('TikTok video URL:'), proxies = {"all": "127.0.0.1:2333"}) - -``` - -- 入口(端口可在config.ini文件中修改) - -```text -网页入口: -http://localhost(服务器IP):5000/ -API入口: -http://localhost(服务器IP):2333/ -``` - -## 🗺️支持的提交格式(包含但不仅限于以下例子): - -- 抖音分享口令 (APP内复制) - -```text -例子:7.43 pda:/ 让你在几秒钟之内记住我 https://v.douyin.com/L5pbfdP/ 复制此链接,打开Dou音搜索,直接观看视频! -``` - -- 抖音短网址 (APP内复制) - -```text -例子:https://v.douyin.com/L4FJNR3/ -``` - -- 抖音正常网址 (网页版复制) - -```text -例子: -https://www.douyin.com/video/6914948781100338440 -``` - -- 抖音发现页网址 (APP复制) - -```text -例子: -https://www.douyin.com/discover?modal_id=7069543727328398622 -``` - -- TikTok短网址 (APP内复制) - -```text -例子: -https://vm.tiktok.com/TTPdkQvKjP/ -``` - -- TikTok正常网址 (网页版复制) - -```text -例子: -https://www.tiktok.com/@tvamii/video/7045537727743380782 -``` - -- 抖音/TikTok批量网址(无需使用符合隔开) - -```text -例子: -2.84 nqe:/ 骑白马的也可以是公主%%百万转场变身 https://v.douyin.com/L4FJNR3/ 复制此链接,打开Dou音搜索,直接观看视频! -8.94 mDu:/ 让你在几秒钟之内记住我 https://v.douyin.com/L4NpDJ6/ 复制此链接,打开Dou音搜索,直接观看视频! -9.94 LWz:/ ok我坦白交代 %%knowknow https://v.douyin.com/L4NEvNn/ 复制此链接,打开Dou音搜索,直接观看视频! -https://www.tiktok.com/@gamer/video/7054061777033628934 -https://www.tiktok.com/@off.anime_rei/video/7059609659690339586 -https://www.tiktok.com/@tvamii/video/7045537727743380782 -``` - -## 🛰️API使用 - -API可将请求参数转换为需要提取的无水印视频/图片直链,配合IOS捷径可实现应用内下载。 - -- 解析请求参数 - -```text -http://localhost(服务器IP):2333/api?url="复制的(抖音/TikTok)口令/链接" -``` - -- 返回参数 - -> 抖音视频 - -```json -{ - "analyze_time": "1.9043s", - "api_url": "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=6918273131559881997", - "nwm_video_url": "http://v3-dy-o.zjcdn.com/23f0dec312ede563bef881af9a88bdc7/624dd965/video/tos/cn/tos-cn-ve-15/eccedcf4386948f5b5a1f0bcfb3dcde9/?a=1128&br=2537&bt=2537&cd=0%7C0%7C0%7C0&ch=0&cr=0&cs=0&cv=1&dr=0&ds=3&er=&ft=sYGC~3E7nz7Th1PZSDXq&l=202204070118030102080650132A21E31F&lr=&mime_type=video_mp4&net=0&pl=0&qs=0&rc=M3hleDRsODlkMzMzaGkzM0ApODpmNWc4ODs5N2lmNzg5aWcpaGRqbGRoaGRmLi4ybnBrbjYuYC0tYy0wc3MtYmJjNTM2NjAtNDFjMzJgOmNwb2wrbStqdDo%3D&vl=&vr=", - "original_url": "https://v.douyin.com/L4FJNR3/", - "platform": "douyin", - "status": "success", - "url_type": "video", - "video_author": "Real机智张", - "video_author_id": "Rea1yaoyue", - "video_author_signature": "", - "video_author_uid": "59840491348", - "video_aweme_id": "6918273131559881997", - "video_comment_count": "89145", - "video_create_time": "1610786002", - "video_digg_count": "2968195", - "video_hashtags": [ - "百万转场变身" - ], - "video_music": "https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/6910889805266504461.mp3", - "video_music_author": "梅尼耶", - "video_music_id": "6910889820861451000", - "video_music_mid": "6910889820861451021", - "video_music_title": "@梅尼耶创作的原声", - "video_play_count": "0", - "video_share_count": "74857", - "video_title": "骑白马的也可以是公主#百万转场变身", - "wm_video_url": "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300ffe0000c01a96q5nis1qu5b1u10&ratio=720p&line=0" -} -``` - -> 抖音图集 - -```json -{ - "album_author": "治愈图集", - "album_author_id": "ZYTJ2002", - "album_author_signature": "取无水印图", - "album_author_uid": "449018054867063", - "album_aweme_id": "7015137063141920030", - "album_comment_count": "5436", - "album_create_time": "1633338878", - "album_digg_count": "193734", - "album_hashtags": [ - "晚霞", - "治愈系", - "落日余晖", - "日落🌄" - ], - "album_list": [ - "https://p26-sign.douyinpic.com/tos-cn-i-0813/5223757a7bef4f8480cd25d0fa2d2d94~noop.webp?x-expires=1651856400&x-signature=K1VjJdWTHCAaYSz14y6NumjjtfI%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&biz_tag=aweme_images&l=202204070120460102101050412A210A47", - "https://p26-sign.douyinpic.com/tos-cn-i-0813/d99467672da840908acccf2d2b4b7ef7~noop.webp?x-expires=1651856400&x-signature=ncBb8Tt7z4PmpUyiCNr%2FJYnwRSA%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&biz_tag=aweme_images&l=202204070120460102101050412A210A47", - "https://p26-sign.douyinpic.com/tos-cn-i-0813/5c2562210b1a4d4c99d6d4dbd2f23f2b~noop.webp?x-expires=1651856400&x-signature=Rsmplb53IKfvKd3mmIb4iQNhlIE%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&biz_tag=aweme_images&l=202204070120460102101050412A210A47", - "https://p26-sign.douyinpic.com/tos-cn-i-0813/9bb74c0c6aff4217bd1491a077b2c817~noop.webp?x-expires=1651856400&x-signature=BLRyHoKP0ybIci57yneOca62dxI%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&biz_tag=aweme_images&l=202204070120460102101050412A210A47" - ], - "album_music": "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/6978805801733442341.mp3", - "album_music_author": "魏同学", - "album_music_id": "6978805810365271000", - "album_music_mid": "6978805810365270791", - "album_music_title": "@魏同学创作的原声", - "album_play_count": "0", - "album_share_count": "30717", - "album_title": "“山海自有归期 风雨自有相逢 意难平终将和解 万事终将如意”#晚霞 #治愈系 #落日余晖 #日落🌄", - "analyze_time": "1.0726s", - "api_url": "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=7015137063141920030", - "original_url": "https://v.douyin.com/Nb8jysN/", - "platform": "douyin", - "status": "success", - "url_type": "album" -} -``` - -> TikTok视频 - -```JSON -{ - "analyze_time": "5.0863s", - "nwm_video_url": "https://v19.tiktokcdn-us.com/cfa357dadd8f913f013a6d0b0dca293f/624e20fa/video/tos/useast5/tos-useast5-ve-0068c003-tx/3296231486014755a1b81aa70c349a53/?a=1233&br=6498&bt=3249&cd=0%7C0%7C0%7C3&ch=0&cr=3&cs=0&cv=1&dr=0&ds=6&er=&ft=bY1KJnB4TJBS6BMy-L1iVKP&l=20220406172333010113135214232FAB56&lr=all&mime_type=video_mp4&net=0&pl=0&qs=0&rc=MzpsaGY6Zjo7PDMzZzczNEApNjY6ZTtkOzxpN2Q3PDo5OmdgZ2BtcjQwai9gLS1kMS9zczJhLTEzYjEuMTJeXzQyLmM6Yw%3D%3D&vl=&vr=", - "original_url": "https://www.tiktok.com/@oregonzoo/video/7080938094823738666", - "platform": "tiktok", - "status": "success", - "url_type": "video", - "video_author": "oregonzoo", - "video_author_SecId": "MS4wLjABAAAArWNQ8-AZN6CxWOkqdeWsMBUuLDmJt8TWUAk0S4aWDW5V5EoqRbuczhaLnxJHCGob", - "video_author_diggCount": 94, - "video_author_followerCount": 1800000, - "video_author_followingCount": 39, - "video_author_heartCount": 29700000, - "video_author_id": "6699816060206171141", - "video_author_nickname": "Oregon Zoo", - "video_author_videoCount": 264, - "video_aweme_id": "7080938094823738666", - "video_comment_count": 61, - "video_create_time": "1648659375", - "video_digg_count": 11800, - "video_hashtags": [ - "redpanda", - "boop", - "sunshine" - ], - "video_music": "https://sf16.tiktokcdn-us.com/obj/ies-music-tx/7075363935741856558.mp3", - "video_music_author": "Gilderoy Dauterive", - "video_music_id": "7075363884613356330", - "video_music_title": "Be the Sunshine", - "video_music_url": "https://sf16.tiktokcdn-us.com/obj/ies-music-tx/7075363935741856558.mp3", - "video_play_count": 60100, - "video_ratio": "720p", - "video_share_count": 298, - "video_title": "Moshu ✨ #redpanda #boop #sunshine", - "wm_video_url": "https://v16m-webapp.tiktokcdn-us.com/0394b9183a5852d4392a7e804bf78c55/624e20f6/video/tos/useast5/tos-useast5-ve-0068c001-tx/fc63ae232e70466398b55ccf97eb3c67/?a=1988&br=6468&bt=3234&cd=0%7C0%7C1%7C0&ch=0&cr=0&cs=0&cv=1&dr=0&ds=3&er=&ft=XY53A3E7nz7Th-pZSDXq&l=202204061723290101131351171341B9BB&lr=tiktok_m&mime_type=video_mp4&net=0&pl=0&qs=0&rc=MzpsaGY6Zjo7PDMzZzczNEApOjo4aDMzZmRlN2loOWk6ZWdgZ2BtcjQwai9gLS1kMS9zczBhNGA0LTIwNjNiYDQ2YmE6Yw%3D%3D&vl=&vr=" -} -``` - -- 下载视频请求参数 - -```text -http://localhost(服务器IP):2333/video?url="复制的(抖音/TikTok)口令/链接" -# 返回无水印mp4文件 -``` - -- 下载音频请求参数 - -```text -http://localhost(服务器IP):2333/music?url="复制的(抖音/TikTok)口令/链接" -# 返回mp3文件 -``` - ---- - -## 💾部署(方式一 手动部署) - -> 注: -> 截图可能因更新问题与文字不符,一切请优先参照文字叙述。 - -> 最好将本项目部署至海外服务器(优先选择美国地区的服务器),否则可能会出现奇怪的问题。 - -例子: -项目部署在国内服务器,而人在美国,点击结果页面链接报错403 ,目测与抖音CDN有关系。 -项目部署在韩国服务器,解析TikTok报错 ,目测TikTok对某些地区或IP进行了限制。 - -> 使用宝塔Linux面板进行部署( -> 中文宝塔要强制绑定手机号了,很流氓且无法绕过,建议使用宝塔国际版,谷歌搜索关键字aapanel自行安装,部署步骤相似。) - -- 首先要去安全组开放5000和2333端口(Web默认5000,API默认2333,可以在文件config.ini中修改。) -- 在宝塔应用商店内搜索python并安装项目管理器 (推荐使用1.9版本) - -![](https://raw.githubusercontent.com/Evil0ctal/TikTokDownloader_PyWebIO/main/Screenshots/BT_Linux_Panel_Deploy_1.png) - ---- - -- 创建一个项目名字随意 -- 路径选择你上传文件的路径 -- Python版本需要至少3以上(在左侧版本管理中自行安装) -- 框架修改为`Flask` -- 启动方式修改为`python` -- Web启动文件选择`web_zh.py` -- API启动文件选择`web_api.py` -- 勾选安装模块依赖 -- 开机启动随意 -- 如果宝塔运行了`Nginx`等其他服务时请自行判断端口是否被占用,运行端口可在文件config.ini中修改。 - -![](https://raw.githubusercontent.com/Evil0ctal/TikTokDownloader_PyWebIO/main/Screenshots/BT_Linux_Panel_Deploy_2.png) - -- 如果有大量请求请使用进程守护启动防止进程关闭 - ---- - -## 💾部署(方式二 Docker) - -- 安装docker - -```yaml -curl -fsSL get.docker.com -o get-docker.sh&&sh get-docker.sh &&systemctl enable docker&&systemctl start docker -``` - -- 留下config.int和docker-compose.yml文件即可 -- 运行命令,让容器在后台运行 - -```yaml -docker compose up -d -``` - -- 查看容器日志 - -```yaml -docker logs -f douyin_tiktok_download_api -``` - -- 删除容器 - -```yaml -docker rm -f douyin_tiktok_download_api -``` - -- 更新 - -```yaml -docker compose pull && docker compose down && docker compose up -d -``` - -## ❤️ 贡献者 - -[![](https://github.com/Evil0ctal.png?size=50)](https://github.com/Evil0ctal) -[![](https://github.com/jw-star.png?size=50)](https://github.com/jw-star) -[![](https://github.com/Jeffrey-deng.png?size=50)](https://github.com/Jeffrey-deng) -[![](https://github.com/chris-ss.png?size=50)](https://github.com/chris-ss) -[![](https://github.com/weixuan00.png?size=50)](https://github.com/weixuan00) -[![](https://github.com/Tairraos.png?size=50)](https://github.com/Tairraos) - -## 🎉截图 - -> 注: -> 截图可能因更新问题与文字不符,一切请优先参照文字叙述。 - -
点击展开截图 - -
- -- 主界面 - -![](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/Screenshots/home.png) - ---- - -- 解析完成 - -> 单个 - -![](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/Screenshots/single_result.png) - ---- - -> 批量 - -![](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/Screenshots/multi_results.png) - ---- - -- API提交/返回 - -> 视频返回值 - -![](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/Screenshots/api_video_result.png) - -> 图集返回值 - -![](https://github.com/Evil0ctal/TikTokDownloader_PyWebIO/blob/main/Screenshots/api_image_result.png) - -> TikTok返回值 - -![](https://raw.githubusercontent.com/Evil0ctal/TikTokDownloader_PyWebIO/main/Screenshots/tiktok_API.png) - ---- - -
- -## :alembic: 技术栈 - -* [PyWebIO](https://www.pyweb.io/) + [Flask](https://flask.palletsprojects.com/) - -## :scroll: 许可证 - -MIT License - ---- -> GitHub [@Evil0ctal](https://github.com/Evil0ctal)  ·  -> Email Evil0ctal1985@gmail.com diff --git a/PyPi/dist/DT_Scraper-1.0.0.tar.gz b/PyPi/dist/DT_Scraper-1.0.0.tar.gz deleted file mode 100644 index 15e74b512ac7d3e2ef4d983fc2a2ff79d3b19331..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15323 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 APyhe` diff --git a/PyPi/pyproject.toml b/PyPi/pyproject.toml deleted file mode 100644 index 0ad39d0..0000000 --- a/PyPi/pyproject.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build-system] -requires = [ - "setuptools>=42", - "wheel" -] -build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/PyPi/setup.py b/PyPi/setup.py deleted file mode 100644 index 8307f36..0000000 --- a/PyPi/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- -# RUN Command Line: -# python3 setup.py sdist (Build-check dist folder) -# python3 -m twine upload --repository pypi dist/* (Upload to PyPi) - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup -import setuptools - -setup( - name='DT_Scraper', # 包的名字 - author='Evil0ctal', # 作者 - version='1.0.1', # 版本号 - license='MIT', - - description='Douyin/TikTok crawler and no watermark video download.', # 描述 - long_description='''Douyin/TikTok crawler and no watermark video download.''', - author_email='evil0ctal1985@gmail.com', # 你的邮箱** - url='https://github.com/Evil0ctal/Douyin_TikTok_Download_API', # 可以写github上的地址,或者其他地址 - # 包内需要引用的文件夹 - # packages=setuptools.find_packages(exclude=['url2io',]), - packages=["src/DT_scraper"], - # keywords='NLP,tokenizing,Chinese word segementation', - # package_dir={'jieba':'jieba'}, - # package_data={'jieba':['*.*','finalseg/*','analyse/*','posseg/*']}, - - # 依赖包 - install_requires=[ - 'requests', - "tenacity", - ], - classifiers=[ - # 'Development Status :: 4 - Beta', - # 'Operating System :: Microsoft' # 你的操作系统 OS Independent Microsoft - 'Intended Audience :: Developers', - # 'License :: OSI Approved :: MIT License', - # 'License :: OSI Approved :: BSD License', # BSD认证 - 'Programming Language :: Python', # 支持的语言 - 'Programming Language :: Python :: 3', # python版本 。。。 - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Topic :: Software Development :: Libraries' - ], - zip_safe=True, -) diff --git a/PyPi/src/DT_Scraper.egg-info/PKG-INFO b/PyPi/src/DT_Scraper.egg-info/PKG-INFO deleted file mode 100644 index d29f4ce..0000000 --- a/PyPi/src/DT_Scraper.egg-info/PKG-INFO +++ /dev/null @@ -1,27 +0,0 @@ -Metadata-Version: 2.1 -Name: DT-Scraper -Version: 1.0.0 -Summary: Douyin/TikTok crawler and no watermark video download. -Home-page: https://github.com/Evil0ctal/Douyin_TikTok_Download_API -Author: Evil0ctal -Author-email: evil0ctal1985@gmail.com -License: MIT -Project-URL: Bug Tracker, https://github.com/Evil0ctal/Douyin_TikTok_Download_API/issues -Platform: UNKNOWN -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Topic :: Software Development :: Libraries -Requires-Python: >=3.6 -Description-Content-Type: text/markdown -License-File: LICENSE - -Douyin/TikTok crawler and no watermark video download. - diff --git a/PyPi/src/DT_Scraper.egg-info/SOURCES.txt b/PyPi/src/DT_Scraper.egg-info/SOURCES.txt deleted file mode 100644 index 4d821d6..0000000 --- a/PyPi/src/DT_Scraper.egg-info/SOURCES.txt +++ /dev/null @@ -1,13 +0,0 @@ -LICENSE -README.md -pyproject.toml -setup.cfg -setup.py -src/DT_Scraper.egg-info/PKG-INFO -src/DT_Scraper.egg-info/SOURCES.txt -src/DT_Scraper.egg-info/dependency_links.txt -src/DT_Scraper.egg-info/requires.txt -src/DT_Scraper.egg-info/top_level.txt -src/DT_Scraper.egg-info/zip-safe -src/DT_scraper/__init__.py -src/DT_scraper/scraper.py \ No newline at end of file diff --git a/PyPi/src/DT_Scraper.egg-info/dependency_links.txt b/PyPi/src/DT_Scraper.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/PyPi/src/DT_Scraper.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/PyPi/src/DT_Scraper.egg-info/requires.txt b/PyPi/src/DT_Scraper.egg-info/requires.txt deleted file mode 100644 index 365ee77..0000000 --- a/PyPi/src/DT_Scraper.egg-info/requires.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests -tenacity diff --git a/PyPi/src/DT_Scraper.egg-info/top_level.txt b/PyPi/src/DT_Scraper.egg-info/top_level.txt deleted file mode 100644 index 742d7f0..0000000 --- a/PyPi/src/DT_Scraper.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -DT_scraper diff --git a/PyPi/src/DT_Scraper.egg-info/zip-safe b/PyPi/src/DT_Scraper.egg-info/zip-safe deleted file mode 100644 index d3f5a12..0000000 --- a/PyPi/src/DT_Scraper.egg-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/PyPi/src/DT_scraper/__init__.py b/PyPi/src/DT_scraper/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/PyPi/src/DT_scraper/requirements.txt b/PyPi/src/DT_scraper/requirements.txt deleted file mode 100644 index 60444cf..0000000 --- a/PyPi/src/DT_scraper/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests==2.28.0 -tenacity==8.0.1 \ No newline at end of file diff --git a/PyPi/src/DT_scraper/scraper.py b/PyPi/src/DT_scraper/scraper.py deleted file mode 100644 index 64ea7ba..0000000 --- a/PyPi/src/DT_scraper/scraper.py +++ /dev/null @@ -1,552 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- -# @Author: https://github.com/Evil0ctal/ -# @Time: 2021/11/06 -# @Update: 2022/09/04 -# @Function: -# 核心代码,估值1块(๑•̀ㅂ•́)و✧ -# 用于爬取Douyin/TikTok数据并以字典形式返回。 -# input link, output dictionary. - - -import re -import json -import requests -from tenacity import * - - -class Scraper: - """ - Scraper.douyin(link): - 输入参数为抖音视频/图集链接,完成解析后返回字典。 - - Scraper.tiktok(link): - 输入参数为TikTok视频/图集链接,完成解析后返回字典。 - """ - - def __init__(self): - self.headers = { - 'user-agent': 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36 Edg/87.0.664.66' - } - self.tiktok_headers = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - "authority": "www.tiktok.com", - "Accept-Encoding": "gzip, deflate", - "Connection": "keep-alive", - "Host": "www.tiktok.com", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) coc_coc_browser/86.0.170 Chrome/80.0.3987.170 Safari/537.36", - } - - @retry(stop=stop_after_attempt(3), wait=wait_random(min=1, max=2)) - def douyin(self, original_url: str, proxies: dict = None): - """ - 利用官方接口解析抖音链接信息 - :param proxies: pip install DT-Scraper, Default not use proxy. - :param original_url: 抖音/TikTok链接(支持长/短链接) TikTok&Douyin URL - :return:包含信息的字典 Dictionary data - """ - headers = self.headers - try: - # 开始时间 - start = time.time() - # 判断是否为个人主页链接 - if 'user' in original_url: - return {'status': 'failed', 'reason': '暂不支持个人主页批量解析', 'function': 'Scraper.douyin()', - 'value': original_url} - else: - # 原视频链接 - r = requests.get(url=original_url, headers=headers, allow_redirects=False, proxies=proxies) - try: - # 2021/12/11 发现抖音做了限制,会自动重定向网址,但是可以从回执头中获取 - long_url = r.headers['Location'] - # 判断是否为个人主页链接 - if 'user' in long_url: - return {'status': 'failed', 'reason': '暂不支持个人主页批量解析', - 'function': 'Scraper.douyin()', - 'value': original_url} - except: - # 报错后判断为长链接,直接截取视频id - long_url = original_url - # 正则匹配出视频ID - try: - # 第一种链接类型 - # https://www.douyin.com/video/7086770907674348841 - key = re.findall('/video/(\d+)?', long_url)[0] - print('视频ID为: {}'.format(key)) - except Exception: - # 第二种链接类型 - # https://www.douyin.com/discover?modal_id=7086770907674348841 - key = re.findall('modal_id=(\d+)', long_url)[0] - print('视频ID为: {}'.format(key)) - # 构造抖音API链接 - api_url = f'https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids={key}' - print("正在请求抖音API链接: " + '\n' + api_url) - # 将回执以JSON格式处理 - js = json.loads(requests.get(url=api_url, headers=headers, proxies=proxies).text) - aweme_id = str(js['item_list'][0]['aweme_id']) - share_url = re.sub("/\\?.*", "", js['item_list'][0]['share_url']) - if share_url is None: - share_url = ( - "https://www.iesdouyin.com/share/video/" + aweme_id) if aweme_id is not None else original_url; - try: - music_share_url = "https://www.iesdouyin.com/share/music/" + str(js['item_list'][0]['music']['mid']) - except: - music_share_url = None - # 判断是否为图集 - if js['item_list'][0]['images'] is not None: - print("类型 = 图集") - # 类型为图集 - url_type = 'album' - # 图集标题 - album_title = str(js['item_list'][0]['desc']) - # 图集作者昵称 - album_author = str(js['item_list'][0]['author']['nickname']) - # 图集作者签名 - album_author_signature = str(js['item_list'][0]['author']['signature']) - # 图集作者UID - album_author_uid = str(js['item_list'][0]['author']['uid']) - # 图集作者抖音号 - album_author_id = str(js['item_list'][0]['author']['unique_id']) - if album_author_id == "": - # 如果作者未修改过抖音号,应使用此值以避免无法获取其抖音ID - album_author_id = str(js['item_list'][0]['author']['short_id']) - # 尝试获取图集BGM信息 - for key in js['item_list'][0]: - if key == 'music': - # 图集BGM链接 - album_music = str(js['item_list'][0]['music']['play_url']['url_list'][0] if len( - js['item_list'][0]['music']['play_url']['url_list']) > 0 else 'No BGM found') - # 图集BGM标题 - album_music_title = str(js['item_list'][0]['music']['title']) - # 图集BGM作者 - album_music_author = str(js['item_list'][0]['music']['author']) - # 图集BGM ID - album_music_id = str(js['item_list'][0]['music']['id']) - # 图集BGM MID - album_music_mid = str(js['item_list'][0]['music']['mid']) - break; - else: - # 图集BGM链接 - album_music = album_music_title = album_music_author = album_music_id = album_music_mid = 'No BGM found ' - # 图集ID - album_aweme_id = str(js['item_list'][0]['statistics']['aweme_id']) - # 评论数量 - album_comment_count = str(js['item_list'][0]['statistics']['comment_count']) - # 获赞数量 - album_digg_count = str(js['item_list'][0]['statistics']['digg_count']) - # 播放次数 - album_play_count = str(js['item_list'][0]['statistics']['play_count']) - # 分享次数 - album_share_count = str(js['item_list'][0]['statistics']['share_count']) - # 上传时间戳 - album_create_time = str(js['item_list'][0]['create_time']) - # 将话题保存在列表中 - album_hashtags = [] - for tag in js['item_list'][0]['text_extra']: - album_hashtags.append(tag['hashtag_name']) - # 将无水印图片链接保存在列表中 - images_list = [] - for data in js['item_list'][0]['images']: - images_list.append(data['url_list'][0]) - # 结束时间 - end = time.time() - # 解析时间 - analyze_time = format((end - start), '.4f') - # 将信息储存在字典中 - album_data = {'status': 'success', - 'analyze_time': (analyze_time + 's'), - 'url_type': url_type, - 'platform': 'douyin', - 'original_url': original_url, - 'share_url': share_url, - 'music_share_url': music_share_url, - 'api_url': api_url, - 'album_aweme_id': album_aweme_id, - 'album_title': album_title, - 'album_author': album_author, - 'album_author_signature': album_author_signature, - 'album_author_uid': album_author_uid, - 'album_author_id': album_author_id, - 'album_music': album_music, - 'album_music_title': album_music_title, - 'album_music_author': album_music_author, - 'album_music_id': album_music_id, - 'album_music_mid': album_music_mid, - 'album_comment_count': album_comment_count, - 'album_digg_count': album_digg_count, - 'album_play_count': album_play_count, - 'album_share_count': album_share_count, - 'album_create_time': album_create_time, - 'album_list': images_list, - 'album_hashtags': album_hashtags} - return album_data - else: - print("类型 = 视频") - # 类型为视频 - url_type = 'video' - # 视频标题 - video_title = str(js['item_list'][0]['desc']) - # 视频作者昵称 - video_author = str(js['item_list'][0]['author']['nickname']) - # 视频作者抖音号 - video_author_id = str(js['item_list'][0]['author']['unique_id']) - if video_author_id == "": - # 如果作者未修改过抖音号,应使用此值以避免无法获取其抖音ID - video_author_id = str(js['item_list'][0]['author']['short_id']) - # vid - vid = str(js['item_list'][0]['video']['vid']) - # 无水印1080p视频链接 - try: - r = requests.get( - "https://aweme.snssdk.com/aweme/v1/play/?video_id={}&radio=1080p&line=0".format(vid), - headers=headers, allow_redirects=False, proxies=proxies) - nwm_video_url_1080p = r.headers['Location'] - except: - nwm_video_url_1080p = "None" - # 有水印视频链接 - wm_video_url = str(js['item_list'][0]['video']['play_addr']['url_list'][0]) - # 无水印视频链接 (在回执JSON中将关键字'playwm'替换为'play'即可获得无水印地址) - nwm_video_url = str(js['item_list'][0]['video']['play_addr']['url_list'][0]).replace('playwm', - 'play') - # 去水印后视频链接(2022年1月1日抖音APi获取到的URL会进行跳转,需要在Location中获取直链) - r = requests.get(url=nwm_video_url, headers=headers, allow_redirects=False, proxies=proxies) - video_url = r.headers['Location'] - # 视频作者签名 - video_author_signature = str(js['item_list'][0]['author']['signature']) - # 视频作者UID - video_author_uid = str(js['item_list'][0]['author']['uid']) - # 尝试获取视频背景音乐 - for key in js['item_list'][0]: - if key == 'music': - if len(js['item_list'][0]['music']['play_url']['url_list']) != 0: - # 视频BGM链接 - video_music = str(js['item_list'][0]['music']['play_url']['url_list'][0]) - else: - video_music = 'No BGM found' - # 视频BGM标题 - video_music_title = str(js['item_list'][0]['music']['title']) - # 视频BGM作者 - video_music_author = str(js['item_list'][0]['music']['author']) - # 视频BGM ID - video_music_id = str(js['item_list'][0]['music']['id']) - # 视频BGM MID - video_music_mid = str(js['item_list'][0]['music']['mid']) - break; - else: - video_music = video_music_title = video_music_author = video_music_id = video_music_mid = 'No BGM found' - # 视频ID - video_aweme_id = str(js['item_list'][0]['statistics']['aweme_id']) - # 评论数量 - video_comment_count = str(js['item_list'][0]['statistics']['comment_count']) - # 获赞数量 - video_digg_count = str(js['item_list'][0]['statistics']['digg_count']) - # 播放次数 - video_play_count = str(js['item_list'][0]['statistics']['play_count']) - # 分享次数 - video_share_count = str(js['item_list'][0]['statistics']['share_count']) - # 上传时间戳 - video_create_time = str(js['item_list'][0]['create_time']) - # 视频封面 - video_cover = js['item_list'][0]['video']['cover']['url_list'][0] - # 视频动态封面 - video_dynamic_cover = js['item_list'][0]['video']['dynamic_cover']['url_list'][0] - # 视频原始封面 - video_origin_cover = js['item_list'][0]['video']['origin_cover']['url_list'][0] - # 将话题保存在列表中 - video_hashtags = [] - for tag in js['item_list'][0]['text_extra']: - video_hashtags.append(tag['hashtag_name']) - # 结束时间 - end = time.time() - # 解析时间 - analyze_time = format((end - start), '.4f') - # 返回包含数据的字典 - video_data = {'status': 'success', - 'analyze_time': (analyze_time + 's'), - 'url_type': url_type, - 'platform': 'douyin', - 'original_url': original_url, - 'share_url': share_url, - 'music_share_url': music_share_url, - 'api_url': api_url, - 'video_title': video_title, - 'nwm_video_url': video_url, - 'nwm_video_url_1080p': nwm_video_url_1080p, - 'wm_video_url': wm_video_url, - 'video_aweme_id': video_aweme_id, - 'video_author': video_author, - 'video_author_signature': video_author_signature, - 'video_author_uid': video_author_uid, - 'video_author_id': video_author_id, - 'video_music': video_music, - 'video_music_title': video_music_title, - 'video_music_author': video_music_author, - 'video_music_id': video_music_id, - 'video_music_mid': video_music_mid, - 'video_comment_count': video_comment_count, - 'video_digg_count': video_digg_count, - 'video_play_count': video_play_count, - 'video_share_count': video_share_count, - 'video_create_time': video_create_time, - 'video_cover': video_cover, - 'video_dynamic_cover': video_dynamic_cover, - 'video_origin_cover': video_origin_cover, - 'video_hashtags': video_hashtags} - return video_data - except Exception as e: - # 返回异常 - return {'status': 'failed', 'reason': e, 'function': 'Scraper.douyin()', 'value': original_url} - - @retry(stop=stop_after_attempt(3), wait=wait_random(min=1, max=2)) - def tiktok(self, original_url: str, proxies: dict = None): - """ - 解析TikTok链接 - :param proxies: {'all': 127.0.0.1:2333}, Default not use proxy. - :param original_url:TikTok链接 - :return:包含信息的字典 - """ - - headers = self.headers - # 开始时间 - start = time.time() - # 校验TikTok链接 - if '@' in original_url: - print("目标链接: ", original_url) - else: - # 从请求头中获取原始链接 - response = requests.get(url=original_url, headers=headers, allow_redirects=False, proxies=proxies) - true_link = response.headers['Location'].split("?")[0] - original_url = true_link - # TikTok请求头返回的第二种链接类型 - if '.html' in true_link: - response = requests.get(url=true_link, headers=headers, allow_redirects=False, proxies=proxies) - original_url = response.headers['Location'].split("?")[0] - print("目标链接: ", original_url) - try: - # 获取视频ID - video_id = re.findall('/video/(\d+)?', original_url)[0] - print('获取到的TikTok视频ID是{}'.format(video_id)) - # 尝试从TikTok网页获取部分视频数据 - try: - tiktok_headers = self.tiktok_headers - html = requests.get(url=original_url, headers=tiktok_headers, proxies=proxies, timeout=1) - # 正则检索网页中存在的JSON信息 - resp = re.search('"ItemModule":{(.*)},"UserModule":', html.text).group(1) - resp_info = ('{"ItemModule":{' + resp + '}}') - result = json.loads(resp_info) - # 从网页中获得的视频JSON数据 - video_info = result["ItemModule"][video_id] - except: - video_info = None - # 从TikTok官方API获取部分视频数据 - tiktok_api_link = 'https://api.tiktokv.com/aweme/v1/aweme/detail/?aweme_id={}'.format( - video_id) - print('正在请求API链接:{}'.format(tiktok_api_link)) - response = requests.get(url=tiktok_api_link, headers=headers, proxies=proxies).text - # 将API获取到的内容格式化为JSON - result = json.loads(response) - if 'image_post_info' in response: - # 判断链接是图集链接 - url_type = 'album' - print('类型为图集/type album') - # 视频标题 - album_title = result["aweme_detail"]["desc"] - # 视频作者昵称 - album_author_nickname = result["aweme_detail"]['author']["nickname"] - # 视频作者ID - album_author_id = result["aweme_detail"]['author']["unique_id"] - # 上传时间戳 - album_create_time = result["aweme_detail"]['create_time'] - # 视频ID - album_aweme_id = result["aweme_detail"]['statistics']['aweme_id'] - try: - # 视频BGM标题 - album_music_title = result["aweme_detail"]['music']['title'] - # 视频BGM作者 - album_music_author = result["aweme_detail"]['music']['author'] - # 视频BGM ID - album_music_id = result["aweme_detail"]['music']['id'] - # 视频BGM链接 - album_music_url = result["aweme_detail"]['music']['play_url']['url_list'][0] - except: - album_music_title, album_music_author, album_music_id, album_music_url = "None", "None", "None", "None" - # 评论数量 - album_comment_count = result["aweme_detail"]['statistics']['comment_count'] - # 获赞数量 - album_digg_count = result["aweme_detail"]['statistics']['digg_count'] - # 播放次数 - album_play_count = result["aweme_detail"]['statistics']['play_count'] - # 下载次数 - album_download_count = result["aweme_detail"]['statistics']['download_count'] - # 分享次数 - album_share_count = result["aweme_detail"]['statistics']['share_count'] - # 无水印图集 - album_list = [] - for i in result["aweme_detail"]['image_post_info']['images']: - album_list.append(i['display_image']['url_list'][0]) - # 结束时间 - end = time.time() - # 解析时间 - analyze_time = format((end - start), '.4f') - # 储存数据 - album_data = {'status': 'success', - 'analyze_time': (analyze_time + 's'), - 'url_type': url_type, - 'api_url': tiktok_api_link, - 'original_url': original_url, - 'platform': 'tiktok', - 'album_title': album_title, - 'album_list': album_list, - 'album_author_nickname': album_author_nickname, - 'album_author_id': album_author_id, - 'album_create_time': album_create_time, - 'album_aweme_id': album_aweme_id, - 'album_music_title': album_music_title, - 'album_music_author': album_music_author, - 'album_music_id': album_music_id, - 'album_music_url': album_music_url, - 'album_comment_count': album_comment_count, - 'album_digg_count': album_digg_count, - 'album_play_count': album_play_count, - 'album_share_count': album_share_count, - 'album_download_count': album_download_count - } - # 返回包含数据的字典 - return album_data - else: - # 类型为视频 - url_type = 'video' - print('类型为视频/type video') - # 无水印视频链接 - nwm_video_url = result["aweme_detail"]["video"]["play_addr"]["url_list"][0] - try: - # 有水印视频链接 - wm_video_url = result["aweme_detail"]["video"]['download_addr']['url_list'][0] - except Exception: - # 有水印视频链接 - wm_video_url = 'None' - # 视频标题 - video_title = result["aweme_detail"]["desc"] - # 视频作者昵称 - video_author_nickname = result["aweme_detail"]['author']["nickname"] - # 视频作者ID - video_author_id = result["aweme_detail"]['author']["unique_id"] - # 上传时间戳 - video_create_time = result["aweme_detail"]['create_time'] - # 视频ID - video_aweme_id = result["aweme_detail"]['statistics']['aweme_id'] - try: - # 视频BGM标题 - video_music_title = result["aweme_detail"]['music']['title'] - # 视频BGM作者 - video_music_author = result["aweme_detail"]['music']['author'] - # 视频BGM ID - video_music_id = result["aweme_detail"]['music']['id'] - # 视频BGM链接 - video_music_url = result["aweme_detail"]['music']['play_url']['url_list'][0] - except: - video_music_title, video_music_author, video_music_id, video_music_url = "None", "None", "None", "None" - # 评论数量 - video_comment_count = result["aweme_detail"]['statistics']['comment_count'] - # 获赞数量 - video_digg_count = result["aweme_detail"]['statistics']['digg_count'] - # 播放次数 - video_play_count = result["aweme_detail"]['statistics']['play_count'] - # 下载次数 - video_download_count = result["aweme_detail"]['statistics']['download_count'] - # 分享次数 - video_share_count = result["aweme_detail"]['statistics']['share_count'] - # 视频封面 - video_cover = result["aweme_detail"]['video']['cover']['url_list'][0] - # 视频动态封面 - video_dynamic_cover = result["aweme_detail"]['video']['dynamic_cover']['url_list'][0] - # 视频原始封面 - video_origin_cover = result["aweme_detail"]['video']['origin_cover']['url_list'][0] - # 将话题保存在列表中 - video_hashtags = [] - for tag in result["aweme_detail"]['text_extra']: - if 'hashtag_name' in tag: - video_hashtags.append(tag['hashtag_name']) - else: - continue - if video_info != None: - # 作者粉丝数量 - video_author_followerCount = video_info['authorStats']['followerCount'] - # 作者关注数量 - video_author_followingCount = video_info['authorStats']['followingCount'] - # 作者获赞数量 - video_author_heartCount = video_info['authorStats']['heartCount'] - # 作者视频数量 - video_author_videoCount = video_info['authorStats']['videoCount'] - # 作者已赞作品数量 - video_author_diggCount = video_info['authorStats']['diggCount'] - else: - # 作者粉丝数量 - video_author_followerCount = 'None' - # 作者关注数量 - video_author_followingCount = 'None' - # 作者获赞数量 - video_author_heartCount = 'None' - # 作者视频数量 - video_author_videoCount = 'None' - # 作者已赞作品数量 - video_author_diggCount = 'None' - # 结束时间 - end = time.time() - # 解析时间 - analyze_time = format((end - start), '.4f') - # 储存数据 - video_data = {'status': 'success', - 'analyze_time': (analyze_time + 's'), - 'url_type': url_type, - 'api_url': tiktok_api_link, - 'original_url': original_url, - 'platform': 'tiktok', - 'video_title': video_title, - 'nwm_video_url': nwm_video_url, - 'wm_video_url': wm_video_url, - 'video_author_nickname': video_author_nickname, - 'video_author_id': video_author_id, - 'video_create_time': video_create_time, - 'video_aweme_id': video_aweme_id, - 'video_music_title': video_music_title, - 'video_music_author': video_music_author, - 'video_music_id': video_music_id, - 'video_music_url': video_music_url, - 'video_comment_count': video_comment_count, - 'video_digg_count': video_digg_count, - 'video_play_count': video_play_count, - 'video_share_count': video_share_count, - 'video_download_count': video_download_count, - 'video_author_followerCount': video_author_followerCount, - 'video_author_followingCount': video_author_followingCount, - 'video_author_heartCount': video_author_heartCount, - 'video_author_videoCount': video_author_videoCount, - 'video_author_diggCount': video_author_diggCount, - 'video_cover': video_cover, - 'video_dynamic_cover': video_dynamic_cover, - 'video_origin_cover': video_origin_cover, - 'video_hashtags': video_hashtags - } - # 返回包含数据的字典 - return video_data - except Exception as e: - # 异常捕获 - return {'status': 'failed', 'reason': e, 'function': 'Scraper.tiktok()', 'value': original_url} - - -if __name__ == '__main__': - # 测试类 - scraper = Scraper() - while True: - url = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', - input("Enter your Douyin/TikTok url here to test: "))[0] - try: - if 'douyin.com' in url: - douyin_date = scraper.douyin(url) - print(douyin_date) - else: - tiktok_date = scraper.tiktok(url) - print(tiktok_date) - except Exception as e: - print("Error: " + str(e))