从 Facebook 页面提取公共帖子,无需 API/APP 密钥/令牌/秘密

2024-03-15

提前澄清一下,我没有 Facebook 帐户,也无意创建一个帐户。另外,我想要实现的目标在我的国家和美国是完全合法的。

我不想使用 Facebook API 来获取 Facebook 页面的最新时间线帖子,而是直接向页面 URL 发送获取请求(例如这一页 https://www.facebook.com/pg/officialstackoverflow/posts/)并从 HTML 源代码中提取帖子。
(我想获取帖子的文字和创建时间。)

当我在 Web 控制台中运行此命令时:

document.getElementsByClassName('userContent')

我得到一个包含最新帖子文本的元素列表。

但我想从 Node.js 脚本中提取该信息。我可以使用像这样的无头浏览器很容易地做到这一点puppeteer或类似的,但这会产生大量不必要的开销。我真的很想要一种简单的方法,比如下载 HTML 代码,将其传递给 Cheerio 并使用 Cheerio 的类似 jQuery 的 API 来提取帖子。

这是我的尝试:

// npm i request cheerio request-promise-native
const rp = require('request-promise-native'); // requires installation of `request`
const cheerio = require('cheerio');

rp.get('https://www.facebook.com/pg/officialstackoverflow/posts/').then( postsHtml => {
    const $ = cheerio.load(postsHtml);

    const timeLinePostEls = $('.userContent');
    console.log(timeLinePostEls.html()); // should NOT be null
    const newestPostEl = timeLinePostEls.get(0);
    console.log(newestPostEl.html()); // should NOT be null
    const newestPostText = newestPostEl.text();
    console.log(newestPostText);
    //const newestPostTime = newestPostEl.parent(??).child('.livetimestamp').title;
    //console.log(newestPostTime);
}).catch(console.error);

很遗憾$('.userContent')不起作用。不过,我能够验证我正在查找的数据是否嵌入到该 HTML 代码中的某个位置。

但我真的无法想出一个好的正则表达式方法或类似的方法来提取该数据。

根据帖子内容,帖子中的 HTML 标签数量差异很大。

以下是包含一个链接的帖子的简单示例:

<div class="_5pbx userContent _3576" data-ft="&#123;&quot;tn&quot;:&quot;K&quot;&#125;"><p>We&#039;re proud to be named one of Built In NYC&#039;s Best Places to Work in 2019, ranking in the top 10 for Best Midsize Places to Work and top 3 (!) for Best Perks and Benefits. See what it took to make the list and check out our profile to see some of our job openings. <a href="https://l.facebook.com/l.php?u=https%3A%2F%2Fbit.ly%2F2H3Kbr2&amp;h=AT29h2HyDsEk0rHRWqJA-Fa4M1qi3nJT1NBi95othaR3qeFuFAMNiVS2Dgtv5KR5m0xqjw6kfwZdhZt0_D3UQT1Oel2UhxRql-KwkA1xqWvrql4u1jDhzrkGVT_XxoUd8_w8_fzLZzzhz23a8yPCK6IPfWKB76_CEFjG3b78y4dFJvY9Z08AYlR01dmi5_FvWVEVytkN-123u6alYE8pqL6Jb6dtIQUTWGXYJPaNMrtxkCUZniEVXEcILkwHGSuHqCTAarboyMP55F1vhYO3OAiVMkvjbN274fVq92YvbK3bi90bU9T-5ADWHDUJ-CwcofSBTW47chstQeY0n_UluD_rBIPLsfXVSnCtpRkR2kXi9zzHLnNeIYeNssv3i7UKS_f5Z2pnVT6xe3zJbNpB68doH1Z__I9nsTCNIyFyKx2VxabecoL03DIawbRrzBoxLAwzNPLACBjTkpEQhdVn4_wdAIjXRL4cLQDcZkLEoG_sspBgRePH23TFbNufQOBly-FNtLHnkUDO2Ca-FYvAGXpcu6J4B1aH3XFPB803lsz-GRdACyOFOgXDXJfwr4WtWzUHxfiOPULWiI43yI5L4aU6wYRhPjxua3RuRZ8oj9fXa1w4Jrht94Ue2wfKtz8" target="_blank" data-ft="&#123;&quot;tn&quot;:&quot;-U&quot;&#125;" rel="noopener nofollow" data-lynx-mode="async">http://*******/2H3Kbr2</a></p></div>

以更易读的形式格式化,它看起来有点像这样:

<div class="_5pbx userContent _3576" data-ft="&#123;&quot;tn&quot;:&quot;K&quot;&#125;">
    <p>
        We&#039;re proud to be named one of Built In NYC&#039;s Best Places to Work in 
        2019, ranking in the top 10 for Best Midsize Places to Work and top 3 (!) for 
        Best Perks and Benefits. See what it took to make the list and check out our 
        profile to see some of our job openings.
        <a href="VERY_LONG_URL.........." target="_blank" data-ft="&#123;&quot;tn&quot;:&quot;-U&quot;&#125;" rel="noopener nofollow" data-lynx-mode="async">SHORT_LINK.....</a>
    </p>
</div>

这个正则表达式seems https://regex101.com/r/EMIrnx/1/工作还可以,但我认为它不是很可靠:

/<div class="[^"]+ userContent [^"]+" data-ft="[^"]+">(.+?)<\/div>/g

