UNPKG

@xbibzlibrary/tiktokscrap

Version:

Powerful TikTok Scraper and Downloader Library

122 lines (96 loc) 4.08 kB
import BaseScraper from './base'; import { TikTokHashtag, TikTokVideo, TikTokPhoto, TikTokScrapOptions, TikTokScrapResult, TikTokHashtagFeedOptions } from '../types'; import { ValidationError, NotFoundError } from '../errors'; export class HashtagScraper extends BaseScraper { constructor(options: TikTokScrapOptions = {}) { super(options); } public async getHashtagByName(hashtag: string): Promise<TikTokScrapResult<TikTokHashtag>> { return this.executeRequest(async () => { if (!this.validator.validateTikTokHashtag(hashtag)) { throw new ValidationError('Invalid TikTok hashtag'); } // Remove # if present const cleanHashtag = hashtag.startsWith('#') ? hashtag.substring(1) : hashtag; const url = this.buildUrl('https://www.tiktok.com', `/tag/${cleanHashtag}`); const response = await this.http.get(url); if (response.status !== 200) { throw new NotFoundError('Hashtag not found'); } return this.parser.parseHashtagData(response.data); }, `Get hashtag by name: ${hashtag}`); } public async getHashtagFeed(options: TikTokHashtagFeedOptions): Promise<TikTokScrapResult<TikTokVideo[] | TikTokPhoto[]>> { return this.executeRequest(async () => { this.validator.validateHashtagFeedOptions(options); const { hashtag, cursor = 0, count = 20 } = options; // First, get hashtag info to extract id const hashtagResult = await this.getHashtagByName(hashtag); if (!hashtagResult.success || !hashtagResult.data) { throw new NotFoundError('Hashtag not found'); } const url = this.buildUrl('https://www.tiktok.com', '/api/challenge/item_list/', { count, id: hashtagResult.data.id, type: 3, // 3 for hashtag secUid: '', maxCursor: cursor, minCursor: 0, retryType: 0, isWeb: 1 }); const response = await this.http.get(url); if (response.status !== 200) { throw new NotFoundError('Could not fetch hashtag feed'); } const data = response.data; if (!data.body || !data.body.itemListData) { return []; } return data.body.itemListData.map((item: any) => { // Check if it's a video or photo if (item.video) { return this.parser.parseVideoObject(item); } else if (item.images) { return this.parser.parsePhotoObject(item); } throw new ValidationError('Unknown item type in hashtag feed'); }); }, `Get hashtag feed for ${options.hashtag}: ${options.count}`); } public async getHashtagVideos(hashtag: string, cursor: number = 0, count: number = 20): Promise<TikTokScrapResult<TikTokVideo[]>> { return this.getHashtagFeed({ hashtag, cursor, count }) as Promise<TikTokScrapResult<TikTokVideo[]>>; } public async getHashtagPhotos(hashtag: string, cursor: number = 0, count: number = 20): Promise<TikTokScrapResult<TikTokPhoto[]>> { return this.getHashtagFeed({ hashtag, cursor, count }) as Promise<TikTokScrapResult<TikTokPhoto[]>>; } public async getTrendingHashtags(count: number = 20): Promise<TikTokScrapResult<TikTokHashtag[]>> { return this.executeRequest(async () => { if (count <= 0 || count > 100) { throw new ValidationError('Count must be between 1 and 100'); } const url = this.buildUrl('https://www.tiktok.com', '/api/challenge/discover/', { count, isWeb: 1 }); const response = await this.http.get(url); if (response.status !== 200) { throw new NotFoundError('Could not fetch trending hashtags'); } const data = response.data; if (!data.body || !data.body.challengeList) { return []; } return data.body.challengeList.map((item: any) => this.parser.parseHashtagObject(item)); }, `Get trending hashtags: ${count}`); } } export default HashtagScraper;