Tiktok 關鍵字 視頻及評論信息爬蟲(1) [2025.04.07]

🙋?♀?Tiktok APP的基于關鍵字檢索的視頻及評論信息爬蟲共分為兩期,希望對大家有所幫助。
第一期見下文。
第二期:基于視頻URL的評論信息爬取

1. Node.js環境配置

首先配置 JavaScript 運行環境(如 Node.js),用于執行加密簽名代碼。
Node.js下載網址:https://nodejs.org/en
Node.js的安裝方法(環境配置非常關鍵,決定了后面的程序是否可以使用):https://blog.csdn.net/liufeifeihuawei/article/details/132425239

2. Py環境配置

import time
import requests
import execjs
import os
from datetime import datetime
from urllib.parse import urlencode
from loguru import logger
import json
import random
from typing import Optional, Dict, List, Any
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

3. 基于關鍵字檢索的視頻信息爬取

1. 主程序:設定爬取的關鍵字
通過文件topics.csv導入你希望爬取的關鍵字。
通過文件videosInfo.json存儲爬取的結果,以字典格式存儲。

if __name__ == '__main__':os.makedirs('../results', exist_ok=True)keywords, fields = read_csv(file_path='topics.csv')  # 設定爬取的關鍵字output_file = f'../results/videosInfo.json'  # 保存結果的文件cookie_str = read_cookie()# 使用多線程并發爬取with ThreadPoolExecutor(max_workers=1) as executor:futures = []for i in range(len(keywords)):futures.append(executor.submit(crawl_keyword, keywords[i], output_file, cookie_str, fields[i], 20))for future in as_completed(futures):try:future.result()except Exception as e:logger.error(f"爬取過程中發生錯誤: {str(e)}")logger.info("所有主題的視頻爬取完成")

2. 多線程爬取單個關鍵詞,限制最大請求次數
通過request_count 設定爬取的請求次數。

def crawl_keyword(keyword: str, output_file: str, cookie_str: str, field: str, max_requests: int = 10):tiktok = TiktokUserSearch(output_file=output_file)has_more = 1cursor = '0'search_id = Nonerequest_count = 0  # 初始化請求計數器while has_more and request_count < max_requests:data = tiktok.main(keyword, field, cookie_str, cursor, search_id)logger.info(f"Request {request_count + 1}: {data}")if data and isinstance(data, dict):# has_more = data.get('has_more', 0)cursor = data.get('cursor', '0')search_id = data.get('log_pb', {}).get('impr_id')if 'data' in data:data = data['data']request_count += 1  # 更新請求計數else:logger.error("No data found in response")breakelse:logger.error("Invalid response format")breaktime.sleep(random.randint(0, 5))  # 隨機延時,避免請求過快write_csv(keyword, request_count, file_path='../results/records.csv')logger.info(f"爬取 {keyword} 的視頻完成,共請求 {request_count} 次")

3. 定義TiktokUserSearch類

允許獲得24類字段,包括:
🖥?視頻的URL、視頻時長、標題等;
👨視頻的發布者個人簡介、獲贊數據、視頻數據等;
👍視頻的點贊信息、分享次數、評論數量、播放次數、收藏次數等;
🎶視頻的背景音樂ID,音樂來源等… …

