百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

引爆朋友圈!我用npx一行命令“偷”光了微信热门文章

haoteby 2025-09-05 23:41 4 浏览

嘿,各位朋友们,大家好!怎么优雅地获取微信公众号的热门文章?

你可能会说,这不就是爬虫吗?用 Python requests + BeautifulSoup,或者 Node.js axios + cheerio 不就分分钟搞定了?没错,但这样是不是有点太“普通”了?每次都得 git clone、npm install,或者配个 Python 虚拟环境,不觉得麻烦吗?

今天,我要介绍一个更酷的玩法——MCP(Module-driven Command Pipeline,模块驱动的命令管道),用 npx 这个神器,实现一行命令,就能把最新的微信热门文章给你安排得明明白白。

什么是 MCP?听起来这么玄乎?

MCP,全称 Module-driven Command Pipeline,中文叫“模块驱动的命令管道”。别被这个名字吓到,说白了,它就是一种软件设计思想,一种架构模式。

它的核心理念是:将一个复杂的任务,拆解成一连串独立的、可复用的“模块(Module)”,然后像搭积木一样,用一个“管道(Pipeline)”把这些模块串起来,让数据在管道里依次流过每个模块,最终完成整个任务。

这就像一个工厂的流水线:

  • 原料:就是你的初始输入(比如,一个指定的目标网址)。
  • 工人:就是我们一个个独立的“模块”,每个工人只干一件事(比如 A 工人负责抓取网页,B 工人负责解析 HTML,C 工人负责筛选数据,D 工人负责格式化输出)。
  • 流水线:就是“管道”,它规定了原料要经过哪些工人,以及先后顺序。
  • 成品:就是你最终想要的结果(比如,一个包含热门文章标题和链接的 JSON 文件)。

MCP 的核心优势

  • 高内聚,低耦合:每个模块只关心自己的任务,模块之间互不干扰,通过管道传递数据。修改一个模块,不会影响其他模块。
  • 超强复用性:写好的模块(比如“下载网页模块”)可以在任何其他 MCP 项目里直接使用。
  • 灵活可扩展:想增加新功能?简单,写个新模块,插到管道里就行了。想改变处理顺序?调整一下管道里的模块顺序即可。
  • 清晰易维护:整个工作流程一目了然,出了问题,可以快速定位是哪个模块的锅。

MCP 架构图解




从图可以看到,数据(Context)在一个上下文中流动,每个模块都可以读取和修改这个上下文,然后将其传递给下一个模块。

实战:三步打造微信热门文章获取器


  1. init 模块:初始化配置,比如要爬取的公众号名称。
  2. fetch_search_page 模块:根据公众号名称,请求搜狗微信搜索,获取搜索结果页的 HTML。
  3. parse_article_list 模块:解析 HTML,提取出文章的标题、链接、摘要和时间。
  4. format_output 模块:将提取到的数据格式化成美观的控制台输出。

架构图解


步骤一:创建项目并安装依赖

咱们用 Node.js 来实现。首先,你需要一个 package.json 文件来让你的脚本能被 npx 识别。

mkdir wechat-hot-articles
cd wechat-hot-articles
npm init -y

接下来,我们需要两个核心依赖:

  • axios: 用于发送 HTTP 请求,获取网页内容。
  • cheerio: 服务端版的 jQuery,用于解析 HTML,提取数据。
npm install axios cheerio

步骤二:编写 MCP 核心代码

现在,激动人心的编码时刻到了!我们将创建一个 index.js 文件,把所有的逻辑都放在里面。

#!/usr/bin/env node

// ↑↑↑ 这行叫做 "Shebang",是告诉系统这个文件要用 node 来执行,是 npx 的关键!

const axios = require('axios');
const cheerio = require('cheerio');

/**
 * 管道运行器
 * @param {Array<Function>} modules - 按顺序执行的模块数组
 * @param {object} initialContext - 初始上下文
 */
async function runPipeline(modules, initialContext = {}) {
    let context = { ...initialContext };
    for (const module of modules) {
        try {
            // 每个模块都接收上下文,并可以修改它
            // 使用 await 确保异步模块正确执行
            context = await module(context);
        } catch (error) {
            console.error(`\n Error in module: ${module.name}`);
            console.error(error.message);
            // 管道中任何一个模块出错,就中断执行
            return;
        }
    }
    return context;
}

// ------------------- MCP 模块定义 -------------------

/**
 * 模块1: 初始化
 * 从命令行参数获取要搜索的公众号名称
 */
async function init(context) {
    console.log(' [Module: init] - Initializing...');
    const query = process.argv[2]; // process.argv[2] 是 npx 命令后的第一个参数
    if (!query) {
        // 如果用户没提供公众号,就抛出错误
        throw new Error('Please provide a WeChat Official Account name. Usage: npx wechat-hot-articles "公众号名称"');
    }
    console.log(` Target Account: ${query}`);
    // 将查询参数添加到上下文中
    return { ...context, query };
}

