-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpost.py
More file actions
78 lines (63 loc) · 3 KB
/
Copy pathpost.py
File metadata and controls
78 lines (63 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""One TikTok post, through GET /api/v1/tiktok/post.
`post` returns the full video record plus TikTok's own content classification
(`categories`), the creation region (`location`) and the creator's follower
stats inline. That classification is what this endpoint adds over a plain video
call, so this script leads with it.
export CHOCODATA_API_KEY="your_key"
python tiktok_post_scraper_api_codes/post.py
python tiktok_post_scraper_api_codes/post.py https://www.tiktok.com/@nba/video/7520021062537530654
"""
import argparse
import datetime
import sys
import urllib.parse
from _common import get_post, rounded
DEFAULT = "https://www.tiktok.com/@duolingo/video/7459895174467177774"
def expiry_of(url):
"""Signed CDN URLs carry their own expiry. Read it rather than guess."""
if not url:
return None
q = urllib.parse.parse_qs(urllib.parse.urlparse(url).query)
raw = (q.get("x-expires") or q.get("expire") or [None])[0]
if raw and raw.isdigit():
return datetime.datetime.fromtimestamp(int(raw), datetime.timezone.utc)
return None
def main():
ap = argparse.ArgumentParser()
ap.add_argument("url", nargs="?", default=DEFAULT, help="a TikTok video permalink")
ap.add_argument("--country", help="egress country, ISO-2 (default us)")
args = ap.parse_args()
p = get_post(url=args.url, country=args.country)
a, s = p["author"], p["stats"]
print(f"@{a['uniqueId']}" + (" [verified]" if a["verified"] else ""))
print(f" {p['caption']}")
print(f" posted {p['created_at'][:10]} {p['video']['duration']}s "
f"region {p['location']} id {p['id']}")
print()
# The reason to call `post`: TikTok's own classification of the content.
print(f" categories : {', '.join(p['categories']) or '(none)'}")
print(f" hashtags : {', '.join(p['hashtags']) or '(none)'}")
sw = p["suggested_words"]
print(f" suggested : {', '.join(sw) if sw else '(empty on this post)'}")
print()
# Every counter in a post is TikTok's display value; flag which are rounded.
print(" engagement value precision")
for k in ("plays", "likes", "comments", "shares", "saves"):
val = s[k]
flag = "rounded" if rounded(val) else "exact"
print(f" {k:<10} {val:>14,} {flag}")
# The creator's follower count on `post` is the rounded display number too.
print(f" author.followerCount {a['followerCount']:>8,} "
f"{'rounded' if rounded(a['followerCount']) else 'exact'} "
f"(call the profile endpoint for the exact figure)")
print()
print(f" music : {p['music']['title']!r} by {p['music']['author']}")
print(f" images : {len(p['images'])} cover frames")
exp = expiry_of(p["thumbnail"])
if exp:
left = exp - datetime.datetime.now(datetime.timezone.utc)
print(f" thumbnail : signed, expires {exp:%Y-%m-%d %H:%M}Z "
f"(in {left.total_seconds() / 3600:.0f}h)")
return 0
if __name__ == "__main__":
sys.exit(main())