class TiktokUserSearch:def __init__(self, output_file: Optional[str] = None):self.config = read_config()self.headers = self.config.get("headers", {})self.cookies = Noneself.output_file = output_file if output_file else f'tiktok_videos_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'self.proxies = self.config.get("proxies", None)  # 代理配置self.lock = threading.Lock()  # 線程鎖def cookie_str_to_dict(self, cookie_str: str) -> Dict[str, str]:"""將cookie字符串轉換為字典"""cookie_dict = {}try:cookies = [i.strip() for i in cookie_str.split('; ') if i.strip() != ""]for cookie in cookies:key, value = cookie.split('=', 1)cookie_dict[key] = valueexcept Exception as e:logger.error(f"轉換cookie時出錯: {str(e)}")raisereturn cookie_dictdef get(self, keyword: str, cursor: str, search_id: Optional[str], cookie_str: str) -> Dict[str, Any]:"""發送請求并獲取數據"""self.cookies = self.cookie_str_to_dict(cookie_str)url = "https://www.tiktok.com/api/search/general/full/"focus_state = "true" if cursor == "0" else "false"params = {"WebIdLastTime": f"{int(time.time())}","aid": "1988","app_language": "zh-Hans","app_name": "tiktok_web","browser_language": "zh-CN",# ... 略"webcast_language": "zh-Hans","msToken": self.cookies["msToken"],}if cursor != "0":params.update({"search_id": search_id})try:x_b = execjs.compile(open('../configs/encrypt.js', encoding='utf-8').read()).call("sign", urlencode(params),self.headers["user-agent"])params.update({"X-Bogus": x_b})except Exception as e:logger.error(f"生成X-Bogus時出錯: {str(e)}")return {"error": str(e)}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=self.proxies)response.raise_for_status()return response.json()except (ex1, ex2, ex3) as e:logger.warning(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:logger.error(f"發生其他錯誤:{e}")return {"error": str(e)}def parse_data(self, data_list: List[Dict[str, Any]], keyword: str, field: str) -> List[str]:"""解析數據并保存到json文件"""resultList = []video_data = []for u in data_list:try:item = u['item']author = item['author']stats = item['stats']author_stats = item['authorStats']video_id = str(item['id']),  # 視頻的唯一標識符(TikTok 視頻 ID)author_name = str(author['uniqueId']),  # 作者的 TikTok 賬號video_url = f'https://www.tiktok.com/@{author_name[0]}/video/{video_id[0]}'video_info = {'search_keyword': keyword,'video_field': field,'video_id': video_id[0],  # 視頻的唯一標識符(TikTok 視頻 ID)'desc': item['desc'],  # 視頻的文字描述(caption/標題)'create_time': datetime.fromtimestamp(item['createTime']).strftime('%Y-%m-%d %H:%M:%S'),  # 視頻的發布時間'duration': item['video']['duration'],  # 視頻時長(單位:秒)'video_url': video_url,  # 視頻播放地址'author_id': author['id'],  # 作者的唯一 ID'author_name': author_name[0],  # 作者的 TikTok 賬號(uniqueId,即用戶名)#... 略'author_following_count': author_stats['followingCount'],  # 作者關注的人數'digg_count': stats['diggCount'],  # 視頻的點贊(like)數量'share_count': stats['shareCount'],  # 視頻的分享次數'comment_count': stats['commentCount'],  # 視頻的評論數量'play_count': stats['playCount'],  # 視頻的播放次數'collect_count': stats.get('collectCount', 0),  # 視頻的收藏次數}# video_info['comments'] = self.get_comments(video_url)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# **追加寫入 JSON 文件**try:# 如果文件存在,讀取已有數據if os.path.exists(self.output_file):with open(self.output_file, 'r', encoding='utf-8') as f:try:existing_data = json.load(f)except json.JSONDecodeError:existing_data = []  # 如果 JSON 解析失敗,重置為空列表else:existing_data = []# 追加新數據existing_data.extend(video_data)# 保存回 JSON 文件with open(self.output_file, 'w', encoding='utf-8') as f:json.dump(existing_data, f, ensure_ascii=False, indent=4)logger.info(f"數據已{'追加' if existing_data else '保存'}到文件: {self.output_file}")except Exception as e:logger.error(f"保存 JSON 文件時出錯: {str(e)}")return resultListdef main(self, keyword: str, field: str, cookie_str: str, cursor: str = "0", search_id: Optional[str] = None) -> Dict[str, Any]:"""主函數,執行搜索并解析數據"""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:if 'data' in dataJson:self.parse_data(dataJson['data'], keyword, field)return dataJson

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900643.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900643.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900643.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【愚公系列】《高效使用DeepSeek》058-選題策劃