例如,如果帖子包含另一个 div 元素,那么它将无法正常工作。除此之外,我无法知道使用这种方法创建帖子的时间/日期?

有什么想法可以相对可靠地提取最近 2-3 个帖子(包括创建日期/时间)吗?


好吧,我终于想通了。我希望这对其他人有用。该函数将提取 20 个最新帖子,包括创建时间:

// npm i request cheerio request-promise-native
const rp = require('request-promise-native'); // requires installation of `request`
const cheerio = require('cheerio');

function GetFbPosts(pageUrl) {
    const requestOptions = {
        url: pageUrl,
        headers: {
            'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:64.0) Gecko/20100101 Firefox/64.0'
        }
    };
    return rp.get(requestOptions).then( postsHtml => {
        const $ = cheerio.load(postsHtml);
        const timeLinePostEls = $('.userContent').map((i,el)=>$(el)).get();
        const posts = timeLinePostEls.map(post=>{
            return {
                message: post.html(),
                created_at: post.parents('.userContentWrapper').find('.timestampContent').html()
            }
        });
        return posts;
    });
}
GetFbPosts('https://www.facebook.com/pg/officialstackoverflow/posts/').then(posts=>{
    // Log all posts
    for (const post of posts) {
        console.log(post.created_at, post.message);
    }
});

由于 Facebook 消息可能具有复杂的格式,因此该消息不是纯文本,而是 HTML。但是您可以删除格式并通过替换来获取文本message: post.html() with message: post.text().

Edit:如果你想获取最新20个以上的帖子,那就更复杂了。前 20 个帖子在初始 html 页面上静态提供。以下所有帖子均通过 ajax 以 8 个帖子为一组进行检索。 可以这样实现:

// make sure your node.js version supports async/await (v10 and above should be fine)
// npm i request cheerio request-promise-native
const rp = require('request-promise-native'); // requires installation of `request`
const cheerio = require('cheerio');

class FbScrape {
    constructor(options={}) {
        this.headers = options.headers || {
            'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:64.0) Gecko/20100101 Firefox/64.0' // you may have to update this at some point
        };
    }

    async getPosts(pageUrl, limit=20) {
        const staticPostsHtml = await rp.get({ url: pageUrl, headers: this.headers });
        if (limit <= 20) {
            return this._parsePostsHtml(staticPostsHtml);
        } else {
            let staticPosts = this._parsePostsHtml(staticPostsHtml);
            const nextResultsUrl = this._getNextPageAjaxUrl(staticPostsHtml);
            const ajaxPosts = await this._getAjaxPosts(nextResultsUrl, limit-20);
            return staticPosts.concat(ajaxPosts);
        }
    }