/**
 * 模块2: 获取搜狗搜索结果页面
 * 使用 axios 请求搜狗微信搜索
 */
async function fetchSearchPage(context) {
    console.log(' [Module: fetchSearchPage] - Fetching search results...');
    const { query } = context;
    // 搜狗微信搜索的 URL
    const url = `https://weixin.sogou.com/weixin?type=1&query=${encodeURIComponent(query)}`;
    
    const response = await axios.get(url, {
        // 必须设置 User-Agent,否则可能被识别为爬虫
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        },
    });

    console.log(' Page fetched successfully.');
    // 将获取到的 HTML 文本添加到上下文中
    return { ...context, html: response.data };
}

/**
 * 模块3: 解析文章列表
 * 使用 cheerio 解析 HTML,提取文章信息
 */
async function parseArticleList(context) {
    console.log(' [Module: parseArticleList] - Parsing article list...');
    const { html } = context;
    const $ = cheerio.load(html);
    const articles = [];

    // 搜狗微信的文章列表在一个叫 'news-list' 的 ul 中
    $('.news-list li').each((index, element) => {
        const titleElement = $(element).find('.txt-box h3 a');
        const title = titleElement.text().trim();
        // 搜狗的链接是临时的,需要处理一下
        const url = 'https://weixin.sogou.com' + titleElement.attr('href');
        const summary = $(element).find('.txt-box .txt-info').text().trim();
        const time = $(element).find('.txt-box .s-p').attr('t');
        
        // 将时间戳转换为可读日期
        const date = new Date(parseInt(time) * 1000).toLocaleString();

        if (title && url) {
            articles.push({ title, url, summary, date });
        }
    });

    if (articles.length === 0) {
        throw new Error('No articles found. The account may not exist or has no recent posts.');
    }
    
    console.log(` Found ${articles.length} articles.`);
    // 将解析出的文章数组添加到上下文中
    return { ...context, articles };
}

/**
 * 模块4: 格式化并输出结果
 * 在控制台打印出漂亮的文章列表
 */
async function formatOutput(context) {
    console.log('\n [Module: formatOutput] - Formatting results...\n');
    const { articles, query } = context;
    
    console.log('=================================================');
    console.log(` Latest Articles for "${query}" `);
    console.log('=================================================\n');

    articles.forEach((article, index) => {
        console.log(`[${index + 1}] ${article.title}`);
        console.log(`     Date: ${article.date}`);
        console.log(`     Summary: ${article.summary}`);
        console.log(`     Link: ${article.url}\n`);
    });

    console.log('=================================================');
    console.log(' All tasks completed!');
    
    // 这个模块是管道的终点,可以不返回 context
    return context;
}


// ------------------- 主函数:启动管道 -------------------

function main() {
    // 定义我们的管道,将所有模块按顺序放入数组
    const pipeline = [
        init,
        fetchSearchPage,
        parseArticleList,
        formatOutput,
    ];

    // 运行管道!
    runPipeline(pipeline);
}

// 执行主函数
main();

步骤三:配置 package.json 并发布

为了让 npx 能找到并执行我们的 index.js,我们需要在 package.json 中添加一个 bin 字段。

打开 package.json,修改成类似下面这样:

JSON

