基于 Python 爬取 TikTok 搜索数据 Tiktok爬虫(2025.3.17)

1. 前言

在数据分析和网络爬虫的应用场景中,我们经常需要获取社交媒体平台的数据,例如 TikTok。本篇文章介绍如何使用 Python 爬取 TikTok 用户搜索数据,并解析其返回的数据。

结果截图

2. 项目环境准备

在正式运行代码之前,我们需要安装相关的 Python 库:

pip install requests pandas execjs loguru

此外,我们需要一个 JavaScript 运行环境(如 Node.js),用于执行加密签名代码。

3. 代码解析

3.1 初始化爬虫类

我们创建 TiktokUserSearch 类,并在初始化方法 __init__ 中设置请求头信息,并初始化输出文件。

class TiktokUserSearch:def __init__(self, output_file=None):self.headers = {  # 设置请求头"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...","referer": "https://www.tiktok.com/"}self.cookies = Noneself.output_file = output_file if output_file else f'tiktok_videos_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'

3.2 处理 Cookie

我们需要将 TikTok 的 cookie 从字符串转换成字典格式,以便后续请求使用。

    def cookie_str_to_dict(self, cookie_str) -> dict:cookie_dict = {}cookies = [i.strip() for i in cookie_str.split('; ') if i.strip() != ""]for cookie in cookies:key, value = cookie.split('=', 1)cookie_dict[key] = valuereturn cookie_dict

3.3 发送请求

TikTok 需要使用 X-Bogus 进行签名,我们需要执行 JavaScript 代码来获取该参数。

为了防止网络不稳定,设置三次重试机制。

可根据自己需求设置代理。

    def get(self, keyword, cursor, search_id, cookie_str):self.cookies = self.cookie_str_to_dict(cookie_str)url = "https://www.tiktok.com/api/search/general/full/"if cursor == "0":focus_state = "true"else:focus_state = "false"params = {"WebIdLastTime": f"{int(time.time())}","aid": "1988","app_language": "zh-Hans","app_name": "tiktok_web","browser_language": "zh-CN","browser_name": "Mozilla","browser_online": "true","browser_platform": "Win32","browser_version": self.headers['user-agent'].replace('Mozilla/', ''),"channel": "tiktok_web","cookie_enabled": "true","cursor": cursor,"device_id": "7339506347602019870","device_platform": "web_pc","focus_state": focus_state,"from_page": "search","history_len": "7","is_fullscreen": "false","is_page_visible": "true","keyword": keyword,"os": "windows","priority_region": "","referer": "","region": "KR","screen_height": "1080","screen_width": "1920","tz_name": "Asia/Shanghai","web_search_code": "{\"tiktok\":{\"client_params_x\":{\"search_engine\":{\"ies_mt_user_live_video_card_use_libra\":1,\"mt_search_general_user_live_card\":1}},\"search_server\":{}}}","webcast_language": "zh-Hans","msToken": self.cookies["msToken"],}if cursor != "0":params.update({"search_id": search_id})x_b = execjs.compile(open('./encrypt.js', encoding='utf-8').read()).call("sign", urlencode(params), self.headers["user-agent"])params.update({"X-Bogus": x_b})headers = self.headers.copy()headers.update({"referer": "https://www.tiktok.com/search?q=" + keyword})max_retries = 3for attempt in range(max_retries):try:response = requests.get(url,headers=headers,cookies=self.cookies,params=params,timeout=(3, 10),proxies=None)return response.json()except (ex1, ex2, ex3) as e:print(f"尝试 {attempt + 1}/{max_retries} 发生网络错误:{e}")if attempt < max_retries - 1:time.sleep(2)else:return {"error": f"Network error after {max_retries} attempts: {str(e)}"}except Exception as e:print(f"发生其他错误:{e}")return {"error": str(e)}

3.4 解析数据并存储

解析 TikTok 返回的视频数据,并保存到 CSV 文件。

