Last active
April 17, 2025 05:30
-
-
Save kiemrong08/eec5a1bf9bf37a68170d8065f1e6a38a to your computer and use it in GitHub Desktop.
get facebook page post
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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