channel-worker 2.5.77 → 2.5.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -48,13 +48,35 @@ async function findReelsTabHref(page) {
|
|
|
48
48
|
// ở mọi tab của page/profile) — không tốn thêm lượt mở profile nào. Ô
|
|
49
49
|
// platform_accounts.stats.followers có sẵn từ lâu nhưng trống 279/279 kênh vì
|
|
50
50
|
// chưa từng có ai ghi vào.
|
|
51
|
+
//
|
|
52
|
+
// KHÔNG quét cả body rồi lấy match ĐẦU TIÊN: đo thật 2026-09-05 trên kênh
|
|
53
|
+
// "Đồ chơi trẻ em 01" ra đúng "1" — trong trang có sẵn chuỗi "… 1 người theo
|
|
54
|
+
// dõi" ở chỗ khác, nó chặn trước số thật của header. Thứ tự ưu tiên:
|
|
55
|
+
// 1. link /followers của chính header (nguồn chắc nhất)
|
|
56
|
+
// 2. nếu không có, quét phần ĐẦU trang (header nằm ở đây) và lấy số LỚN NHẤT
|
|
57
|
+
// Trả về cả danh sách ứng viên để log soi được khi Facebook đổi giao diện.
|
|
58
|
+
const FOLLOWER_RE = /([\d][\d.,]*\s*(?:N|Tr|K|M|B|nghìn|triệu|tỷ)?)\s*(?:người theo dõi|người follow|followers)/gi;
|
|
59
|
+
|
|
51
60
|
async function scrapeFollowers(page) {
|
|
52
|
-
return page.evaluate(() => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
return page.evaluate((reSrc) => {
|
|
62
|
+
const out = { picked: null, from: '', candidates: [] };
|
|
63
|
+
const mk = () => new RegExp(reSrc, 'gi');
|
|
64
|
+
|
|
65
|
+
// 1. Link "N người theo dõi" trong header — href chứa /followers.
|
|
66
|
+
for (const a of document.querySelectorAll("a[href*='followers']")) {
|
|
67
|
+
const t = (a.innerText || '').replace(/\s+/g, ' ').trim();
|
|
68
|
+
const m = mk().exec(t);
|
|
69
|
+
if (m) { out.picked = m[1].trim(); out.from = 'link'; out.candidates.push(t.slice(0, 60)); return out; }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 2. Phần đầu trang: header đứng trên cùng, nên cắt bớt đuôi để tránh vớ
|
|
73
|
+
// phải chuỗi trong danh sách gợi ý/bình luận phía dưới.
|
|
74
|
+
const head = (document.body?.innerText || '').slice(0, 3000);
|
|
75
|
+
let m; const re = mk();
|
|
76
|
+
while ((m = re.exec(head))) out.candidates.push(m[1].trim());
|
|
77
|
+
if (out.candidates.length) { out.from = 'head'; out.picked = out.candidates.slice(); }
|
|
78
|
+
return out;
|
|
79
|
+
}, FOLLOWER_RE.source).catch(() => ({ picked: null, from: 'error', candidates: [] }));
|
|
58
80
|
}
|
|
59
81
|
|
|
60
82
|
async function scrapeTiles(page) {
|
|
@@ -104,9 +126,12 @@ async function run({ page, payload, log }) {
|
|
|
104
126
|
// lượt đọc view, hai việc độc lập nhau.
|
|
105
127
|
let followers = null;
|
|
106
128
|
if (mode === 'deep') {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
const f = await scrapeFollowers(page);
|
|
130
|
+
// Từ link header thì tin thẳng; quét phần đầu trang thì lấy số LỚN NHẤT
|
|
131
|
+
// trong các ứng viên — chuỗi nhiễu ("1 người theo dõi") luôn thua số thật.
|
|
132
|
+
const nums = (Array.isArray(f.picked) ? f.picked : [f.picked]).map(parseViews).filter((n) => Number.isFinite(n) && n > 0);
|
|
133
|
+
followers = nums.length ? Math.max(...nums) : null;
|
|
134
|
+
log('info', `[fb-stats] follower: ${followers ?? 'không đọc được'} (nguồn ${f.from || 'none'}, ứng viên: ${JSON.stringify(f.candidates).slice(0, 200)})`);
|
|
110
135
|
}
|
|
111
136
|
|
|
112
137
|
// Cuộn để tải thêm tile (mỗi lần ~12 reel). 6 lần ≈ 60-80 reel gần nhất — đủ
|