    def parse_data(self, data_list):resultList = []video_data = []for u in data_list:try:item = u['item']author = item['author']stats = item['stats']author_stats = item['authorStats']  # 添加作者统计信息# 提取需要的数据video_info = {'video_id': item['id'],'desc': item['desc'],'create_time': datetime.fromtimestamp(item['createTime']).strftime('%Y-%m-%d %H:%M:%S'),'duration': item['video']['duration'],# 作者基本信息'author_id': author['id'],'author_name': author['uniqueId'],'author_nickname': author['nickname'],'author_signature': author['signature'],'author_verified': author['verified'],# 作者统计信息'author_following_count': author_stats['followingCount'],  # 关注数'author_follower_count': author_stats['followerCount'],    # 粉丝数'author_heart_count': author_stats['heartCount'],          # 获赞总数'author_video_count': author_stats['videoCount'],          # 视频总数'author_digg_count': author_stats['diggCount'],           # 点赞数# 视频统计信息'digg_count': stats['diggCount'],'share_count': stats['shareCount'],'comment_count': stats['commentCount'],'play_count': stats['playCount'],'collect_count': stats.get('collectCount', 0),'video_url': item['video']['playAddr']}# 添加标签信息if 'challenges' in item:video_info['hashtags'] = ','.join([tag['title'] for tag in item['challenges']])else:video_info['hashtags'] = ''# 添加音乐信息if 'music' in item:music = item['music']video_info.update({'music_id': music['id'],'music_title': music['title'],'music_author': music['authorName'],'music_original': music['original']})video_data.append(video_info)resultList.append(f"https://www.tiktok.com/@{author['uniqueId']}")except Exception as e:logger.error(f"解析视频数据时出错: {str(e)}")continue# 将数据保存到CSV文件try:df = pd.DataFrame(video_data)# 检查文件是否存在file_exists = os.path.exists(self.output_file)# 如果文件不存在,创建新文件并写入表头# 如果文件存在,追加数据不写入表头df.to_csv(self.output_file, mode='a', header=not file_exists,index=False, encoding='utf-8-sig')logger.info(f"数据已{'追加' if file_exists else '保存'}到文件: {self.output_file}")except Exception as e:logger.error(f"保存CSV文件时出错: {str(e)}")return resultList

3.5 运行爬虫

我们定义 main 方法,负责调用 get 方法获取数据并解析。