{
  "name": "wechat-hot-articles-mcp-demo", // 包名,需要是唯一的
  "version": "1.0.0",
  "description": "A MCP demo to fetch latest articles from a WeChat Official Account via npx.",
  "main": "index.js",
  "bin": {
    "wechat-hot-articles": "./index.js" // 关键!定义命令名和对应的执行文件
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["wechat", "scraper", "mcp", "npx"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "axios": "^0.21.1", // 版本号可能不同
    "cheerio": "^1.0.0-rc.10"
  }
}

最重要的就是 bin 字段。它告诉 npm,当用户安装这个包时,要创建一个名为 wechat-hot-articles 的命令行工具,它实际执行的是 ./index.js 文件。

本地测试

在发布到 npm 之前,我们可以在本地模拟 npx 的行为。在项目根目录下运行:

Bash

npm link

这个命令会在你的全局 node_modules 中创建一个指向你当前项目的符号链接。然后你就可以在任何地方使用你定义的命令了:

Bash

wechat-hot-articles "前端大全"

如果一切顺利,你将看到控制台输出“前端大全”公众号的最新文章列表!

发布到 NPM

测试无误后,就可以发布到 npm 让全世界的开发者使用了!

  1. 去 npmjs.com 注册一个账号。
  2. 在你的终端登录 npm:npm login。
  3. 发布!npm publish。(注意:package.json 里的 name 必须是 npm 上没有被占用的)

发布成功后,任何人(包括你自己)就可以在任何有 Node.js 环境的电脑上,通过 npx 一行命令运行你的杰作了:

Bash

npx wechat-hot-articles-mcp-demo "前端大全"

(这里的包名
wechat-hot-articles-mcp-demo 换成你自己的)

总结

看,我们通过 MCP 架构,非常优雅地实现了一个功能强大且易于维护的命令行工具。回顾一下:

  • MCP 思想:把复杂任务拆解为 模块(Module),用 管道(Pipeline) 串联,实现了高内聚、低耦合和高复用。
  • 代码实现:我们用 JavaScript 写了四个独立的异步模块,和一个管道运行器 runPipeline 来驱动它们。
  • npx 魔法:通过配置 package.json 的 bin 字段和发布到 npm,我们创造了一个可以随处运行的、免安装的命令行工具。

这种模式不仅可以用来做爬虫,任何有固定流程的命令行任务,比如代码生成器、自动化部署脚本、文件处理器等等,都可以用 MCP 的思想来构建。它能让你的代码结构更清晰,逻辑更优雅。

相关推荐

引爆朋友圈!我用npx一行命令“偷”光了微信热门文章

嘿,各位朋友们,大家好!怎么优雅地获取微信公众号的热门文章?你可能会说,这不就是爬虫吗?用Pythonrequests+BeautifulSoup,或者Node.jsaxios+che...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:...

开源的 Gitee+ 微信小程序,提供仓库浏览、检索等功能

总在用我们国产的码云Gitee代码托管平台,但该平台还没有一个我用着顺手的小程序,于是Gitee+就诞生了。界面截图:仓库地址请在Gitee上搜索“Gitee+”。准备工作在开发Gite...

首例iPhone 15 Pro电池鼓包事件出现 原因可能是过热

iPhone15Pro和iPhone15ProMax的过热问题早已传得沸沸扬扬,一些用户抱怨这两款旗舰机手感过热。不过,他们都没有遇到电池鼓包的问题,直到今天,一位Redditor...

一看就会一搭就废?分享7个亲测有效的穿搭“套路”

晚上好啊~最近写了不少穿搭稿,每次看评论经常能看到这样的留言:眼睛:我已经学会了~手:我有自己的想法。还有同学留言:一看就会,一搭就废。不奇怪!知易行难嘛。明明学了很多穿搭技巧,等到自己要穿的时候就忘...

十个网络安全小技巧 防止个人信息泄漏

随着社会的进步和发展,个人信息泄露事件层出不穷。当个人信息泄漏后,轻则会收到漫天飞舞的短信,重则可能造成经济损失。因此为了不影响正常的生活,需要时时刻刻警惕自己的信息不被泄露。那么如何才能有效地防止个...

2018国家网络安全宣传周系列动漫③丨如何防止电脑被偷窥?这几招教你逮住黑客

来源:光明网2018国家网络安全宣传周系列动漫③——个人电脑安全篇电脑已经成为人们日常重要的工具之一,但电脑安全防护意识却在大众心目中并不完善,再之,信息安全系统的不完善,很容易成为黑客的攻击目标。无...

「网络安全宣传周」送您一份安全上网指南

网络安全学习9月17日-24日是国家网络安全宣传周,今年的主题是“网络安全为人民,网络安全靠人民”。我们作为一名合格的好网民,要具备一定的网络安全知识和网络安全防护技能,更要注意提高网络安全意识,防范...

职场达人都在偷偷用的Excel神技:WEEKDAY函数让日期秒变星期几

你是不是经常需要查看某个日期对应星期几?还在手动翻日历?作为Excel函数专家,今天我要教你一个超实用的技巧——用WEEKDAY函数一键判断日期是周几!这个函数看似简单,但灵活运用能解决工作中的各...

制作 Excel 日历的两种思路:手动创建和 使用公式动态生成

制作Excel日历主要有两种思路:手动创建(简单灵活)和使用公式动态生成(自动更新)。下面我将分别介绍两种方法,并提供详细步骤:...

Web OS 时代就在眼下,只是你还没注意到 App as a Browser 这个事实

这篇文章由36Kr新来的99(1999)后作者Link编译得来。文章作者LukeWroblewski是现任Google产品主管,他此前参与创立的Bagcheck和Polar...

如何在Form表单中添加WebBrowser控件,vba编程学习

vba编程中,如果需要显示某个网页的内容可以使用WebBrowser控件来实现。方法也比较容易,下面介绍一下如何在Form中添加WebBrowser控件。1、打开WindowsExcel,同时按A...

DevExpress Demo示例展示:媲美Excel的电子表格控件

今天介绍一个很强大的电子表格(Spreadsheet)控件:DevExpressSpreadsheetforASP.NETMVC。它是一个仿ExcelUI的电子表格控件,可完全无障碍读写...

Word,Excel,PPTOffice文件Web浏览器预览

前两天接到一个需求:需要在线预览用户上传的Word,Excel,PPT文档前端同事一听到这个需求,心里就开始骂了,什么鬼需求啊,浏览器怎么打开Word,Excel吗?这个要开发一个浏览器插件,这...

web报表工具有什么好推荐_webi报表

接触数据分析,到深入参与企业数字化转型和数据行业建议,已将近8年多,对数据分析、报表工具还是有些个人经验。我从个人的经验角度,和大家说说我用过哪些web报表工具。首先是开源的web报表:BIRTPr...