diff --git a/README.md b/README.md
index ed0300c..a718916 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,35 @@ https://github.com/user-attachments/assets/cc385b6c-422c-489b-a5fc-63e92c695b80
## 1. 使用教程
-### 1.1. 登录
+### 1.1. 安装
+
+
+安装配置详情
+
+依赖 Golang 环境,安装方法请参考 [Golang 官方文档](https://go.dev/doc/install)。
+
+设置 Go 国内源的代理,
+
+```bash
+# 配置 GOPROXY 环境变量,以下三选一
+
+# 1. 七牛 CDN
+go env -w GOPROXY=https://goproxy.cn,direct
+
+# 2. 阿里云
+go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
+
+# 3. 官方
+go env -w GOPROXY=https://goproxy.io,direct
+```
+
+
+
+Windows 问题:
+
+可以参考这里 https://github.com/xpzouying/xiaohongshu-mcp/issues/56
+
+### 1.2. 登录
第一次需要手动登录,需要保存小红书的登录状态。
@@ -145,7 +173,7 @@ https://github.com/user-attachments/assets/cc385b6c-422c-489b-a5fc-63e92c695b80
go run cmd/login/main.go
```
-### 1.2. 启动 MCP 服务
+### 1.3. 启动 MCP 服务
启动 xiaohongshu-mcp 服务。
@@ -158,7 +186,7 @@ go run .
go run . -headless=false
```
-## 1.3. 验证 MCP
+## 1.4. 验证 MCP
```bash
npx @modelcontextprotocol/inspector
@@ -172,7 +200,7 @@ npx @modelcontextprotocol/inspector
按照上面配置 MCP inspector 后,点击 `List Tools` 按钮,查看所有的 Tools。
-## 1.4. 使用 MCP 发布
+## 1.5. 使用 MCP 发布
### 检查登录状态
@@ -410,7 +438,6 @@ npx @modelcontextprotocol/inspector
-
## 小红书 MCP 互助群
因为项目刚刚启动,会有很多问题,拉一个群大家一起讨论问题,一起为开源项目做贡献。扫我的微信二维码加群讨论技术。
@@ -418,4 +445,3 @@ npx @modelcontextprotocol/inspector
**申请时务必添加备注。**

-
diff --git a/browser/browser.go b/browser/browser.go
index 0cd380b..4da1392 100644
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -6,11 +6,30 @@ import (
"github.com/xpzouying/xiaohongshu-mcp/cookies"
)
-func NewBrowser(headless bool) *headless_browser.Browser {
+type browserConfig struct {
+ binPath string
+}
+
+type Option func(*browserConfig)
+
+func WithBinPath(binPath string) Option {
+ return func(c *browserConfig) {
+ c.binPath = binPath
+ }
+}
+
+func NewBrowser(headless bool, options ...Option) *headless_browser.Browser {
+ cfg := &browserConfig{}
+ for _, opt := range options {
+ opt(cfg)
+ }
opts := []headless_browser.Option{
headless_browser.WithHeadless(headless),
}
+ if cfg.binPath != "" {
+ opts = append(opts, headless_browser.WithChromeBinPath(cfg.binPath))
+ }
// 加载 cookies
cookiePath := cookies.GetCookiesFilePath()
diff --git a/cmd/login/main.go b/cmd/login/main.go
index a096203..5a3fd96 100644
--- a/cmd/login/main.go
+++ b/cmd/login/main.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
+ "flag"
"github.com/go-rod/rod"
"github.com/sirupsen/logrus"
@@ -12,9 +13,14 @@ import (
)
func main() {
+ var (
+ binPath string // 浏览器二进制文件路径
+ )
+ flag.StringVar(&binPath, "bin", "", "浏览器二进制文件路径")
+ flag.Parse()
// 登录的时候,需要界面,所以不能无头模式
- b := browser.NewBrowser(false)
+ b := browser.NewBrowser(false, browser.WithBinPath(binPath))
defer b.Close()
page := b.NewPage()
diff --git a/configs/browser.go b/configs/browser.go
index 0e80071..34f4538 100644
--- a/configs/browser.go
+++ b/configs/browser.go
@@ -2,6 +2,8 @@ package configs
var (
useHeadless = true
+
+ binPath = ""
)
func InitHeadless(h bool) {
@@ -12,3 +14,11 @@ func InitHeadless(h bool) {
func IsHeadless() bool {
return useHeadless
}
+
+func SetBinPath(b string) {
+ binPath = b
+}
+
+func GetBinPath() string {
+ return binPath
+}
diff --git a/go.mod b/go.mod
index 56b140d..a1bf9fd 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/xpzouying/xiaohongshu-mcp
-go 1.23.5
+go 1.24.0
require (
github.com/gin-gonic/gin v1.10.1
@@ -9,7 +9,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
- github.com/xpzouying/headless_browser v0.0.2
+ github.com/xpzouying/headless_browser v0.1.0
)
require (
@@ -45,7 +45,7 @@ require (
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
- golang.org/x/sys v0.35.0 // indirect
+ golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 723e0a7..6aa5250 100644
--- a/go.sum
+++ b/go.sum
@@ -81,8 +81,12 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xpzouying/headless_browser v0.0.2 h1:sLc4gqUT/5IyTruYIOfCW4aZLinq38hIdUHCHem1KYo=
github.com/xpzouying/headless_browser v0.0.2/go.mod h1:bQTSzGYHIipa1zwToMlOGHcXWDlvw8y33Cx5zzElekc=
+github.com/xpzouying/headless_browser v0.1.0 h1:0FyMIzhe/If/VhEdDrs7T1fqm1gOZSCFrmMXI/1JM58=
+github.com/xpzouying/headless_browser v0.1.0/go.mod h1:bQTSzGYHIipa1zwToMlOGHcXWDlvw8y33Cx5zzElekc=
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
+github.com/ysmood/fetchup v0.5.2 h1:P9w3OIA7RSNEEFvEmOiTq09IOu42C96PMyZ1MWd8TAs=
+github.com/ysmood/fetchup v0.5.2/go.mod h1:yCv8s8itjsCul1LGXJ1Q+8EQnZcVjfbZ4+l1zDm4StE=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
@@ -110,6 +114,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
+golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
diff --git a/main.go b/main.go
index 914c636..7c5e07b 100644
--- a/main.go
+++ b/main.go
@@ -10,11 +10,14 @@ import (
func main() {
var (
headless bool
+ binPath string // 浏览器二进制文件路径
)
flag.BoolVar(&headless, "headless", true, "是否无头模式")
+ flag.StringVar(&binPath, "bin", "", "浏览器二进制文件路径")
flag.Parse()
configs.InitHeadless(headless)
+ configs.SetBinPath(binPath)
// 初始化服务
xiaohongshuService := NewXiaohongshuService()
diff --git a/service.go b/service.go
index d55969a..d8d8589 100644
--- a/service.go
+++ b/service.go
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/mattn/go-runewidth"
+ "github.com/xpzouying/headless_browser"
"github.com/xpzouying/xiaohongshu-mcp/browser"
"github.com/xpzouying/xiaohongshu-mcp/configs"
"github.com/xpzouying/xiaohongshu-mcp/pkg/downloader"
@@ -50,7 +51,7 @@ type FeedsListResponse struct {
// CheckLoginStatus 检查登录状态
func (s *XiaohongshuService) CheckLoginStatus(ctx context.Context) (*LoginStatusResponse, error) {
- b := browser.NewBrowser(configs.IsHeadless())
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -117,7 +118,7 @@ func (s *XiaohongshuService) processImages(images []string) ([]string, error) {
// publishContent 执行内容发布
func (s *XiaohongshuService) publishContent(ctx context.Context, content xiaohongshu.PublishImageContent) error {
- b := browser.NewBrowser(configs.IsHeadless())
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -134,7 +135,7 @@ func (s *XiaohongshuService) publishContent(ctx context.Context, content xiaohon
// ListFeeds 获取Feeds列表
func (s *XiaohongshuService) ListFeeds(ctx context.Context) (*FeedsListResponse, error) {
- b := browser.NewBrowser(configs.IsHeadless())
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -158,7 +159,7 @@ func (s *XiaohongshuService) ListFeeds(ctx context.Context) (*FeedsListResponse,
}
func (s *XiaohongshuService) SearchFeeds(ctx context.Context, keyword string) (*FeedsListResponse, error) {
- b := browser.NewBrowser(configs.IsHeadless())
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -181,7 +182,7 @@ func (s *XiaohongshuService) SearchFeeds(ctx context.Context, keyword string) (*
// GetFeedDetail 获取Feed详情
func (s *XiaohongshuService) GetFeedDetail(ctx context.Context, feedID, xsecToken string) (*FeedDetailResponse, error) {
- b := browser.NewBrowser(configs.IsHeadless())
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -207,7 +208,7 @@ func (s *XiaohongshuService) GetFeedDetail(ctx context.Context, feedID, xsecToke
// PostCommentToFeed 发表评论到Feed
func (s *XiaohongshuService) PostCommentToFeed(ctx context.Context, feedID, xsecToken, content string) (*PostCommentResponse, error) {
// 使用非无头模式以便查看操作过程
- b := browser.NewBrowser(false)
+ b := newBrowser()
defer b.Close()
page := b.NewPage()
@@ -230,3 +231,7 @@ func (s *XiaohongshuService) PostCommentToFeed(ctx context.Context, feedID, xsec
return response, nil
}
+
+func newBrowser() *headless_browser.Browser {
+ return browser.NewBrowser(configs.IsHeadless(), browser.WithBinPath(configs.GetBinPath()))
+}