Goal: turn "web article clipping" into a stable workflow, rather than a one-off manual operation.
Relationship with Obsidian / local knowledge base
This Skill is the entry point for articles into the local knowledge system. It does not directly pass off web pages as already-organized knowledge, but first saves them as source_candidatewith source information, for subsequent manual screening, knowledge compilation, or writing workflows to continue processing.
Default division of labor:
- Obsidian Web Clipper: manual clipping in the browser; the template is responsible for generating minimal OKF frontmatter.
- This skill: Agent batch fetching, re-fetching failed pages, checking article body quality.
- Subsequent knowledge workflow: compile high-value candidate materials into source cards, concepts, methods, cases, exercises, or writing material.
Newly added or fetched articles are by default not "organized knowledge", but "materials pending judgment". The status field uses:
unprocessed -> queued -> source_carded -> compiled -> used
ignored / needs_human
The saved Markdown should include the following fields as much as possible, to make it easy for Obsidian, detection scripts, and subsequent Agents to read:
---
type: source_candidate
title: "..."
summary: "..."
source: "https://..."
resource: "https://..."
source_kind: web_article
platform: web
author: "..."
published: "YYYY-MM-DD"
clipped: "ISO时间"
clipper: agent_web_clipper
okf_version: local-okf-v0.2
compile_status: queued
status: unprocessed
training_relevance: medium
topics:
- Agent
candidate_outputs:
- source_card
- writing_fuel
reuse_note: ""
tags:
- clipping
---
Field meanings:
type: source_candidate: indicates that this is a candidate source, not a formal Wiki page.source_kind: source type, for examplewechat_article、x_status、web_article。training_relevance: content reuse relevance. Agent fetching defaults tomedium, can be overridden with--training-relevance high|medium|low|unknown.topics: OKF topic tags. The script roughly infers them from the title, URL, description, and tags, and they can also be explicitly specified with--topic Agent --topic AI培训.candidate_outputs: the types of knowledge assets that may be split out later. The script defaults tosource_card+writing_fuel, and can be repeatedly appended with--candidate-output method, etc.compile_status: the main status field read by subsequent automatic detection and compilation workflows. Agent fetching defaults toqueued, indicating entry into the candidate queue but no automatic writing to the formal Wiki.
What problem does this skill solve
It covers two types of tasks:
-
Single article clipping
- Input an article URL
- Extract the title, summary, publication date, and body
- Save as Markdown to a user-specified directory
-
Batch clipping
- Input an index page, archive page, topic page, or author homepage
- Fetch the first N articles, popular articles, latest articles, or a specified set of articles as requested by the user
- Save in batch locally
Execution strategy prioritizing success rate
Follow the order below; do not reverse it:
First use and dependency self-check
After this skill is shared as a zip with others, always use the wrapper; do not directly invoke the main script.
On first run, execute first:
bash <web-clipper-root>/scripts/run_web_clipper.sh --help
Note: this command has side effects.
It will first run bootstrap, which may create the default clipping directory and, under the current project, .web-clipper/EXTEND.md。
The wrapper will first call scripts/bootstrap.sh, automatically doing these things:
- Check whether Python 3 is available
- Attempt to automatically install Python 3 when it is missing
- Check whether the main script is complete
- Automatically create the default clipping directory
- On first run, write under the current project
.web-clipper/EXTEND.md
By default, the "directory where the current command is located" is treated as the target project root directory.
So the wrapper should be executed from the target vault root directory, not run casually from a subdirectory.
If automatic installation fails, clearly tell the user:
- It is stuck on the Python runtime, not the web scraping logic
- The installation command recommended for the current platform
- After installation is complete, just rerun the wrapper
If the user does not provide a save directory, the priority order is as follows:
- In the current project
.web-clipper/EXTEND.mdinsidedefault_output_dir - Under the current repository
00_收件箱/Clippings/ - Under the current working directory
Clippings/
Path A: Direct scripting (shortest successful path)
Applies to:
- Public web pages
- No login required
- The article page HTML directly contains the body text or structured JSON
- Substack / regular blogs / simple CMS
- X/Twitter long-form posts, old-style blogs, article pages that are publicly readable but not standardized in structure
Approach:
- Prefer calling
scripts/run_web_clipper.sh - Single article: directly scrape the URL
- Batch: first let the script try to statically extract article links from the index page
Inside the script, the extractor is automatically selected based on the site and failure conditions:
- X/Twitter long-form posts and WeChat official account articles prefer
defuddle@0.19.2, this is currently the shortest successful path - Regular web pages first statically parse HTML / JSON-LD /
<article>/<main> - After static failure, fall back to
defuddle defuddleAfter failure, fall back to Jina Reader (r.jina.ai)- For pages such as WeChat official accounts, if
defuddlefails, then use the Metaso Reader API; Jina often gets the WeChat "environment abnormal" page, so it is not the preferred path for official accounts
The output frontmatter will include the extractor field, making it easy to troubleshoot which path succeeded.
Path A2: Jina Reader fallback
Applies to:
- Static direct scraping fails
- The page structure is old, and the body text is not in
<article>/<main>in defuddledid not get the body text, but Jina Reader can clean it into Markdown
Approach:
- The script will automatically call
https://r.jina.ai/<原始URL> - No API Key required
- The output will continue to be saved as unified local Markdown
Path A3: Metaso Reader API fallback
Applies to:
- Static direct scraping fails (WeChat official accounts, sites with anti-scraping measures, etc.)
- The script reports
article body not found - No additional installation is needed, only an API Key needs to be configured
Approach:
- The script will automatically fall back to the Metaso Reader API
- The environment variable needs to be set
METASO_API_KEY - The API returns structured Markdown, including image links
- It works very well for anti-scraping sites such as WeChat official accounts and 163
If not configured METASO_API_KEY, this layer will be skipped and it will continue with paths B/C.
Path B: Browser supplementary link
Applies to:
- Lazy loading on the index page
- Only some article links are exposed in the static HTML
- The page needs to be scrolled to get all of the first N articles
- The page structure is strange, and the script cannot reliably collect links
Approach:
- Use
browserto open the index page - Scroll the page until enough article URLs have been collected
- Execute JS in the page and return the deduplicated list of article URLs
- Write the URL list to a temporary txt file
- then call
scripts/run_web_clipper.sh --url-file ...for batch landing
This pattern is very important:the browser is responsible for getting links, the script is responsible for landing the body text。
Path C: The browser directly extracts the body text
Applicable:
- single-article static scraping is blocked
web_fetchis blocked- the script fails to fetch the body text, but the browser can normally open the rendered page
Approach:
- use
browserto enter the article page - use evaluate to extract the body text from the DOM or embedded JSON in the page
- assemble Markdown
- use
writeto land the file
Only use C when both A and B are unstable, because C is heavier but has a high success rate.
Path C2: CDP Proxy fallback
Applicable:
- the script has confirmed that body text extraction failed
- the browser page itself can open normally
- the host's Browser runtime errors during initialization, tab selection, or the evaluate stage, and continuing to retry the same runtime yields no benefit
If the current environment provides web-access or an equivalent browser-control Skill, first read it completely and comply with it, then use CDP Proxy. Dependency checks, risk notices, and tab cleanup are all part of the execution contract and cannot be skipped.
Execution order:
- run the dependency check for
web-accessto confirm that Chrome remote debugging and Proxy are available. - Show the automation risk notice required by that Skill in the reply.
- Create a new background tab through Proxy, and do not reuse or close the user's existing tabs.
- Open the target article, and use
/evalto read the title, author, publication date, summary, and body text container. For WeChat public accounts, check#js_contentfirst, and at the same time check the page title and body text length to rule out verification pages, environment anomaly pages, and empty-shell pages. - Only after the body text reaches the scale of a complete article, assemble the unified frontmatter and Markdown; search summaries, body text fragments, and verification page text must not be passed off as the original text.
- After saving successfully, close the tab you created, and record
extractor: browser_cdpin the frontmatter for easy tracing of the fallback path.
CDP Proxy is an alternative execution surface after a Browser runtime failure, and it does not change this Skill's output: the final result is still a locally structured, complete Markdown file that can enter the OKF / Wiki process.
Input clarification rules
If the user has not specified, prioritize filling in these 4 things:
- single article or batch
- how many articles to scrape
- where to save
- whether to scrape only the body text, or also retain frontmatter metadata
Default values:
- single article: scrape 1 article
- batch: if the user says "the first few articles / the first 20 articles / popular articles", follow the user's request; if no quantity is specified, ask first
- output format: Markdown + YAML frontmatter
- default save directory: if the user has not specified, put it in the user's existing clipping directory; if there is no existing directory, use
00_收件箱/Clippings/
Recommended commands
Single article
WEB_CLIPPER_ROOT="<web-clipper-root>"
bash "$WEB_CLIPPER_ROOT/scripts/run_web_clipper.sh" \
--url "<文章URL>" \
--mode single \
--output-dir "<输出目录>" \
--training-relevance medium \
--candidate-output source_card \
--candidate-output writing_fuel
Batch, statically collect links
WEB_CLIPPER_ROOT="<web-clipper-root>"
bash "$WEB_CLIPPER_ROOT/scripts/run_web_clipper.sh" \
--url "<索引页URL>" \
--mode batch \
--count 20 \
--output-dir "<输出目录>"
Batch, browser first collects links then lands them
WEB_CLIPPER_ROOT="<web-clipper-root>"
bash "$WEB_CLIPPER_ROOT/scripts/run_web_clipper.sh" \
--url-file /tmp/article_urls.txt \
--mode batch \
--count 20 \
--output-dir "<输出目录>"
If the user has not explicitly passed --output-dir, the wrapper will prioritize reading .web-clipper/EXTEND.md default directory; if not configured, automatically fall back to 00_收件箱/Clippings/ or Clippings/。
Suggested JS for browser URL collection
When a static page cannot get all links, use browser.act(kind="evaluate") to execute similar logic:
async () => {
const collect = () => [
...new Set(
Array.from(document.querySelectorAll('a[href*="/p/"]'))
.map((a) => a.href)
.filter((h) => !h.includes("/comments")),
),
];
let urls = collect();
for (let i = 0; i < 8 && urls.length < 20; i++) {
window.scrollTo(0, document.body.scrollHeight);
await new Promise((r) => setTimeout(r, 1500));
urls = collect();
}
return urls;
};
Then write the results to /tmp/article_urls.txt, one URL per line.
Output requirements
Output one Markdown file per article. Recommended structure:
---
type: source_candidate
title: "..."
author: "..."
source: "..."
resource: "..."
archive: "..."
source_kind: web_article
platform: web
published: "YYYY-MM-DD"
clipped: "ISO时间"
clipper: agent_web_clipper
okf_version: local-okf-v0.2
compile_status: queued
status: unprocessed
training_relevance: medium
topics:
- Agent
candidate_outputs:
- source_card
- writing_fuel
reuse_note: ""
tags:
- clipping
---
# 标题
> 摘要
正文...
File naming
Default format:
YYYY-MM-DD 标题.md
If the date is missing, use:
unknown-date 标题.md
Failure handling
If a batch task fails, do not just end with "it failed." Troubleshoot in this order:
- Whether the index page uses lazy loading, causing static fetching to get only some links
- Whether the article page is a public page
- Whether the article body is hidden in structured JSON, for example
body_html - Whether browser rendering is needed before extracting the body
Clearly tell the user which layer it is stuck at:
- Link collection failed
- Body extraction failed
- File write failed
Currently verified successful site patterns
Substack
Verified:
- Article URLs can be collected from the archive page
- The body can be extracted from the embedded JSON field
body_htmlin the article page - Suitable for saving to Obsidian Clippings
For this type of site, prioritize batch processing with scripts; when the archive page uses lazy loading, use the browser to supplement URL collection.
WeChat Official Accounts
Verified:
- Direct static scraping is blocked by anti-scraping measures (verification page)
defuddlecan directly scrape the body, title, and author from publicly accessible Official Account articles, and is the current preferred path- Jina Reader often scrapes WeChat's "abnormal environment" verification page, so it is not the preferred path for Official Accounts
- Via the Metaso Reader API (path A3), it can serve as a fallback after
defuddlefails, returning complete Markdown + image links - Requires configuring the
METASO_API_KEYenvironment variable
Delivery receipt
After completion, give a brief result receipt. Do not be verbose:
已保存到: <目录或文件路径>
类型: clipping / Markdown
理由: 已按要求完成单篇或批量文章收藏
If it is a batch task, add one more line:
- X succeeded
- Y failed
Cases where this skill should not be triggered
Do not force the use of this skill in these cases:
- The user wants a summary, rewrite, or translation, not saving for archival
- The user wants to scrape content behind login, and no usable login state is available
- The user wants to scrape all pages of an entire site for crawler archiving
- The user wants browser automation testing, not article saving
Security boundaries
- Only process public HTTP(S) pages that the user has the right to access; do not bypass login, paywalls, CAPTCHAs, or access controls.
- Article bodies may be protected by copyright. The default use is personal research, internal archiving, and knowledge organization; retain the author, date, and original link, and do not publicly repost the full text without authorization.
defuddleruns a fixed version vianpx; Jina Reader and the optional Metaso fallback will send the target URL to third-party services.- Setting
METASO_API_KEYmeans enabling the optional Metaso fallback; before use, confirm account costs and data transmission boundaries. - The wrapper will restrict output to within the current project. The first bootstrap may install Python, create the clipping directory, and write
.web-clipper/EXTEND.md. These side effects should be explained to the user before execution.