    _parsePostsHtml(postsHtml) {
        const $ = cheerio.load(postsHtml);
        const timeLinePostEls = $('.userContent').map((i,el)=>$(el)).get();
        const posts = timeLinePostEls.map(post => {
            return {
                message: post.html(),
                created_at: post.parents('.userContentWrapper').find('.timestampContent').html()
            }
        });
        return posts;
    }

    async _getAjaxPosts(resultsUrl, limit=8, posts=[]) {
        const responseBody = await rp.get({ url: resultsUrl, headers: this.headers });
        const extractedJson = JSON.parse(responseBody.substr(9));
        const postsHtml = extractedJson.domops[0][3].__html;
        const newPosts = this._parsePostsHtml(postsHtml);
        const allPosts = posts.concat(newPosts);
        const nextResultsUrl = this._getNextPageAjaxUrl(postsHtml);
        if (allPosts.length+1 >= limit)
            return allPosts;
        else
            return await this._getAjaxPosts(nextResultsUrl, limit, allPosts);
    }

    _getNextPageAjaxUrl(html) {
        return 'https://www.facebook.com' + /"(\/pages_reaction_units\/more[^"]+)"/g.exec(html)[1].replace(/&amp;/g, '&') + '&__a=1';
    }
}

const fbScrape = new FbScrape();
const minimum = 28; // minimum number of posts to request (gets rounded up to 20, 28, 36, 44, 52, 60, 68 etc... because of page sizes (page1=20; all_following_pages=8)
fbScrape.getPosts('https://www.facebook.com/pg/officialstackoverflow/posts/', minimum).then(posts => { // get at least the 28 latest posts
    // Log all posts
    for (const post of posts) {
        console.log(post.created_at, post.message);
    }
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 Facebook 页面提取公共帖子,无需 API/APP 密钥/令牌/秘密 的相关文章

随机推荐

  • cv2.imshow() 在线程中调用时启动但不弹出窗口

    cv2 imshow 在线程中调用时不会弹出窗口 docker中出现了python3的图标 但没有窗口 没有绘图 另外 我需要半双工通信的功能 因此我不能只删除线程 如何正确显示螺纹内的图片 任何回应表示赞赏 背景 MacOS Catali
  • Flash Builder 中的 TODO 任务列表

    Flash Builder 支持任务列表吗 我正在测试 PHP 的 Flash Builder 我可以在 PHP 代码中看到我的 TODO 但在 AS 或 MXML 中看不到 我找到了这个插件 http www richinternet d
  • 创建数据模型的最佳实践[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 对于当前的项目 我正在创建一个数据模型 是否有任何来源可以找到良好数据模型的 最佳实践 好的意味着灵活
  • REST 服务的最佳用途是什么?

    我知道像 Facebook 这样的网站现在正在使用 REST 服务 但我想知道其他使用 REST 的应用程序 以及是否存在特定情况下使用 REST 比其他方法更合理 REST 与 CRUD 数据服务无关 是的 您可以使用 REST 来执行类
  • 有没有办法在 PHP 中追溯创建基于时间/节点的 UUID?

    大多数基于时间 类型 1 的 UUID 是使用当前时间创建的 我正在将 MySQL 数据库迁移到 cassandra 并希望为存档项目创建基于时间的 UUID 有人可以提供一个如何使用过去的时间段创建 1 类 UUID 的示例吗 所有 uu
  • 如何发现我创建的物化视图的基础查询?

    我在 Postgres 9 3 中创建了一个物化视图 但此后我丢失了创建它的底层 SELECT 查询 我想删除物化视图 重写查询以包含更多数据 然后创建一个同名但具有新基础查询的物化视图 Just SELECT pg get viewdef
  • 创建键绑定以运行上一个或最后一个 shell 命令

    我经常想快速重新运行我使用的最后一个 shell 命令 我知道您可以将焦点转移到终端 向上箭头并输入 但我认为一定有比这三个步骤更好的方法 The sendSequencevscode 中的命令变得越来越强大 因此我寻找一种方法来创建一个可
  • 有没有一种简单的方法可以将 vulture 与 django 一起使用?

    我正在尝试清理 django 中一些混乱的遗留代码 Vulture http pypi python org pypi vulture看起来是一个不错的选择 但它似乎不知道如何查找 urls py 引用的视图函数 这并不奇怪 因为大多数函数
  • HTML 中的 valign 与 text-align

    我无法通过以下代码找出 HTML 中 valign 与 text align 之间的区别 table width 500 border 0 tr td style background color FFA500 h1 Main Title
  • 在解组操作期间将 nil="true" 转换为 null

    我从服务器接收 XML 该服务器的架构将几乎每个元素指定为
  • Java:jsr166y Phaser 的教程/说明

    这个问题 https stackoverflow com questions 1148125 resources on the upcoming fork join framework两年前被问到 但它提到的资源要么不是很有帮助 恕我直言
  • kivy 应用程序内的一个简单网页作为小部件

    我想在 ubuntu 64 位上运行的 kivy 应用程序中有一个 Web 视图 我查看了网络 浏览了 kivy 的文档并在 StackOverflow 上搜索答案 但我找不到任何可以完成我的工作的东西 一个简单的网页需要在 kivy 应用
  • 可以检测元素的样式是否只是浏览器默认样式还是由样式表或内联样式设置吗?

    我的 javascript 包含在 X 的网站中 但我对她的网站或她包含它的位置没有任何其他控制权 如果她的造型 element 我想不管它 但如果她没有 我有一个样式表 我会注入 有什么办法可以检测她是否有造型吗 检查它的高度是 0 还是
  • 如何用Go实现BitSet?

    我在Go中没有找到BitSet包 所以我尝试实现它 我想使用 uint64 数组来存储这些位 我需要分配 uint64 数组的位数 使用 Java 我可以定义一个采用整数的构造函数 虽然 Go 不提供构造函数 但我如何正确初始化 当用户调用
  • 交换 css 字体时是否会发出 js 事件?

    我可以判断字体何时已成功 或以其他方式 加载 然后使用 JS 进行操作吗 Context 我正在使用 playwright 打印一系列文档 我目前正在加载这些字体https fonts googleapis com css2 family
  • 理解 Java 中抽象类的用途

    假设我有两个类 A和B A类被定义为抽象类 而B类扩展了这个抽象类 最后我测试结果 这两个类都是同一个包的一部分 public abstract class A protected abstract void method1 protect
  • 游标与 While 循环 - SQL Server

    假设我在数据库 本例中为 SQL Server 2008 中有一堆可用于创建方程的行 OperationID EquationID Operation Amount Order 1 1 12 1 2 1 12 2 3 2 2
  • Kubernetes 将 env 变量传递给“kubectl create”

    我需要将动态环境变量传递给kubectl create 像这样的东西 kubectl create f app yaml Target prod 基于Target的代码部署在不同的服务器上 如果您想避免安装第 3 方插件 则可以使用 sed
  • 如何使用 Language-Ext 将 Task> 绑定到 Either<>

    我有可以请求数据库中保存的卡列表的设备 例如移动电话 支付终端等 为了安全起见 设备需要对自己进行授权 并在 JWT 令牌中提供其序列号 如果其中任何一项不匹配 我们将拒绝该请求 感谢最近对相关问题的大量帮助 我现在有两种在类似场景中使用的
  • 从 Facebook 页面提取公共帖子,无需 API/APP 密钥/令牌/秘密

    提前澄清一下 我没有 Facebook 帐户 也无意创建一个帐户 另外 我想要实现的目标在我的国家和美国是完全合法的 我不想使用 Facebook API 来获取 Facebook 页面的最新时间线帖子 而是直接向页面 URL 发送获取请求