当前位置: 首页 > article >正文

使用 Python 爬取 TikTok 评论的实现与解析

在今天的博客中,我将分享如何使用 Python 爬取 TikTok 视频的评论信息。通过构建一个简单的爬虫,我们可以抓取 TikTok 上某个视频的所有评论,并将其保存到 CSV 文件中。以下是详细的代码实现与解释。

一、引入必要的库

在代码开始的部分,我们导入了几个关键的 Python 库:

import time
import requests
import execjs
import pandas as pd
from datetime import datetime
from urllib.parse import urlencode
from loguru import logger
  • time:用于时间操作。
  • requests:处理 HTTP 请求,获取数据。
  • execjs:用于执行 JavaScript 代码,帮助加密请求参数。
  • pandas:用于处理数据并将数据保存为 CSV 文件。
  • datetime:用于获取当前时间并进行格式化。
  • urlencode:用于对 URL 参数进行编码。
  • loguru:用于日志记录,帮助我们调试和记录程序运行的状态。

二、定义 TiktokUserSearch

我们通过一个名为 TiktokUserSearch 的类来实现爬虫的主体逻辑。该类负责初始化请求的参数、发送 HTTP 请求、解析数据以及保存评论。

1. 初始化请求头和 Cookie

首先,在类的初始化方法中,我们设置了请求头(self.headers)和 Cookie(self.cookies),用于模拟浏览器访问 TikTok。

class TiktokUserSearch:
    def __init__(self, output_file=None):
        self.headers = {
            "authority": "www.tiktok.com",
            "accept": "*/*",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
        }
        self.cookies = None
        self.output_file = output_file if output_file else f'tiktok_comments_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
  • self.headers 包含了用户代理(User-Agent)和其他常见的 HTTP 请求头部信息。
  • self.cookies 用于存储用户的登录信息。
  • self.output_file 用于设置输出文件的文件名,默认为 tiktok_comments_时间.csv 格式。
2. cookie_str_to_dict 方法

这个方法用于将传入的 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] = value
    return cookie_dict
3. 发送 GET 请求

get 方法是用来发送 HTTP 请求并获取评论数据的。根据传入的 cursor 参数(分页标识符),我们构建请求参数并发送请求。

def get(self, cursor, cookie_str):
    self.cookies = self.cookie_str_to_dict(cookie_str)
    url = "https://www.tiktok.com/api/comment/list/"
    params = { ... }
    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})
    
    response = requests.get(
        url,
        headers=self.headers,
        cookies=self.cookies,
        params=params,
        timeout=(3, 10),
        proxies=None
    )
    return response.json()
  • cookie_str 用于设置请求的 Cookie。
  • 通过调用 JavaScript 文件 encrypt.js 中的 sign 方法对请求参数进行加密。
  • requests.get 发送 GET 请求获取 TikTok 的评论数据。
4. 解析和保存评论数据