??【技術大咖愚公搬代碼:全棧專家的成長之路,你關注的寶藏博主在這里!】?? ??開發者圈持續輸出高質量干貨的"愚公精神"踐行者——全網百萬開發者都在追更的頂級技術博主! ?? 江湖人稱"愚公搬代碼",用七年如一日的精神深耕技術領域,以"…

零基礎教程:Windows電腦安裝Linux系統(雙系統/虛擬機)全攻略

一、安裝方式選擇 方案對比表 特性雙系統安裝虛擬機安裝性能原生硬件性能依賴宿主機資源分配磁盤空間需要獨立分區&#xff08;建議50GB&#xff09;動態分配&#xff08;默認20GB起&#xff09;內存占用獨占全部內存需手動分配&#xff08;建議4GB&#xff09;啟動方式開機選…

LeetCode 2968.執行操作使頻率分數最大

給你一個下標從 0 開始的整數數組 nums 和一個整數 k 。 你可以對數組執行 至多 k 次操作&#xff1a; 從數組中選擇一個下標 i &#xff0c;將 nums[i] 增加 或者 減少 1 。 最終數組的頻率分數定義為數組中眾數的 頻率 。 請你返回你可以得到的 最大 頻率分數。 眾數指的…

excel經驗

Q:我現在有一個excel&#xff0c;有一列數據&#xff0c;大概兩千多行。如何在這一列中 篩選出具有關鍵字的內容&#xff0c;并輸出到另外一列中。 A: 假設數據在A列&#xff08;A1開始&#xff09;&#xff0c;關鍵字為“ABC”在相鄰空白列&#xff08;如B1&#xff09;輸入公…

HTTP查詢參數示例(XMLHttpRequest查詢參數)(帶查詢參數的HTTP接口示例——以python flask接口為例)flask查詢接口

文章目錄 HTTP查詢參數請求示例接口文檔——獲取城市列表代碼示例效果 帶查詢參數的HTTP接口示例——以python flask接口為例app.pyREADME.md運行應用API示例客戶端示例關鍵實現說明&#xff1a;運行方法&#xff1a; HTTP查詢參數請求示例 接口文檔——獲取城市列表 代碼示例…

將飛帆制作的網頁作為 div 集成到自己的網頁中

并且自己的網頁可以和飛帆中的控件相互調用函數。效果&#xff1a; 上鏈接 將飛帆制作的網頁作為 div 集成到自己的網頁中 - 文貝 進入可以復制、運行代碼

Redis主從復制:告別單身Redis!

目錄 一、 為什么需要主從復制&#xff1f;&#x1f914;二、 如何搭建主從架構&#xff1f;前提條件?步驟&#x1f4c1; 創建工作目錄&#x1f4dc; 創建 Docker Compose 配置文件&#x1f680; 啟動所有 Redis&#x1f50d; 驗證主從狀態 &#x1f4a1; 重要提示和后續改進 …

k8s 1.30.6版本部署(使用canal插件)

#系統環境準備 參考 https://blog.csdn.net/dingzy1/article/details/147062698?spm1001.2014.3001.5501 #配置下載源 curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/Release.key |gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyri…

機器學習的一百個概念(7)獨熱編碼

前言 本文隸屬于專欄《機器學習的一百個概念》&#xff0c;該專欄為筆者原創&#xff0c;引用請注明來源&#xff0c;不足和錯誤之處請在評論區幫忙指出&#xff0c;謝謝&#xff01; 本專欄目錄結構和參考文獻請見[《機器學習的一百個概念》 ima 知識庫 知識庫廣場搜索&…

RHCSA復習

在Linux中&#xff0c; wrx 分別代表寫&#xff08;write&#xff09;、讀&#xff08;read&#xff09;和執行&#xff08;execute&#xff09;權限&#xff0c;它們對應的權限值分別是&#xff1a; - r &#xff08;讀權限&#xff09;&#xff1a;權限值為4。 - w &am…

“樂企“平臺如何重構業財稅票全流程生態?