    def main(self, keyword, cookie_str, cursor="0", search_id=None):dataJson = self.get(keyword, cursor, search_id, cookie_str)if dataJson:if "error" in dataJson:return {"cursor": cursor, "search_id": search_id, "data": [], "status": "-2", "error": dataJson["error"]}elif "verify_event" in str(dataJson):return {"cursor": cursor, "search_id": search_id, "data": [], "status": "-1"}else:# 解析数据并保存到CSVif 'data' in dataJson:self.parse_data(dataJson['data'])return dataJson

3.6 运行入口

最后,我们编写 if __name__ == '__main__' 逻辑,定义要爬取的关键词,并进行循环爬取。

if __name__ == '__main__':os.makedirs('results1', exist_ok=True)topics = ["Chen Duxiu","Li Dazhao",
]for keyword in topics:logger.info(f"开始爬取 {keyword} 的视频")output_file = f'results1/{keyword}_videos.csv'  # 你可以自定义文件名tiktok = TiktokUserSearch(output_file=output_file)cookie_str = '_ttp=2ZzUB37CLclhWsrgyW56Erox1XM; tiktok_webapp_theme_auto_dark_ab=1; delay_guest_mode_vid=5; passport_csrf_token=d8e4d28ec7abdf12a7829d524dca64de; passport_csrf_token_default=d8e4d28ec7abdf12a7829d524dca64de; tt_chain_token=SSmpjX/0in/IP8BYwawD+Q==; multi_sids=7361707798058615814%3A53b730c284c4eaaa9bb2157eef01d70d; cmpl_token=AgQQAPNoF-RO0rYU5JqLsx0__dmghl8Nv5IhYNkWMA; passport_auth_status=a8a7a1e1c4b96a994a45acb38dc83509%2C; passport_auth_status_ss=a8a7a1e1c4b96a994a45acb38dc83509%2C; uid_tt=7857882a3366539dc1d9ca226b3fdc91f76b1b072c7da11dd4120368d88bf861; uid_tt_ss=7857882a3366539dc1d9ca226b3fdc91f76b1b072c7da11dd4120368d88bf861; sid_tt=53b730c284c4eaaa9bb2157eef01d70d; sessionid=53b730c284c4eaaa9bb2157eef01d70d; sessionid_ss=53b730c284c4eaaa9bb2157eef01d70d; store-idc=maliva; store-country-code=ca; store-country-code-src=uid; tt-target-idc=useast1a; tt-target-idc-sign=t3pz21FprSb2qc1ucJWFQbxzCKwgoBX9PKUWEbPHh7_4mpPThOuO0EN9pm2ORzFqk0bLFt6MtI9-gofvcVtQFoGSTOI_JvUWIAAUSHz1mM1A9jP1kRk_qucQnxEMOLvir3s4ffm0hJSh62RyKNO5LBTlT-fsqbi2tQVUwrgIGF-2HFT04S52ciyRnKAXr_0NyD3Aa0lM4J4hUGplo46wKRfId1DwwajXudUfjqJ3rvAuA8qURTsSHCKuDjLbcdfhcC0WKqemrmHFBJ11hGFJxiL4VEOClIoJGrF1_S9jvlx0H0Nph9BHlHNA-wzwi3NF6hPK17WL3TSvsqfEiKclZ5ScpHMv7ATYfOK4BVOzKXrq6fCxzNBT5kCNc4-ImuvjBNqpY8yL2s2KusWxslveOyIq3gwU3Dhxl084w5Tsp13xzuFOGNVHK5ZPeS5ERmykYFB6uTIHty9W_Z6pwN1tT9yQ-34qyZRZB7WONZn_NAFsywU6Hj4wcHLQkJ-tIiAO; last_login_method=google; tiktok_webapp_theme_source=auto; tiktok_webapp_theme=dark; sid_guard=53b730c284c4eaaa9bb2157eef01d70d%7C1740207607%7C15551996%7CThu%2C+21-Aug-2025+07%3A00%3A03+GMT; sid_ucp_v1=1.0.0-KDc2NDMxMTQzMTkwNTY2NTJiOWZhMmZhM2ZlMDg3ZDE0YzNiOGU5NTUKGQiGiIec0MeClWYQ9-vlvQYYsws4CEASSAQQAxoGbWFsaXZhIiA1M2I3MzBjMjg0YzRlYWFhOWJiMjE1N2VlZjAxZDcwZA; ssid_ucp_v1=1.0.0-KDc2NDMxMTQzMTkwNTY2NTJiOWZhMmZhM2ZlMDg3ZDE0YzNiOGU5NTUKGQiGiIec0MeClWYQ9-vlvQYYsws4CEASSAQQAxoGbWFsaXZhIiA1M2I3MzBjMjg0YzRlYWFhOWJiMjE1N2VlZjAxZDcwZA; odin_tt=a7027d0b8a102be6dd20600ca35291f4aee8c003895d8913d5c2f8276f16d6b974345357a22ed0bdcb57930d326afded3e5bd3105e061197876c80a92bbdbbfc29402634b8e7439ba178a1c7ed9ca552; tt_csrf_token=qs6ncqIZ-SbUZVzUbkUZ2SViJyY7VzYRki0M; perf_feed_cache={%22expireTimestamp%22:1742302800000%2C%22itemIds%22:[%227481325874978000134%22%2C%227463463290065161505%22%2C%227466633420098112799%22]}; msToken=BnkIjkPpJEc1i9jiiwT_paC5FW-NL62UVF7-lzpHYki9WIA_KpLrplpY-qlZfuG7V12rbCDHiyQYNrZcOnTzZLk1cvnH3_E_89nfOpqpVquKbSR-Nqr6bGDmL220vjBHdutm4R-gfVnYIG7fvWOJUkZ7yg==; ttwid=1%7Cv5j4n07c_G3ZtA91KIuree-ptnDLwgTwFuM8BnZINnQ%7C1742131441%7Ce88a85fcc36fd7e79815fddb10d16ef553b5e2e4a51c65e2c40098ade19023e2; msToken=iYDKRqCM8rSqc_9ZDzQnWcQiv_iJqPk15-6Y-iFBUmk4uIzb61dM13b9fWHcg4hxGkl9L3n56glok05TllvGurkwpgBYEF8N76ZRIii7OvNEkrk004dagNuoqQVeV9Bzd0_9naXjFXtEiMRi330G5Jdakw==; passport_fe_beating_status=false'has_more = 1cursor = '0'search_id = Nonewhile has_more:data = tiktok.main(keyword, cookie_str, cursor, search_id)logger.info(data)if data and isinstance(data, dict):has_more = data.get('has_more', 0)logger.info(has_more)cursor = data.get('cursor', '0')search_id = data.get('log_pb', {}).get('impr_id')if 'data' in data:data = data['data']else:logger.error("No data found in response")breakelse:logger.error("Invalid response format")breaktime.sleep(1)  # 添加延时避免请求过快logger.info(f"爬取 {keyword} 的视频完成")time.sleep(30)logger.info(f"等待30秒后继续爬取下一个主题")logger.info("所有主题的视频爬取完成")

4. 关键问题与解决方案

  1. TikTok 反爬措施

    • 需要定期更新 Cookie
    • 适当增加请求间隔
    • 使用代理提高稳定性
  2. X-Bogus 参数