parse_and_save_comments 方法用于解析返回的 JSON 数据,并将评论信息提取后保存到 CSV 文件。

    def parse_and_save_comments(self, data):
        """
        解析评论数据并保存到CSV文件,保存所有可用字段
        :param data: API返回的评论数据
        """
        if 'comments' not in data:
            logger.error("没有找到评论数据")
            return
            
        comments_list = []
        for comment in data['comments']:
            # 解析每条评论的所有可用字段
            comment_data = {
                # 评论基本信息
                'comment_id': comment['cid'],
                'text': comment['text'],
                'create_time': datetime.fromtimestamp(comment['create_time']).strftime('%Y-%m-%d %H:%M:%S'),
                'digg_count': comment['digg_count'],
                'reply_count': comment['reply_comment_total'],
                'is_author_digged': comment.get('is_author_digged', False),
                'aweme_id': comment.get('aweme_id', ''),
                'stick_position': comment.get('stick_position', 0),
                'is_sticky': comment.get('is_sticky', False),
                'label_list': str(comment.get('label_list', [])),
                
                # 用户信息
                'user_nickname': comment['user']['nickname'],
                'user_id': comment['user']['uid'],
                'unique_id': comment['user'].get('unique_id', ''),
                'user_sec_uid': comment['user'].get('sec_uid', ''),
                'user_avatar_thumb': comment['user'].get('avatar_thumb', {}).get('url_list', [None])[0],
                'user_signature': comment['user'].get('signature', ''),
                'user_verified': comment['user'].get('verified', False),
                'user_follow_status': comment['user'].get('follow_status', 0),
                'user_follower_count': comment['user'].get('follower_count', 0),
                'user_following_count': comment['user'].get('following_count', 0),
                'user_total_favorited': comment['user'].get('total_favorited', 0),
                
                # 评论状态信息
                'status': comment.get('status', 0),
                'trans_btn_style': comment.get('trans_btn_style', 0),
                'text_extra': str(comment.get('text_extra', [])),
                'comment_language': comment.get('comment_language', ''),
                
                # 标记是否为回复评论
                'is_reply': False,
                'parent_comment_id': '',
                'parent_comment_user_id': '',
                'parent_comment_user_nickname': ''
            }
            
            # 添加回复评论(如果有)
            if comment.get('reply_comment'):
                for reply in comment['reply_comment']:
                    reply_data = {
                        # 回复评论基本信息
                        'comment_id': reply['cid'],
                        'text': f"[回复] {reply['text']}",
                        'create_time': datetime.fromtimestamp(reply['create_time']).strftime('%Y-%m-%d %H:%M:%S'),
                        'digg_count': reply['digg_count'],
                        'reply_count': 0,
                        'is_author_digged': reply.get('is_author_digged', False),
                        'aweme_id': reply.get('aweme_id', ''),
                        'stick_position': reply.get('stick_position', 0),
                        'is_sticky': reply.get('is_sticky', False),
                        'label_list': str(reply.get('label_list', [])),
                        
                        # 回复用户信息
                        'user_nickname': reply['user']['nickname'],
                        'user_id': reply['user']['uid'],
                        'unique_id': reply['user'].get('unique_id', ''),
                        'user_sec_uid': reply['user'].get('sec_uid', ''),
                        'user_avatar_thumb': reply['user'].get('avatar_thumb', {}).get('url_list', [None])[0],
                        'user_signature': reply['user'].get('signature', ''),
                        'user_verified': reply['user'].get('verified', False),
                        'user_follow_status': reply['user'].get('follow_status', 0),
                        'user_follower_count': reply['user'].get('follower_count', 0),
                        'user_following_count': reply['user'].get('following_count', 0),
                        'user_total_favorited': reply['user'].get('total_favorited', 0),
                        
                        # 回复评论状态信息
                        'status': reply.get('status', 0),
                        'trans_btn_style': reply.get('trans_btn_style', 0),
                        'text_extra': str(reply.get('text_extra', [])),
                        'comment_language': reply.get('comment_language', ''),
                        
                        # 标记为回复评论,并添加父评论信息
                        'is_reply': True,
                        'parent_comment_id': comment['cid'],
                        'parent_comment_user_id': comment['user']['uid'],
                        'parent_comment_user_nickname': comment['user']['nickname']
                    }
                    
                    # 如果回复中包含被回复用户信息
                    if 'reply_to_reply_id' in reply:
                        reply_data.update({
                            'reply_to_reply_id': reply.get('reply_to_reply_id', ''),
                            'reply_to_username': reply.get('reply_to_username', ''),
                            'reply_to_user_id': reply.get('reply_to_userid', '')
                        })
                    
                    comments_list.append(reply_data)
                    
            comments_list.append(comment_data)
  • comments_list 存储所有评论(包括回复评论)。
  • 通过 pandas.DataFrame 将评论数据转化为表格格式并保存为 CSV 文件。

三、主程序执行流程

在主程序中,我们设置了输出文件名和 Cookie 字符串,然后调用 get 方法获取评论数据,并将数据传递给 parse_and_save_comments 方法进行解析和保存。

if __name__ == '__main__':
    output_file = 'tiktok_comments_output.csv'
    tiktok = TiktokUserSearch(output_file=output_file)
    
    cookie_str = 'your_cookie_string_here'
    cursor = '0'
    data = tiktok.get(cursor, cookie_str)

    if 'error' not in data:
        result = tiktok.parse_and_save_comments(data)
        logger.info(f"评论获取结果:")
    else:
        logger.error(f"获取评论失败:{data['error']}")

结果:

四、注意事项

  1. Cookie 获取:由于 TikTok 的 API 需要通过登录的 Cookie 才能获取评论,因此需要提供有效的 Cookie。可以通过浏览器的开发者工具获取 Cookie 字符串。
  2. 请求加密:TikTok 对请求参数进行了加密,使用 execjs 库执行 JavaScript 代码来加密请求参数。
  3. 分页机制:TikTok 的评论数据是分页的,每次请求会返回一个 cursor,表示下一页数据的位置。如果有更多数据,使用该 cursor 进行下一次请求。
  4. 错误处理:代码中包含了简单的错误处理机制,当请求发生网络错误时,会进行重试,最多重试三次。

五、总结

通过本文介绍的 TikTok 评论爬虫,您可以方便地抓取 TikTok 上的视频评论,并将其保存到本地进行进一步分析或存档。希望本文能对你有所帮助!


http://www.kler.cn/a/593907.html

相关文章:

  • 区跨链密码学
  • 基于 Swoole 的 PHP 异步框架评分与对比(按综合流行度排名)
  • 【MySQL】基本查询(第一弹)
  • 淘宝商品详情页API字段深度解析:如何精准提取SKU、销量、促销信息?
  • OAK相机入门(一):深度测距原理
  • 观察RenderDoc截帧UE时“Event”代表什么
  • Linux系统——crontab定时任务
  • 网络工程师的要求
  • 深蕾半导体IP-KVM产品方案解析
  • 云盘搭建笔记
  • 论文笔记——BiLLP框架
  • 【CAD二次开发】调试无法进入断点提示无可用源问题(非空心断点)
  • 【机器学习】什么是逻辑回归
  • 单调队列【C/C++】
  • JAVA接入DeepSeek大模型接口开发---阿里云的百炼模型
  • wpa_supplicant驱动初始化源码分析
  • 当Anaconda的安装路径与我想创建的conda虚拟环境路径不一致时,应该怎么操作?
  • Docker Compose部署MantisBT
  • java学习笔记5
  • 智能AI流式输出的前端展现过程