2025年&#xff0c;國家稅務總局持續推進的"便民辦稅春風行動"再次推進數字化服務升級&#xff0c;其中"樂企"平臺作為稅務信息化的重要載體&#xff0c;持續優化數電票服務能力&#xff0c;為企業提供更高效、更規范的稅務管理支持。在這一背景下&#xf…

Android audio(6)-audiopolicyservice介紹

AudioPolicyService 是策略的制定者&#xff0c;比如某種 Stream 類型不同設備的音量&#xff08;index/DB&#xff09;是多少、某種 Stream 類型的音頻數據流對應什么設備等等。而 AudioFlinger 則是策略的執行者&#xff0c;例如具體如何與音頻設備通信&#xff0c;維護現有系…

Boost庫搜索引擎項目(版本1)

Boost庫搜索引擎 項目開源地址 Github&#xff1a;https://github.com/H0308/BoostSearchingEngine Gitee&#xff1a;https://gitee.com/EPSDA/BoostSearchingEngine 版本聲明 當前為最初版本&#xff0c;后續會根據其他內容對當前項目進行修改&#xff0c;具體見后續版本…

git分支合并信息查看

TortoiseGit工具 1、選擇"Revision graph" 2、勾選view中的 Show branchings and merges Arrows point towards merges 3、圖案說明 紅色部分?&#xff1a;代表當前分支 橙色部分?&#xff1a;代表遠程分支 黃色部分?&#xff1a;代表一個tag 綠色部分?&#xf…

Java學習筆記(多線程):ReentrantLock 源碼分析

本文是自己的學習筆記&#xff0c;主要參考資料如下 JavaSE文檔 1、AQS 概述1.1、鎖的原理1.2、任務隊列1.2.1、結點的狀態變化 1.3、加鎖和解鎖的簡單流程 2、ReentrantLock2.1、加鎖源碼分析2.1.1、tryAcquire()的具體實現2.1.2、acquirQueued()的具體實現2.1.3、tryLock的具…

在C++11及后續標準中,auto和decltype是用于類型推導的關鍵特性,它們的作用和用法。

在C11及后續標準中&#xff0c;auto和decltype是用于類型推導的關鍵特性&#xff0c;它們的作用和用法有所不同。以下是詳細說明&#xff1a; 1. auto 關鍵字 基本作用 自動推導變量的類型&#xff08;根據初始化表達式&#xff09;主要用于簡化代碼&#xff0c;避免顯式書寫…

Linux:進程程序替換execl

目錄 引言 1.單進程版程序替換 2.程序替換原理 3.6種替換函數介紹 3.1 函數返回值 3.2 命名理解 3.3 環境變量參數 引言 用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支)&#xff0c;我們所創建的所有的子進程&#xff0c;執行的代碼&#x…

LeetCode.02.04.分割鏈表

分割鏈表 給你一個鏈表的頭節點 head 和一個特定值 x &#xff0c;請你對鏈表進行分隔&#xff0c;使得所有 小于 x 的節點都出現在 大于或等于 x 的節點之前。 你不需要 保留 每個分區中各節點的初始相對位置。 示例 1&#xff1a; 輸入&#xff1a;head [1,4,3,2,5,2], x …

Johnson算法 流水線問題 java實現

某印刷廠有 6項加工任務J1&#xff0c;J2&#xff0c;J3&#xff0c;J4&#xff0c;J5&#xff0c;J6&#xff0c;需要在兩臺機器Mi和M2上完 成。 在機器Mi上各任務所需時間為5,1,8,5,3,4單位; 在機器M2上各任務所需時間為7,2,2,4,7,4單位。 即時間矩陣為&#xff1a; T1 {5, …

按鍵++,--在操作uint8_t類型(一個取值為1~10的數)中,在LCD中顯示兩位數字問題

問題概況 在執行按鍵&#xff0c;--過程中&#xff0c;本來數值為1~10.但是在執行過程中&#xff0c;發現數值在經過10數值后&#xff0c;后面的“0”會一直在LCD顯示屏中顯示。 就是執行操作中&#xff0c;從1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xf…