Back to Home
Gistify Snippet
Public
Scraper ytstalk
Scraper ytstalk
#github-gist#javascript#gist:24b1696bd5a386d490f22dedf34fb5b2#gistsync:2026-09-02T12:40:43Z
2
Rawytstalk.jsjavascript
128 lines| 1 | |
| 2 | |
| 3 | import * as __ns_873 from 'axios' |
| 4 | const axios = __ns_873.default |
| 5 | import * as __ns_874 from 'node:url' |
| 6 | const pathToFileURL = __ns_874.pathToFileURL |
| 7 | |
| 8 | async function stalkYT(query) { |
| 9 | if (!query) throw new Error('Masukkan username/handle YouTube.') |
| 10 | const clean = query |
| 11 | .trim() |
| 12 | .replace(/^https?:\/\/(www\.)?youtube\.com\//i, '') |
| 13 | .replace(/^@/, '') |
| 14 | .split(/[/?#]/)[0] |
| 15 | |
| 16 | const url = `https://www.youtube.com/@${clean}` |
| 17 | |
| 18 | try { |
| 19 | const { data: html } = await axios.get(url, { |
| 20 | headers: { |
| 21 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', |
| 22 | 'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8' |
| 23 | }, |
| 24 | timeout: 15000 |
| 25 | }) |
| 26 | |
| 27 | const m = html.match(/ytInitialData\s*=\s*({.+?});<\/script>/) |
| 28 | if (!m) throw new Error('Gagal membaca struktur halaman YouTube.') |
| 29 | const data = JSON.parse(m[1]) |
| 30 | const meta = data.metadata?.channelMetadataRenderer || {} |
| 31 | const header = data.header?.pageHeaderRenderer || data.header?.c4TabbedHeaderRenderer || {} |
| 32 | |
| 33 | const name = meta.title || clean |
| 34 | const bio = meta.description || 'Tidak ada deskripsi.' |
| 35 | const avatar = meta.avatar?.thumbnails?.slice(-1)[0]?.url || header?.content?.pageHeaderViewModel?.image?.decoratedAvatarViewModel?.avatar?.avatarViewModel?.image?.sources?.slice(-1)[0]?.url || `https://avatar.iran.liara.run/public/boy?username=${encodeURIComponent(clean)}` |
| 36 | const channelUrl = meta.channelUrl || `https://www.youtube.com/@${clean}` |
| 37 | |
| 38 | let subscribers = '-' |
| 39 | let videos = '-' |
| 40 | |
| 41 | const rows = header?.content?.pageHeaderViewModel?.metadata?.contentMetadataViewModel?.metadataRows || [] |
| 42 | for (const r of rows) { |
| 43 | for (const part of r.metadataParts || []) { |
| 44 | const txt = part.text?.content || '' |
| 45 | if (/subscriber|pelanggan/i.test(txt)) subscribers = txt |
| 46 | if (/video/i.test(txt)) videos = txt |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | return { |
| 51 | name, |
| 52 | username: clean, |
| 53 | bio, |
| 54 | avatar, |
| 55 | subscribers, |
| 56 | videos, |
| 57 | url: channelUrl |
| 58 | } |
| 59 | } catch (err) { |
| 60 | const searchUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}&sp=EgIQAg%253D%253D` |
| 61 | const { data: html2 } = await axios.get(searchUrl, { |
| 62 | headers: { |
| 63 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' |
| 64 | }, |
| 65 | timeout: 15000 |
| 66 | }) |
| 67 | const m2 = html2.match(/ytInitialData\s*=\s*({.+?});<\/script>/) |
| 68 | if (!m2) throw new Error(`Channel YouTube "@${clean}" tidak ditemukan.`) |
| 69 | const data2 = JSON.parse(m2[1]) |
| 70 | const channelRenderer = data2.contents?.twoColumnSearchResultsRenderer?.primaryContents?.sectionListRenderer?.contents?.[0]?.itemSectionRenderer?.contents?.find(c => c.channelRenderer)?.channelRenderer |
| 71 | |
| 72 | if (!channelRenderer) throw new Error(`Channel YouTube "@${clean}" tidak ditemukan.`) |
| 73 | |
| 74 | return { |
| 75 | name: channelRenderer.title?.simpleText || clean, |
| 76 | username: clean, |
| 77 | bio: channelRenderer.descriptionSnippet?.runs?.map(r => r.text).join('') || 'Tidak ada deskripsi.', |
| 78 | avatar: channelRenderer.thumbnail?.thumbnails?.slice(-1)[0]?.url || '', |
| 79 | subscribers: channelRenderer.videoCountText?.simpleText || channelRenderer.subscriberCountText?.simpleText || '-', |
| 80 | videos: channelRenderer.videoCountText?.simpleText || '-', |
| 81 | url: `https://www.youtube.com/${channelRenderer.navigationEndpoint?.commandMetadata?.webCommandMetadata?.url || '@' + clean}` |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | const __dflt_36 = { stalkYT } |
| 87 | |
| 88 | if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { |
| 89 | const __exports = { stalkYT: stalkYT, default: __dflt_36 } |
| 90 | const __fns = Object.fromEntries(Object.entries(__exports).filter(([, v]) => typeof v === 'function')) |
| 91 | const __names = Object.keys(__fns) |
| 92 | if (__names.length === 0) { |
| 93 | console.log('Tidak ada fungsi yang bisa di-demo. Import modul ini dari kode lain.') |
| 94 | } else { |
| 95 | const __search = __names.find((n) => /search|cari/i.test(n)) |
| 96 | const __name = __search || __names[0] |
| 97 | const __fn = __fns[__name] |
| 98 | const __isClass = /^class\s/.test(Function.prototype.toString.call(__fn)) |
| 99 | const __defaultArgs = (() => { |
| 100 | const n = __name.toLowerCase() |
| 101 | if (/emoji/.test(n)) return [{ emoji: '๐๐' }] |
| 102 | if (/textpro|generate.*text|effect/.test(n)) return ['neon', 'Hello'] |
| 103 | if (/image|wallpaper|search|query|anime|movie|manga|news|lyrics|wiki|quote|stalk|detail/.test(n)) return ['naruto'] |
| 104 | if (/download|youtube|video|audio|music|spotify|tiktok|instagram|facebook|twitter|reels|terabox|media/.test(n)) return ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'] |
| 105 | if (/dork/.test(n)) return ['example.com', 'documents'] |
| 106 | if (/tts|speech|prompt|chat|ask|translate/.test(n)) return ['Hello world'] |
| 107 | if (/parse|format|normalize|list|features/.test(n)) return [] |
| 108 | return ['test'] |
| 109 | })() |
| 110 | let __args = __defaultArgs |
| 111 | if (process.argv.length > 2) __args = process.argv.slice(2).map((a) => { try { return JSON.parse(a) } catch { return a } }) |
| 112 | if (__args.length === 0 && !__search && !__isClass && __fn.length > 0) { |
| 113 | console.log('Fungsi: ' + __names.join(', ')) |
| 114 | console.log('Contoh: node ' + "ytstalk.js" + ' ' + __name + ' <arg>') |
| 115 | } else { |
| 116 | ;(async () => { |
| 117 | try { |
| 118 | const r = __isClass ? new __fn(...__args) : await __fn(...__args) |
| 119 | console.log(JSON.stringify(r, null, 2)) |
| 120 | } catch (e) { |
| 121 | console.error('Error: ' + (e && e.message ? e.message : e)) |
| 122 | process.exitCode = 1 |
| 123 | } |
| 124 | })() |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
Discussion & Comments0
No comments yet โ be the first to leave feedback or start a code discussion.
Sign in with your GitHub account to write comments and join the discussion.