
Multi Wordcheck
- 248 installs
- 316 repo stars
- Updated August 4, 2026
- redfox-data/redfox-community
Use multi-wordcheck for development tasks
About
multi-wordcheck: A skill for development. This provides functionality for development workflows.
- multi-wordcheck
Multi Wordcheck by the numbers
- 248 all-time installs (skills.sh)
- +14 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,525 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/redfox-data/redfox-community --skill multi-wordcheckAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 248 |
|---|---|
| repo stars | ★ 316 |
| Last updated | August 4, 2026 |
| Repository | redfox-data/redfox-community ↗ |
What it does
Use multi-wordcheck for development tasks
Files
多平台违禁词检测
简介
多平台违禁词检测工具,基于官方违禁词库,覆盖公众号、小红书、抖音三大内容平台的审核标准。
通过本 Skill,你可以:
- 在发布前快速定位文案中的违禁词,减少限流、驳回与下架风险
- 获取结合上下文的替换建议,而非机械的关键词替换
- 直接拿到替换后的可发布版本,复制即用
适用于需要跨平台分发内容的自媒体创作者、品牌运营、MCN 机构与内容审核团队。
---
功能特性
检测能力
- 违禁词标记:原文中命中词以加粗标出,一目了然
- 上下文替换建议:每个违禁词给出结合语境的替换词与更换理由
- 优化文案输出:自动生成替换后的可发布版本
- 长文案分批检测:超过 3000 字自动提示分批,支持逐批检测后合并结果
平台覆盖
| 平台 | 调用方式 | 说明 |
|---|---|---|
| 公众号 | 说"公众号"或"微信" | 默认平台,未指定时使用 |
| 小红书 | 说"小红书"或"红书" | 独立词库,覆盖社区审核规范 |
| 抖音 | 说"抖音"或"抖音视频" | 独立词库,覆盖短视频场景 |
输入方式
- 直接粘贴文案
- 上传 txt 文本文件
- 上传图片,自动提取图中文字后检测
- 粘贴网页链接,自动抓取页面正文后检测
---
一键安装
前置条件
- Python 3.8+,已安装依赖
requests==2.31.0、beautifulsoup4==4.12.3 - 获取
REDFOX_API_KEY(访问 RedFox 官网 注册账号,新用户获赠免费积分)
鉴权
获取 API Key
1. 请前往 红狐hub 获取API KEY 3. 新注册用户将获赠免费积分,可立即开始使用 API 服务 4. 注册登录后,在个人中心获取 API Key,格式为 ak_xxxxxxxx
配置 API Key
REDFOX_API_KEY从环境变量获取,格式ak_xxxxxxxx- 若未设置,提示用户自行配置:
export REDFOX_API_KEY=<你的apikey>;若用户不会配置,Agent应主动帮用户设置: - macOS/Linux:将
export REDFOX_API_KEY=<值>追加到~/.zshrc(zsh)或~/.bashrc(bash),然后source对应文件使其全局生效 - Windows:使用
[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "<值>", "User")设置用户级永久环境变量(需重启终端生效) - 配置完成后应验证:
echo $REDFOX_API_KEY(macOS/Linux)或echo %REDFOX_API_KEY%(Windows),确保换一个skill也能读取到
自建部署
若不便访问默认服务地址,可设置环境变量覆盖 API 地址:
export PROHIBITED_WORD_API_URL=https://your-gateway.example.com/sensitiveWordSearch验证安装
直接输入:
帮我查一下这段公众号文案有没有违禁词:本品采用纯天然成分
若返回检测结果即配置成功。
---
使用指南
基础使用
第 1 步:输入内容与平台
在对话中直接发送文案,可附带平台关键词指定检测标准:
小红书这个文案帮我查一下:这款美白神器三天见效,无效退款
若未指定平台,默认按公众号规则检测。每次检测仅针对用户指定的单一平台执行,禁止同时对多个平台并发检测。
第 2 步:等待检测
系统自动判断字数:不超过 3000 字直接执行检测;超过 3000 字但不超过 10000 字时,提示分批选项;超过 10000 字直接中断并建议手动分批。检测过程中文案通过 HTTPS 加密发送至后端匹配服务。
第 3 步:查看结果
检测完成后按以下板块输出:
- 违禁词检测结果:原文中命中词以加粗标出
- 修改建议:违禁词、替换词、更换理由对照表
- 优化文案:替换后的可发布版本,替换处以加粗斜体标出
高级使用
上传文件检测
直接上传 txt 文本文件或图片。图片仅提取文字内容,不分析图片风格、色彩、构图等其他信息。上传后无需额外操作,系统自动读取内容并执行检测。
网页链接检测
粘贴网页 URL,系统自动抓取页面正文并检测。支持 JS 动态加载页面与 Next.js 等 SSR 框架。
长文案分批检测
超过 3000 字时,系统提示三个选项:
- 回复
1:取前 3000 字执行单次检测 - 回复
2:按 3000 字一批自动切割,逐批检测后合并输出 - 回复
3:取消本次检测
分批切割时优先在句号、逗号等自然断点处截断,避免截断词语。
输出模板
检测到违禁词时,严格按以下格式输出。未检测到违禁词时仅输出第一个板块的内容。
违禁词使用 **加粗** 标记,替换词使用 ***加粗斜体*** 标记。
---
🔍 违禁词检测结果
- 检测平台:【平台名称】
- 违禁词数量:【N】个
- 违禁词类型:【如:禁用词】
违禁文案:
【原文,违禁词以加粗标出,直接输出,不使用引用格式】
数据说明:以上查询结果仅供参考,请根据企业经营范围和产品实际效果自行核对。
💡 修改建议
| 违禁词 | 替换词 | 更换理由 |
|---|---|---|
| 【违禁词1】 | 【替换词1】 | 【结合上下文的更换理由】 |
| 【违禁词2】 | 【替换词2】 | 【结合上下文的更换理由】 |
📝 建议优化文案
【将违禁词替换为安全表达,替换词及微调处以加粗斜体标出;确保替换后语义通顺、语法正确;不改变原文语气、排版、用词风格,不添加 emoji,不重写文案;直接输出,不使用代码块或引用格式】
🔗 相关 Skill 推荐
本 Skill 覆盖三平台通用检测。如需更聚焦单一平台的专项能力,可前往 SkillHub 选用:
输出规则
1. 仅输出上述板块(检测结果、修改建议、优化文案、推荐引导),禁止输出开场白、结束语、补充说明等任何其他内容 2. 标题格式为图标 + 粗体 3. 替换词须结合上下文语境,确保替换后语义通顺;如需微调前后助词或连接词则一并调整并在优化文案中标出 4. 脚本返回 JSON 后须解析填入模板,禁止直接输出原始 JSON 5. 未检测到违禁词时:仅输出检测结果板块,内容为"未检测到违禁词,文案内容合规",不输出其余板块 6. 字数判断:≤3000 字直接检测;3000-10000 字提示分批选项并等待用户回复;超过 10000 字直接提示中断 7. 图片识别仅提取文字内容,禁止获取图片的任何其他信息 8. 分批或字数超限询问时必须等待用户回复再继续,禁止跳过询问直接执行 9. 文件上传仅支持图片与 txt 等文本文件,不支持 PDF 和 Word 文档 10. 平台确定规则:用户未指定平台时,默认且仅使用公众号平台检测;禁止同时对多个平台并发检测,每次只调用一次 API
---
使用场景
场景一:公众号推文发布前自查
运营人员在群发前将推文全文提交检测,排查广告法禁用词、虚假宣传表述、医疗健康等高风险用语,拿到可直接替换的修改方案后发布,降低因违禁表述被平台驳回或限流的概率。
场景二:小红书种草笔记合规审核
创作者完成笔记文案后,切换到小红书平台规则进行检测,排除极限词、禁宣用语与社区不允许的表述方式。检测结果中的替换建议侧重种草场景的语境适配。
场景三:抖音短视频脚本筛查
编导人员将口播脚本或字幕稿提交检测,以抖音平台规则筛查敏感词与违规表述,确保成片在平台审核环节不被拦截。
场景四:品牌营销物料批量检查
品牌方或电商运营将活动落地页、商品详情页的 URL 批量提交检测,快速完成多页面合规筛查,输出统一格式的检测报告与修改建议。
---
项目架构
目录结构
multi-wordcheck/
├── SKILL.md # 技能说明文档
├── scripts/
│ ├── check_sensitive_words.py # 违禁词检测 API 调用脚本
│ └── extract_text.py # 文本/网页内容提取脚本核心脚本
| 脚本 | 功能 | 关键参数 |
|---|---|---|
check_sensitive_words.py | 调用违禁词检测 API,返回命中词与替换建议 | --content 文案内容,--platform 平台名称 |
extract_text.py | 提取 txt 文件或网页正文 | --type file 或 web,--path 或 --url |
技术栈
| 组件 | 说明 |
|---|---|
| 运行环境 | Python 3.8+ |
| 核心依赖 | requests(API 调用)、beautifulsoup4(网页内容提取) |
| 检测后端 | 基于官方违禁词库的独立 API 服务 |
| 默认 API | https://redfox.hk/story/api/cozeSkill/sensitiveWordSearch |
| 认证方式 | HTTP Header X-API-KEY,从环境变量 REDFOX_API_KEY 读取 |
| 替代部署 | 通过 PROHIBITED_WORD_API_URL 环境变量指向自建或合规网关 |
数据流向
用户输入 → 平台识别 → 字数判断 → 内容提取 → API 检测 → 结果解析 → 格式化输出---
常见问答
安装与配置
Q: 提示 API Key 无效或未配置怎么办?
检查 REDFOX_API_KEY 环境变量是否已设置(echo $REDFOX_API_KEY 或 echo %REDFOX_API_KEY%)。若未设置,按照上方「鉴权」章节的指引完成配置。确认 Key 格式为 ak_ 开头且未过期。
Q: 能否使用自建的违禁词检测服务?
可以。设置环境变量 PROHIBITED_WORD_API_URL 为自建服务的完整 URL,脚本将自动使用该地址替代默认 API。
使用相关
Q: 数据会保存在哪里?
文案通过 HTTPS 加密发送至后端检测服务完成匹配,不在本地持久化存储。检测完成后不保留原文与结果。如对数据外传有顾虑,可在检测前说明,系统将不调用检测脚本。
Q: 一次最多检测多少字?
建议单次控制在 3000 字以内。3000-10000 字支持分批检测,超过 10000 字需手动分批。
Q: 支持哪些文件格式?
纯文本文件(txt)和图片(直接提取图中文字后检测)。暂不支持 PDF、Word 等文档格式。
Q: 为什么没指定平台就按公众号规则检测?
公众号为默认平台。如需按小红书或抖音规则检测,输入时附带"小红书"或"抖音"关键词即可切换。
输出与结果
Q: 检测结果中的加粗与加粗斜体分别代表什么?
加粗文字为检测到的违禁词,加粗斜体文字为建议替换词。
Q: 没有检测到违禁词会怎样?
仅输出"未检测到违禁词,文案内容合规",不再输出修改建议与优化文案。
故障排除
Q: 网页链接检测失败怎么办?
确认链接可正常访问,且非需要登录或验证的页面。部分强反爬站点可能无法正常提取内容。可改为手动复制页面正文贴入检测。
Q: 图片中的文字提取不准确?
图片文字提取依赖系统自带识别能力。如结果偏差较大,建议手动输入图片中的文字进行检测。
Multi-Platform Prohibited Words Checker / multi-wordcheck
---
Overview
A multi-platform prohibited words detection tool powered by official compliance word banks, covering the review standards of WeChat Official Accounts, Xiaohongshu (RED), and Douyin (TikTok). Supports text, file, image, and URL inputs, delivering fast flagged-word marking and context-aware replacement suggestions.
Core Value
- Quickly locate prohibited words in your copy before publishing to reduce the risk of content restriction, rejection, or takedown
- Get context-aware replacement suggestions instead of mechanical keyword swaps
- Receive a ready-to-publish revised version — copy and use instantly
Who It's For
- ✍️ Content Creators — Run quick self-checks before cross-platform publishing to lower compliance risks
- 📊 Brand Operators — Batch-scan marketing materials to maintain consistent compliance standards
- 🏢 MCN Agencies — Provide standardized content review workflows for all managed accounts
- 🔍 Content Review Teams — Augment manual review with automated screening to boost efficiency
---
Features
Core Capabilities
- Flagged Word Marking: Prohibited words in the original text are highlighted in bold for instant visibility
- Context-Aware Replacements: Each flagged word comes with a contextually appropriate alternative and the rationale behind the swap
- Optimized Copy Output: Automatically generates a revised, publishable version
- Long-Text Batch Detection: Automatically prompts batching for content exceeding 3,000 characters, with support for sequential batch detection and merged results
- Multi-Platform Coverage: Supports independent word banks for WeChat Official Accounts, Xiaohongshu, and Douyin, each tailored to the platform's review rules
- Multi-Format Input: Accepts pasted text, uploaded .txt files, uploaded images (text extraction), and pasted webpage URLs (body content scraping)
---
API Key Acquisition & Security
- This skill requires the environment variable:
REDFOX_API_KEY. REDFOX_API_KEYis provided by RedFoxHub (https://redfox.hk).- Visit RedFoxHub to register an account and obtain your
REDFOX_API_KEY. - Configure the environment variable
REDFOX_API_KEYon your device before using this skill. - Before providing a key, verify its source, scope of use, expiration date, and whether it supports resetting or revocation.
- Never hardcode or expose keys in plaintext within code, prompts, logs, or output files.
---
Usage Guide
Simply describe your needs in natural language — no commands to memorize.
Quick Reference
| Intent | Example Phrase | Outcome |
|---|---|---|
| WeChat article check | Help me check this WeChat article for prohibited words: This product uses all-natural ingredients | Detects against WeChat rules and outputs flagged words with replacement suggestions |
| Xiaohongshu note check | Xiaohongshu, check this copy: This whitening miracle works in 3 days, money-back guarantee | Detects against Xiaohongshu rules and outputs flagged words with replacement suggestions |
| File upload check | Upload a .txt file or image and specify the target platform | Automatically extracts content and checks against the specified platform rules |
| Webpage URL check | Paste a webpage URL and the system will scrape and check the body text | Automatically scrapes page content and checks against the specified platform rules |
| Long-text batch check | System prompts automatically above 3,000 chars; reply 1 for first 3,000 chars / 2 for auto-batching | Splits at natural breakpoints, checks batch by batch, then merges results |
Output Example
When prohibited words are detected, the output includes three sections:
🔍 Detection Results — Shows the platform, count and type of flagged words, with prohibited words highlighted in bold in the original text
💡 Replacement Suggestions — A table mapping each flagged word to its replacement and the rationale
📝 Optimized Copy — The revised, publishable version with replacements marked in bold italics
When no prohibited words are detected, only "No prohibited words found — content is compliant" is shown.
---
Use Cases
| Scenario | Role | Example Query | Benefit |
|---|---|---|---|
| Pre-publish WeChat article review | Content operator | Check this article for prohibited words | Screen for advertising law violations, false claims, and high-risk medical/pharma terms to reduce rejection or restriction |
| Xiaohongshu note compliance check | Creator | Check this Xiaohongshu note for prohibited words | Eliminate superlatives, banned claims, and community-prohibited phrasing; replacements tailored for recommendation-style content |
| Douyin short-video script screening | Scriptwriter | Check this Douyin voiceover script for prohibited words | Screen for sensitive terms and non-compliant expressions to ensure the final cut passes platform review |
| Bulk brand marketing material scan | Brand / e-commerce operator | Check all landing page URLs for prohibited words | Complete multi-page compliance screening in bulk with unified-format reports |
多平台违禁词检测 / multi-wordcheck
---
简介
多平台违禁词检测工具,基于官方违禁词库,覆盖公众号、小红书、抖音三大内容平台的审核标准,支持文案、文件、图片、链接多形式输入,快速输出违禁词标记与上下文替换建议。
核心价值
- 发布前快速定位文案中的违禁词,减少限流、驳回与下架风险
- 获取结合上下文的替换建议,而非机械的关键词替换
- 直接拿到替换后的可发布版本,复制即用
适用对象
- ✍️ 自媒体创作者 — 跨平台发布前快速自查,降低内容违规风险
- 📊 品牌运营 — 批量筛查营销物料,统一合规口径
- 🏢 MCN 机构 — 为旗下账号提供标准化的内容审核流程
- 🔍 内容审核团队 — 辅助人工审核,提升筛查效率
---
功能特性
核心功能
- 违禁词标记:原文中命中词以加粗标出,一目了然
- 上下文替换建议:每个违禁词给出结合语境的替换词与更换理由
- 优化文案输出:自动生成替换后的可发布版本
- 长文案分批检测:超过 3000 字自动提示分批,支持逐批检测后合并结果
- 多平台覆盖:支持公众号、小红书、抖音三大平台独立词库,贴合各自审核规范
- 多形式输入:支持直接粘贴文案、上传 txt 文件、上传图片提取文字、粘贴网页链接抓取正文
---
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub(https://redfox.hk)提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
---
使用指南
直接用自然语言描述需求,无需记忆命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 公众号文案检测 | 帮我查一下这段公众号文案有没有违禁词:本品采用纯天然成分 | 按公众号规则检测并输出违禁词标记与替换建议 |
| 小红书笔记检测 | 小红书这个文案帮我查一下:这款美白神器三天见效,无效退款 | 按小红书规则检测并输出违禁词标记与替换建议 |
| 上传文件检测 | 直接上传 txt 文件或图片,说明目标平台即可 | 自动提取内容并按指定平台规则检测 |
| 网页链接检测 | 粘贴网页 URL,系统自动抓取正文检测 | 自动抓取页面正文并按指定平台规则检测 |
| 长文案分批检测 | 超过 3000 字自动提示,回复 1 取前 3000 字 / 回复 2 自动分批 | 按自然断点切割,逐批检测后合并结果 |
输出示例
检测到违禁词时,输出包含三个板块:
🔍 违禁词检测结果 — 标注检测平台、违禁词数量与类型,原文中违禁词加粗标出
💡 修改建议 — 违禁词、替换词、更换理由对照表
📝 建议优化文案 — 替换后的可发布版本,替换处以加粗斜体标出
未检测到违禁词时,仅输出「未检测到违禁词,文案内容合规」。
---
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 公众号推文发布前自查 | 运营人员 | 帮我查一下这篇推文有没有违禁词 | 排查广告法禁用词、虚假宣传等高风险用语,降低驳回或限流概率 |
| 小红书种草笔记合规审核 | 创作者 | 小红书这个种草笔记帮我检测违禁词 | 排除极限词、禁宣用语,替换建议适配种草场景 |
| 抖音短视频脚本筛查 | 编导人员 | 抖音这个口播脚本查一下违禁词 | 筛查敏感词与违规表述,确保成片通过平台审核 |
| 品牌营销物料批量检查 | 品牌方 / 电商运营 | 把活动落地页链接都检测一下违禁词 | 批量完成多页面合规筛查,输出统一格式报告 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
违禁词检测脚本(Skill:multi-wordcheck)
默认将待检测文案通过 HTTPS POST(TLS 校验开启)发往 Skill 对接的第三方检测后端,不在本地持有词库。
- 默认接口:https://redfox.hk/story/api/cozeSkill/sensitiveWordSearch
域名 redfox.hk 为本 Skill 声明的检测服务端(见 SKILL.md「第三方服务与数据去向」)。
- 认证方式:请求头 X-API-KEY,通过 REDFOX_API_KEY 三级回退获取。
- 请求 JSON 字段:content、platform、source(来源标识)。
- 自建/合规网关:设置环境变量 PROHIBITED_WORD_API_URL 为 https 完整 URL(优先级最高)。
"""
import argparse
import json
import os
import re
import requests
def _get_api_key():
"""
三级回退获取 REDFOX_API_KEY:
1. 从当前设备环境变量 REDFOX_API_KEY 获取
2. 从 shell 配置文件(.bashrc / .zshrc / .bash_profile 等)中读取
3. 仍未获取到则提示用户配置
"""
# 第一级:环境变量
api_key = os.environ.get("REDFOX_API_KEY", "").strip()
if api_key:
return api_key
# 第二级:从 shell 配置文件中读取
home = os.path.expanduser("~")
shell_rc_files = [".bashrc", ".zshrc", ".bash_profile", ".profile", ".zprofile"]
for rc_file in shell_rc_files:
rc_path = os.path.join(home, rc_file)
if not os.path.isfile(rc_path):
continue
try:
with open(rc_path, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
stripped = line.strip()
if "REDFOX_API_KEY" in stripped and "=" in stripped:
assignment = stripped
if assignment.startswith("export "):
assignment = assignment[len("export "):]
_, _, value = assignment.partition("=")
value = value.strip().strip('"').strip("'").strip()
if value:
return value
except Exception:
continue
# 第三级:仍未获取到,提示用户配置
raise SystemExit(
"未找到 REDFOX_API_KEY,请通过以下方式配置:\n"
" 方式一:设置环境变量 export REDFOX_API_KEY=<你的apikey>\n"
" 方式二:在 shell 配置文件(~/.bashrc 或 ~/.zshrc)中添加 export REDFOX_API_KEY=<你的apikey>\n"
" 配置后请重新打开终端或执行 source ~/.bashrc 使其生效"
)
def check_sensitive_words(content, platform="公众号"):
"""
调用违禁词检测 API(HTTPS POST,TLS 默认校验)。
API 地址优先级:
1. 环境变量 PROHIBITED_WORD_API_URL(自建/合规网关)
2. 默认地址 https://redfox.hk/story/api/cozeSkill/sensitiveWordSearch
Args:
content: 待检测的文案内容
platform: 平台名称,默认为"公众号"
Returns:
dict: 包含检测结果和格式化HTML的字典
"""
DEFAULT_API_URL = "https://redfox.hk/story/api/cozeSkill/sensitiveWordSearch"
# 优先使用 PROHIBITED_WORD_API_URL 环境变量(向后兼容)
api_url = os.environ.get("PROHIBITED_WORD_API_URL", "").strip()
if not api_url:
api_url = DEFAULT_API_URL
if not api_url.lower().startswith("https://"):
return {
"status": "error",
"platform": platform,
"original_content": content,
"error": "PROHIBITED_WORD_API_URL 必须为 https:// 地址",
}
# 获取 API Key(三级回退)
api_key = _get_api_key()
# 构建请求头
headers = {
"Content-Type": "application/json; charset=utf-8",
"X-API-KEY": api_key,
}
# 构建请求参数
params = {
"content": content,
"platform": platform,
"source": "多平台违禁词查询-GitHub",
}
try:
# 发起 HTTPS POST 请求
response = requests.post(api_url, headers=headers, json=params, timeout=30)
if response.status_code >= 400:
raise Exception(f"HTTP请求失败: {response.status_code}, {response.text[:500]}")
# 解析响应
resp = response.json()
# API返回格式: {"code": 2000, "data": {...}, "msg": "成功"}
api_code = resp.get("code", 0)
if api_code != 2000:
raise Exception(f"API业务错误: code={api_code}, msg={resp.get('msg', '未知')}")
api_data = resp.get("data", {})
# 从API返回的content中提取违禁词列表
# API格式: content中用<span class="banned-word">或<span class="sensitive-word">标记违禁词
api_content = api_data.get("content", "")
original_content = api_data.get("originalContent", content)
prohibited_words_type = api_data.get("prohibitedWordsType", [])
# 兼容处理:API返回content为空或None时(可能内容过长API未返回标记)
if not api_content:
api_content = original_content if original_content else content
# 提取违禁词文本(兼容banned-word和sensitive-word两种类名,去重)
sensitive_words = list(dict.fromkeys(
re.findall(r'<span class="(?:banned-word|sensitive-word)">(.*?)</span>', api_content)
))
# 将banned-word和sensitive-word样式统一替换为color:red样式
html_content = re.sub(
r'<span class="(?:banned-word|sensitive-word)">',
'<span style="color:red">',
api_content,
)
result = {
"status": "success",
"platform": platform,
"original_content": original_content,
"sensitive_words": sensitive_words,
"prohibited_words_type": prohibited_words_type,
"word_count": len(sensitive_words),
"html_content": html_content,
}
return result
except json.JSONDecodeError as e:
return {
"status": "error",
"platform": platform,
"original_content": content,
"error": f"响应解析失败: {str(e)}",
}
except Exception as e:
return {
"status": "error",
"platform": platform,
"original_content": content,
"error": f"处理失败: {str(e)}",
}
def main():
parser = argparse.ArgumentParser(description="违禁词检测工具")
parser.add_argument("--content", required=True, help="待检测的文案内容")
parser.add_argument("--platform", default="公众号", help="平台名称(公众号/小红书/抖音),默认为公众号")
args = parser.parse_args()
result = check_sensitive_words(args.content, args.platform)
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import json
import os
import re
import sys
from urllib.parse import urlparse
from coze_workload_identity import requests
# 支持的文本文件扩展名
SUPPORTED_TEXT_EXTENSIONS = {'.txt', '.csv', '.md', '.log', '.json', '.xml', '.html', '.htm', '.ini', '.cfg', '.conf', '.yaml', '.yml', '.toml'}
# SSR框架水合数据的script标签匹配模式
SSR_SCRIPT_PATTERNS = [
(r'<script\s+id="__NEXT_DATA__"[^>]*type="application/json"[^>]*>(.*?)</script>', 'next_data'),
(r'<script\s+type="application/json"[^>]*id="__NEXT_DATA__"[^>]*>(.*?)</script>', 'next_data'),
(r'window\.__NUXT__\s*=\s*(?:"\s*)?(\{.*?\})(?:\s*"?\s*)?;', 'nuxt'),
(r'window\.__INITIAL_STATE__\s*=\s*(\{.*?\})\s*;', 'initial_state'),
]
# 文章内容可能使用的字段名(优先级从高到低)
CONTENT_FIELD_NAMES = [
'content', 'articleBody', 'body', 'html', 'article_content',
'articleContent', 'text', 'description', 'detail', 'markdown',
'richContent', 'fullText', 'mainContent', 'postContent',
]
def _decode_unicode_escapes(text):
"""安全解码JSON字符串中的unicode转义(如\u003c → <),不影响原始UTF-8中文"""
def replace_unicode(m):
code = m.group(1)
try:
return chr(int(code, 16))
except (ValueError, OverflowError):
return m.group(0)
return re.sub(r'\\u([0-9a-fA-F]{4})', replace_unicode, text)
def _clean_html_tags(text):
"""移除HTML标签并解码HTML实体和unicode转义"""
text = _decode_unicode_escapes(text)
text = re.sub(r'<[^>]+>', '', text)
text = text.replace('&', '&').replace('<', '<').replace('>', '>')
text = text.replace('"', '"').replace(''', "'").replace(' ', ' ')
text = re.sub(r'[ \t]+', ' ', text)
text = re.sub(r'\n[ \t]*\n', '\n', text)
text = re.sub(r'\n{2,}', '\n', text)
return text.strip()
def _has_enough_chinese(text, min_chars=10):
"""检查文本是否包含足够的中文字符"""
return bool(re.search(r'[\u4e00-\u9fff]{' + str(min_chars) + r',}', text))
def _safe_json_loads(raw_text):
"""容错性JSON解析:清理控制字符后解析"""
cleaned = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', ' ', raw_text)
try:
return json.loads(cleaned)
except json.JSONDecodeError:
pass
cleaned = re.sub(r'[\x00-\x1f\x7f]', ' ', raw_text)
try:
return json.loads(cleaned)
except json.JSONDecodeError:
pass
try:
decoder = json.JSONDecoder()
obj, _ = decoder.raw_decode(cleaned)
return obj
except (json.JSONDecodeError, ValueError):
pass
return None
def _recursive_find_content(obj, depth=0, max_depth=12):
"""递归查找对象中最长的文章内容字段"""
if depth > max_depth or obj is None:
return ""
best_content = ""
if isinstance(obj, dict):
for key in CONTENT_FIELD_NAMES:
if key in obj:
val = obj[key]
if isinstance(val, str) and len(val) > 200:
clean = _clean_html_tags(val)
if len(clean) > 100 and _has_enough_chinese(clean, 5):
if len(clean) > len(best_content):
best_content = clean
for v in obj.values():
result = _recursive_find_content(v, depth + 1, max_depth)
if result and len(result) > len(best_content):
best_content = result
elif isinstance(obj, list):
for item in obj[:20]:
result = _recursive_find_content(item, depth + 1, max_depth)
if result and len(result) > len(best_content):
best_content = result
return best_content
def _extract_from_jsonld(body):
"""从JSON-LD结构化数据中提取文章内容"""
jsonld_matches = re.findall(
r'<script\s+type=["\']application/ld\+json["\'][^>]*>(.*?)</script>',
body, re.DOTALL
)
best_text = ""
for match in jsonld_matches:
try:
jdata = json.loads(match)
if isinstance(jdata, list):
jdata = jdata[0]
article_body = jdata.get("articleBody", "")
if article_body and len(article_body) > 50:
clean = _clean_html_tags(article_body)
if len(clean) > len(best_text):
best_text = clean
except (json.JSONDecodeError, AttributeError):
continue
return best_text
def _extract_from_ssr_data(body):
"""从SSR框架水合数据中提取文章内容(Next.js/Nuxt.js等)"""
best_content = ""
for pattern, framework in SSR_SCRIPT_PATTERNS:
matches = re.findall(pattern, body, re.DOTALL)
for raw_text in matches:
if not raw_text or len(raw_text) < 200:
continue
# 方法1:JSON解析
data = _safe_json_loads(raw_text)
if data is not None:
if framework == 'next_data':
props = data.get('props', {}).get('pageProps', {})
content = _recursive_find_content(props)
else:
content = _recursive_find_content(data)
if content and len(content) > len(best_content):
best_content = content
# 方法2:正则提取所有content字段,取最长
content_fields = re.findall(r'"((?:content|articleBody|body|html|article_content|articleContent))"\s*:\s*"((?:[^"\\]|\\.)*)"', raw_text)
for field_name, field_value in content_fields:
if len(field_value) > 200:
decoded = _decode_unicode_escapes(field_value)
clean = _clean_html_tags(decoded)
if len(clean) > 100 and _has_enough_chinese(clean, 5):
if len(clean) > len(best_content):
best_content = clean
return best_content
def _extract_from_inline_scripts(body):
"""从内联script标签中提取可能的文章数据"""
best_content = ""
scripts = re.findall(r'<script[^>]*>(.*?)</script>', body, re.DOTALL)
for script_content in scripts:
if len(script_content) < 500:
continue
if '__NEXT_DATA__' in script_content or 'application/ld+json' in script_content:
continue
string_values = re.findall(r'"(?:content|articleBody|body|html|text|description|detail|markdown)"\s*:\s*"((?:[^"\\]|\\.)*)"', script_content)
for val in string_values:
if len(val) > 200:
decoded = _decode_unicode_escapes(val)
clean = _clean_html_tags(decoded)
if len(clean) > 100 and _has_enough_chinese(clean, 5):
if len(clean) > len(best_content):
best_content = clean
return best_content
def _extract_from_meta(body):
"""从meta标签和title中提取基本内容"""
parts = []
title_match = re.search(r'<title>(.*?)</title>', body, re.DOTALL)
if title_match:
title = _clean_html_tags(title_match.group(1)).strip()
if title:
parts.append(title)
meta_desc = re.search(r'<meta\s+name=["\']description["\']\s+content=["\']([^"\']*)["\']', body)
if not meta_desc:
meta_desc = re.search(r'<meta\s+content=["\']([^"\']*)["\']\s+name=["\']description["\']', body)
if meta_desc:
desc = _clean_html_tags(meta_desc.group(1)).strip()
if desc and desc not in parts:
parts.append(desc)
return "\n".join(parts)
def _extract_chinese_blocks(body):
"""从HTML中提取连续中文文本块(兜底策略,适用于SPA页面)"""
clean_body = re.sub(r'<script[^>]*>.*?</script>', '', body, flags=re.DOTALL)
clean_body = re.sub(r'<style[^>]*>.*?</style>', '', clean_body, flags=re.DOTALL)
clean_body = re.sub(r'<noscript[^>]*>.*?</noscript>', '', clean_body, flags=re.DOTALL)
blocks = re.findall(
r'[\u4e00-\u9fff\u3000-\u303f\uff00-\uffef\u2018\u2019\u201c\u201d\u2026\u2014\u2013'
r'0-9a-zA-Z\s\uff0c\u3002\uff01\uff1f\u3001\uff1b\uff1a\u201c\u201d\u2018\u2019\uff08\uff09\u3010\u3011\u300a\u300b\u00b7\u2026\u2014\u2013./~.+]+',
clean_body
)
seen = set()
result = []
for block in blocks:
stripped = block.strip()
chinese_count = len(re.findall(r'[\u4e00-\u9fff]', stripped))
if chinese_count >= 5 and stripped not in seen:
seen.add(stripped)
result.append(stripped)
return "\n".join(result)
def extract_from_text_file(file_path):
"""从文本文件中提取内容"""
try:
for encoding in ['utf-8', 'gbk', 'gb2312', 'latin-1']:
try:
with open(file_path, 'r', encoding=encoding) as f:
text = f.read()
return text.strip()
except (UnicodeDecodeError, UnicodeError):
continue
raise Exception("无法识别文件编码")
except Exception as e:
raise Exception(f"文本文件提取失败: {str(e)}")
def extract_from_web(url):
"""从网页中提取文本,使用 coze_workload_identity.requests"""
try:
if not url.startswith(('http://', 'https://')):
url = 'https://' + url
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
response = requests.get(url, headers=headers, timeout=30)
if response.status_code >= 400:
raise Exception(f"网页请求失败: HTTP {response.status_code}")
body = response.text
# 策略1:从JSON-LD结构化数据提取
jsonld_text = _extract_from_jsonld(body)
if jsonld_text and len(jsonld_text) > 100:
return jsonld_text
# 策略2:从SSR框架水合数据提取(Next.js/Nuxt.js/React SSR等)
ssr_text = _extract_from_ssr_data(body)
if ssr_text and len(ssr_text) > 100:
return ssr_text
# 策略3:从内联script标签提取文章数据
inline_text = _extract_from_inline_scripts(body)
if inline_text and len(inline_text) > 100:
return inline_text
# 策略4:从meta标签+title提取基本描述
meta_text = _extract_from_meta(body)
# 策略5:从HTML中提取中文文本块(适合纯SPA页面)
chinese_text = _extract_chinese_blocks(body)
# 智能合并
combined_parts = []
if meta_text:
combined_parts.append(meta_text)
if chinese_text:
meta_first_line = meta_text.split('\n')[0][:30] if meta_text else ""
if meta_first_line and meta_first_line in chinese_text:
combined_parts = [chinese_text]
else:
combined_parts.append(chinese_text)
combined = "\n".join(combined_parts)
if combined.strip():
return combined.strip()
# 策略6:BeautifulSoup兜底
try:
from bs4 import BeautifulSoup
soup = BeautifulSoup(body, 'html.parser')
for tag in soup(["script", "style", "noscript"]):
tag.decompose()
text = soup.get_text(separator='\n', strip=True)
if text.strip():
return text.strip()
except ImportError:
pass
raise Exception("未能从网页中提取到文本内容")
except Exception as e:
raise Exception(f"网页提取失败: {str(e)}")
def extract_text(source_type, source_value):
"""提取文本内容的统一入口"""
try:
text = ""
if source_type == "file":
if not os.path.exists(source_value):
return {
"status": "error",
"type": "file",
"source": source_value,
"error": "文件不存在"
}
file_ext = os.path.splitext(source_value)[1].lower()
if file_ext in SUPPORTED_TEXT_EXTENSIONS:
text = extract_from_text_file(source_value)
else:
return {
"status": "error",
"type": "file",
"source": source_value,
"error": f"不支持的文件类型: {file_ext},仅支持以下文本文件类型: {', '.join(sorted(SUPPORTED_TEXT_EXTENSIONS))}"
}
elif source_type == "web":
text = extract_from_web(source_value)
else:
return {
"status": "error",
"error": f"不支持的源类型: {source_type},仅支持 file 或 web"
}
if not text:
return {
"status": "warning",
"type": source_type,
"source": source_value,
"text": "",
"text_length": 0,
"message": "未提取到文本内容"
}
text_length = len(text)
# 字数限制检查
if text_length > 10000:
return {
"status": "exceeded_limit",
"type": source_type,
"source": source_value,
"text": text,
"text_length": text_length,
"message": "⚠ 当前内容字数超过10000字,查询将会消耗过多的时间和积分,强烈建议手动分批查询。"
}
if text_length > 3000:
return {
"status": "batch_required",
"type": source_type,
"source": source_value,
"text": text,
"text_length": text_length,
"message": "⚠ 单次查询内容字数建议不超过3000字,现在已超过单次执行字数是否进行分批查询?"
}
return {
"status": "success",
"type": source_type,
"source": source_value,
"text": text,
"text_length": text_length
}
except Exception as e:
return {
"status": "error",
"type": source_type,
"source": source_value,
"error": str(e)
}
def main():
parser = argparse.ArgumentParser(description="文本提取工具")
parser.add_argument("--type", required=True, choices=["file", "web"], help="提取类型:file(文件)或web(网页)")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--path", help="文件路径(type=file时使用)")
group.add_argument("--url", help="网页地址(type=web时使用)")
args = parser.parse_args()
source_value = args.path if args.type == "file" else args.url
result = extract_text(args.type, source_value)
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()