    • 需要使用 JavaScript 计算签名
    • 依赖 encrypt.js 进行加密
  3. 数据存储

    • 使用 pandas 处理数据
    • 追加模式写入 CSV,避免数据丢失

6. 总结

本文介绍了如何使用 Python 爬取 TikTok 用户搜索数据,包括如何构造请求、解析数据并存储到 CSV 文件。希望对有类似需求的读者有所帮助!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35320.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云、腾讯云云主机如何提升远程桌面安全(VNC登录)

远程桌面连接&#xff08;RDP&#xff09;是管理主机的常用方式&#xff0c;但同时也带来了安全风险。黑客会对远程桌面进行暴力破解攻击和撞库攻击。作为云主机&#xff0c;在远程桌面方面有天然的安全优势&#xff1a;可以关闭远程桌面服务或端口&#xff0c;限制只能通过网页…

【etcd】

一、ETCD 简介 etcd是一个由CoreOS团队开发的开源项目&#xff0c;旨在提供一个高可用的、分布式的、一致的键值存储&#xff0c;用于配置共享和服务发现。尽管它看起来像一个键值存储&#xff0c;但etcd的设计目标远远超出了传统数据库的功能范围。 etcd的核心特性包括&…

深圳南柯电子|医疗设备EMC检测测试整改:保障患者安全的第一步

在医疗设备领域&#xff0c;电磁兼容性&#xff08;EMC&#xff09;是确保设备安全、有效运行的关键指标。随着医疗技术的飞速发展&#xff0c;医疗设备日益复杂&#xff0c;其电磁环境也愈发复杂多变。EMC检测测试及整改因此成为医疗设备研发、生产、销售过程中不可或缺的一环…

项目实战系列:基于瑞萨RA6M5构建多节点OTA升级-系统设计<一>

项目背景 原嵌入式控制系统采用分布式模块化架构&#xff0c;由12个功能板卡&#xff08;通信控制、信号采集、驱动执行等&#xff09;组成。系统维护阶段存在以下痛点&#xff1a; 低效的本地烧录机制&#xff1a;各板卡固件升级需通过JTAG接口逐一手动连接JLINK仿真器&#x…

五大方向全面对比 IoTDB 与 OpenTSDB

对比系列第三弹&#xff0c;详解 IoTDB VS OpenTSDB&#xff01; 之前&#xff0c;我们已经深入探讨了时序数据库 Apache IoTDB 与 InfluxDB、Apache HBase 在架构设计、性能和功能方面等多个维度的区别。还没看过的小伙伴可以点击阅读&#xff1a; Apache IoTDB vs InfluxDB 开…

RAGFlow部署与使用(开源本地知识库管理系统,包括kibana配置)

一、RAGFlow 简介 戳我访问RAGFlow RAGFlow 是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。它可以给我们搭建本地知识库&#xff0c;将用户的知识文档上传到RAGFlow后&#xff0c;通过文档切分、向量入库&#xff0c;在…

HTB 学习笔记 【中/英】《Web 应用 - 布局》P2

&#x1f4cc; 这篇文章讲了什么&#xff1f; 介绍了 Web 应用的架构和布局&#xff0c;包括不同的基础设施、组件、架构模式等。讲解了 常见的 Web 应用部署方式&#xff08;单服务器、多服务器等&#xff09;&#xff0c;并分析了它们的安全性。介绍了 微服务架构&#xff0…

蓝牙系统的核心组成解析

一、硬件层&#xff1a;看得见的物理载体 1. 射频模块&#xff08;Radio Frequency Module&#xff09; 专业描述&#xff1a;工作在2.4GHz ISM频段&#xff0c;支持GFSK/π/4 DQPSK/8DPSK调制方式 功能类比&#xff1a;相当于人的"嘴巴"和"耳朵" 发射端…

LeRobot源码剖析——对机器人各个动作策略的统一封装:包含ALOHA ACT、Diffusion Policy、VLA模型π0

前言 过去2年多的深入超过此前7年&#xff0c;全靠夜以继日的勤奋&#xff0c;一天当两天用&#xff0c;抠论文 抠代码 和大模型及具身同事讨论&#xff0c;是目前日常 而具身库里&#xff0c;idp3、π0、lerobot值得反复研究&#xff0c;故&#xff0c;近期我一直在抠π0及l…

ISP--Gamma Correction

文章目录 现象Gamma产生的原因CRT属性导致人眼的亮度特性 gamma校正LUT法线性插值法模拟gamma法 现象 从上往下看左侧黑色块黑得越来越严重&#xff0c;对比度也在逐渐加深。此时灰阶的高亮区获得的数据位变少&#xff0c;暗区获得的数据位变多&#xff0c;暗区细节会更多。但是…

光谱相机识别瓶子材质的技术原理和应用案例

一、技术原理 ‌光谱特征差异识别‌ 不同材质的塑料&#xff08;如PET、PP、PE等&#xff09;因化学结构差异&#xff0c;在近红外或可见光波段会呈现独特的光谱反射曲线。例如&#xff0c;高光谱相机通过分析数百个窄波段的光谱数据&#xff0c;可生成每种材质的“光谱指纹”…

某快餐店用户市场数据挖掘与可视化

1、必要库的载入 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns2、加载并清洗数据 # 2.1 加载数据 df pd.read_csv(/home/mw/input/survey6263/mcdonalds.csv)# 2.2 数据清洗 # 2.2.1 检查缺失值 print(缺失值情况&#xff1a;) print(df.isn…

MySQL 衍生表(Derived Tables)

在SQL的查询语句select …. from …中&#xff0c;跟在from子句后面的通常是一张拥有定义的实体表&#xff0c;而有的时候我们会用子查询来扮演实体表的角色&#xff0c;这个在from子句中的子查询会返回一个结果集&#xff0c;这个结果集可以像普通的实体表一样查询、连接&…

Electron使用WebAssembly实现CRC-16 MAXIM校验

Electron使用WebAssembly实现CRC-16 MAXIM校验 将C/C语言代码&#xff0c;经由WebAssembly编译为库函数&#xff0c;可以在JS语言环境进行调用。这里介绍在Electron工具环境使用WebAssembly调用CRC-16 MAXIM格式校验的方式。 CRC-16 MAXIM校验函数WebAssembly源文件 C语言实…

HTB 学习笔记 【中/英】《前端 vs. 后端》P3

&#x1f4cc; 这篇文章讲了什么&#xff1f; 介绍了 前端&#xff08;客户端&#xff09; 和 后端&#xff08;服务器端&#xff09; 的区别。解释了 全栈开发&#xff08;Full Stack Development&#xff09;&#xff0c;即前端后端开发。介绍了 前端和后端常用的技术。讨论…

SpringBoot集成ElasticSearch实现支持错别字检索和关键字高亮的模糊查询

文章目录 一、背景二、环境准备1.es8集群2.Kibana3.Canal 三、集成到SpringBoot1.新增依赖2.es配置类3.建立索引4.修改查询方法 四、修改前端 一、背景 我们在开发项目的搜索引擎的时候&#xff0c;如果当数据量庞大、同时又需要支持全文检索模糊查询&#xff0c;甚至你想做到…

麒麟系统使用-安装 SQL Developer

文章目录 前言一、基础准备1.基本环境2.相关包下载 二、进行相关配置1.配置JAVA2.配置SQL Developer 总结 前言 作为我国自主研发的操作系统&#xff0c;麒麟系统在使用时需要考虑安装相应的app。尽管麒麟系统是基于linux开发&#xff0c;可由于版本的一些差异&#xff0c;麒麟…

PrimeTime:timing_report_unconstrained_paths变量

相关阅读 PrimeTimehttps://blog.csdn.net/weixin_45791458/category_12900271.html?spm1001.2014.3001.5482 PrimeTime自Q-2019.12版本起引入了timing_report_unconstrained_paths变量&#xff08;默认值为false&#xff09;&#xff0c;该变量控制是否在使用report_timing命…

洛谷 P1115 最大子段和(前缀和详解)c++

题目链接&#xff1a;P1115 最大子段和 - 洛谷 1.题目分析 2.算法原理 解法&#xff1a;利用前缀和 思考&#xff1a;如何求出以a[i]为结尾的所有子区间中最大的子段和 假设 i 等于5&#xff0c;以 a[ i ] 为结尾的区间一共是五段&#xff08;黑色线条部分&#xff09;&#…

JetBrains(全家桶: IDEA、WebStorm、GoLand、PyCharm) 2024.3+ 2025 版免费体验方案

JetBrains&#xff08;全家桶: IDEA、WebStorm、GoLand、PyCharm&#xff09; 2024.3 2025 版免费体验方案 前言 JetBrains IDE 是许多开发者的主力工具&#xff0c;但从 2024.02 版本起&#xff0c;JetBrains 调整了试用政策&#xff0c;新用户不再享有默认的 30 天免费试用…