Skip to content

Instantly share code, notes, and snippets.

@kiemrong08
Last active April 17, 2025 05:30
Show Gist options
  • Save kiemrong08/eec5a1bf9bf37a68170d8065f1e6a38a to your computer and use it in GitHub Desktop.
Save kiemrong08/eec5a1bf9bf37a68170d8065f1e6a38a to your computer and use it in GitHub Desktop.
get facebook page post
// Script lấy nội dung bài viết Facebook (phiên bản tránh xung đột)
// Đóng gói tất cả trong một IIFE (Immediately Invoked Function Expression)
// để tránh xung đột với các biến và hàm toàn cục đã tồn tại
(async function() {
// Cấu hình
const CONFIG = {
scrollDelay: 1500, // Thời gian chờ sau mỗi lần cuộn (ms)
maxScrollAttempts: 10, // Số lần cuộn tối đa nếu không tìm thấy bài mới
waitAfterClick: 500, // Thời gian chờ sau khi click "Xem thêm" (ms)
scrollStep: true // Cuộn theo từng bước hay cuộn đến cuối trang
};
// Tạo biến lưu trữ mới mỗi khi chạy script
const postsArray = [];
// Hàm trì hoãn (promise)
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
// Hàm thu thập dữ liệu bài post
function collectPosts() {
// Cách 1: Sử dụng selector data-ad-rendering-role="story_message"
const postElements = document.querySelectorAll('div[data-ad-rendering-role="story_message"]');
postElements.forEach(post => {
// Lấy ID/link của bài viết để phân biệt
const postLink = post.querySelector('a[href*="/posts/"]')?.href || '';
if (postsArray.some(p => p.postLink === postLink) && postLink) {
return; // Bỏ qua nếu đã thu thập và có link
}
// Lấy nội dung chính từ story_message
let content = post.innerText.trim();
// Nếu không lấy được nội dung từ cách trên, thử cách khác
if (!content) {
const textElements = post.querySelectorAll('div[dir="auto"][style*="text-align: start"]');
content = Array.from(textElements)
.map(el => el.innerText.trim())
.filter(Boolean)
.join('\n');
}
// Lấy các thông tin bổ sung
const timeElement = post.querySelector('abbr')?.title || post.querySelector('abbr')?.innerText || '';
const reactions = post.querySelector('span[data-testid="UFI2ReactionsCount/root"]')?.innerText || '';
const comments = post.querySelector('span[data-testid="UFI2CommentsCount/root"]')?.innerText || '';
const shares = post.querySelector('span[data-testid="UFI2SharesCount/root"]')?.innerText || '';
// Lấy hình ảnh nếu có
const images = Array.from(post.querySelectorAll('img[src*="fbcdn.net"]'))
.map(img => img.src)
.filter(src => !src.includes('emoji') && !src.includes('sticker'))
.filter((value, index, self) => self.indexOf(value) === index); // Loại bỏ trùng lặp
// Thêm vào mảng nếu có nội dung
if (content && !postsArray.some(p => p.content === content)) {
const postId = postLink.split('/posts/')[1]?.split('/')[0] || `post-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
postsArray.push({
id: postId,
content,
postLink,
time: timeElement,
reactions,
comments,
shares,
images,
extractedAt: new Date().toISOString()
});
console.log(`Đã thu thập bài viết: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`);
}
});
// Cách 2: Sử dụng selector data-ad-comet-preview="message" hoặc data-ad-preview="message"
const messageContainers = document.querySelectorAll('div[data-ad-comet-preview="message"], div[data-ad-preview="message"]');
messageContainers.forEach(container => {
try {
// Lấy ID của container hoặc tạo ID mới nếu không có
const containerId = container.id || '';
// Bỏ qua nếu đã xử lý (kiểm tra bằng ID)
if (postsArray.some(p => p.id === containerId) && containerId) {
return;
}
// Tìm nội dung trong container
const contentDivs = container.querySelectorAll('div[dir="auto"][style*="text-align:start"]');
let content = '';
contentDivs.forEach(div => {
content += div.textContent.trim() + '\n';
});
content = content.trim();
// Đảm bảo không lấy các bài trùng lặp
if (content && !postsArray.some(p => p.content === content)) {
// Tìm các thông tin bổ sung trong các phần tử lân cận
let parentNode = container;
for (let i = 0; i < 5; i++) {
parentNode = parentNode.parentNode;
if (!parentNode) break;
}
// Tìm thời gian, reactions, comments từ parentNode
const timeElement = parentNode?.querySelector('abbr')?.title || parentNode?.querySelector('abbr')?.innerText || '';
const reactions = parentNode?.querySelector('span[data-testid="UFI2ReactionsCount/root"]')?.innerText || '';
const comments = parentNode?.querySelector('span[data-testid="UFI2CommentsCount/root"]')?.innerText || '';
const shares = parentNode?.querySelector('span[data-testid="UFI2SharesCount/root"]')?.innerText || '';
// Tìm các hình ảnh
const images = Array.from(parentNode?.querySelectorAll('img[src*="fbcdn.net"]') || [])
.map(img => img.src)
.filter(src => !src.includes('emoji') && !src.includes('sticker'))
.filter((value, index, self) => self.indexOf(value) === index);
// Tìm link bài viết
const postLink = parentNode?.querySelector('a[href*="/posts/"]')?.href || '';
// Thêm vào mảng kết quả
postsArray.push({
id: containerId || `post-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
content,
postLink,
time: timeElement,
reactions,
comments,
shares,
images,
extractedAt: new Date().toISOString()
});
console.log(`Đã thu thập bài viết (cách 2): ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`);
}
} catch (error) {
console.warn('Lỗi khi trích xuất bài viết:', error);
}
});
// Loại bỏ các bài viết trùng lặp dựa trên nội dung
const uniquePosts = [];
const contentSet = new Set();
for (const post of postsArray) {
if (!contentSet.has(post.content)) {
contentSet.add(post.content);
uniquePosts.push(post);
}
}
// Cập nhật mảng posts
postsArray.length = 0;
uniquePosts.forEach(post => postsArray.push(post));
return postsArray.length;
}
// Hàm xử lý click "Xem thêm"
async function handleSeeMore() {
// Tìm tất cả nút "Xem thêm" trên trang
const seeMoreButtons = Array.from(document.querySelectorAll('div[role="button"][tabindex="0"]'))
.filter(btn => {
const text = btn.textContent.trim().toLowerCase();
return text === "xem thêm" || text === "see more";
});
let clickCount = 0;
for (const btn of seeMoreButtons) {
try {
btn.click();
clickCount++;
await delay(CONFIG.waitAfterClick);
} catch (err) {
console.warn('Lỗi khi click nút "Xem thêm":', err);
}
}
return clickCount;
}
// Hàm cuộn trang
async function scrollPage() {
if (CONFIG.scrollStep) {
// Cuộn từng bước
const currentPosition = window.scrollY;
window.scrollBy(0, window.innerHeight * 0.8);
await delay(CONFIG.scrollDelay);
// Kiểm tra xem đã cuộn được không
return window.scrollY > currentPosition;
} else {
// Cuộn đến cuối trang
const previousHeight = document.body.scrollHeight;
window.scrollTo(0, document.body.scrollHeight);
await delay(CONFIG.scrollDelay);
// Kiểm tra xem trang có tải thêm nội dung không
return document.body.scrollHeight > previousHeight;
}
}
// Hàm chính để lấy dữ liệu
async function scrapePostsFromFacebook() {
console.log('=== Bắt đầu thu thập dữ liệu bài viết Facebook ===');
console.log('Script đang chạy, vui lòng không sử dụng trang cho đến khi hoàn thành...');
let scrollAttempts = 0;
let previousPostCount = 0;
// Vòng lặp chính
while (scrollAttempts < CONFIG.maxScrollAttempts) {
// Click vào các nút "Xem thêm" trước khi thu thập
const clickedButtons = await handleSeeMore();
// Thu thập bài viết
const currentPostCount = collectPosts();
console.log(`Tổng số bài viết hiện tại: ${postsArray.length}, đã click ${clickedButtons} nút "Xem thêm"`);
// Cuộn xuống để tải thêm nội dung
const scrolled = await scrollPage();
// Kiểm tra xem có bài viết mới không
if (postsArray.length === previousPostCount || !scrolled) {
scrollAttempts++;
console.log(`Không có bài viết mới, lần thử ${scrollAttempts}/${CONFIG.maxScrollAttempts}`);
} else {
scrollAttempts = 0; // Reset nếu tìm thấy bài viết mới
}
previousPostCount = postsArray.length;
}
console.log(`\n=== Hoàn thành! Đã thu thập được ${postsArray.length} bài viết ===`);
// Thêm thông tin tổng hợp
const result = {
posts: postsArray,
timestamp: new Date().toISOString(),
pageUrl: window.location.href,
totalPosts: postsArray.length
};
return result;
}
// Hàm tạo file JSON để download
const downloadResults = (data) => {
const dataStr = JSON.stringify(data, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const pageName = window.location.pathname.split('/')[1] || 'facebook';
a.setAttribute('href', url);
a.setAttribute('download', `${pageName}_posts_${new Date().toISOString().replace(/[:.]/g, '-')}.json`);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
// Hiển thị thống kê ngắn gọn
function displayStats(data) {
console.log("\n=== THỐNG KÊ BÀI VIẾT ===");
console.log(`Tổng số bài viết: ${data.posts.length}`);
// Hiển thị thông tin một số bài viết đầu tiên
const previewCount = Math.min(5, data.posts.length);
console.log(`\nXem trước ${previewCount} bài đầu tiên:`);
for (let i = 0; i < previewCount; i++) {
const post = data.posts[i];
console.log(`\nBài ${i + 1}:`);
console.log(`Nội dung: ${post.content.substring(0, 100)}${post.content.length > 100 ? '...' : ''}`);
console.log(`Link: ${post.postLink || 'Không có'}`);
console.log(`Thời gian: ${post.time || 'Không có'}`);
console.log(`Tương tác: ${post.reactions || '0'} reactions, ${post.comments || '0'} comments, ${post.shares || '0'} shares`);
console.log(`Số ảnh: ${post.images?.length || 0}`);
}
console.log('\nDữ liệu đã được lưu vào biến "facebookPostsData" trong console.');
console.log('Bạn có thể truy cập dữ liệu này hoặc tải xuống file JSON.');
}
// Chạy script và tải xuống kết quả
try {
console.log("Đang chạy script thu thập dữ liệu Facebook...");
const postsData = await scrapePostsFromFacebook();
console.log('Dữ liệu bài viết đã thu thập xong.');
displayStats(postsData);
downloadResults(postsData);
// Lưu dữ liệu vào biến toàn cục để người dùng có thể truy cập nếu cần
window.facebookPostsData = postsData;
console.log("\nQuá trình thu thập hoàn tất!");
console.log("Bạn có thể truy cập dữ liệu bằng biến 'facebookPostsData' trong console");
console.log("(Ví dụ: gõ 'facebookPostsData.posts[0]' để xem bài đầu tiên)");
return postsData;
} catch (error) {
console.error('Đã xảy ra lỗi khi thu thập dữ liệu:', error);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment