This skill guides the implementation of web page reading and content extraction functionality using the z-ai-web-dev-sdk package, enabling applications to fetch and process web page content programmatically.
Skills Path
Skill Location: {project_path}/skills/web-reader
This skill is located at the above path in your project.
Reference Scripts: Example test scripts are available in the {Skill Location}/scripts/ directory for quick testing and reference. See {Skill Location}/scripts/web-reader.ts for a working example.
Overview
Web Reader allows you to build applications that can extract content from web pages, retrieve article metadata, and process HTML content. The API automatically handles content extraction, providing clean, structured data from any web URL.
IMPORTANT: z-ai-web-dev-sdk MUST be used in backend code only. Never use it in client-side code.
Prerequisites
The z-ai-web-dev-sdk package is already installed. Import it as shown in the examples below.
CLI Usage (For Simple Tasks)
For simple web page content extraction, you can use the z-ai CLI instead of writing code. This is ideal for quick content scraping, testing URLs, or simple automation tasks.
Basic Page Reading
# Extract content from a web page
z-ai function --name "page_reader" --args '{"url": "https://example.com"}'
# Using short options
z-ai function -n page_reader -a '{"url": "https://www.example.com/article"}'
Save Page Content
# Save extracted content to JSON file
z-ai function \
-n page_reader \
-a '{"url": "https://news.example.com/article"}' \
-o page_content.json
# Extract and save blog post
z-ai function \
-n page_reader \
-a '{"url": "https://blog.example.com/post/123"}' \
-o blog_post.json
Common Use Cases
# Extract news article
z-ai function \
-n page_reader \
-a '{"url": "https://news.site.com/breaking-news"}' \
-o news.json
# Read documentation page
z-ai function \
-n page_reader \
-a '{"url": "https://docs.example.com/getting-started"}' \
-o docs.json
# Scrape blog content
z-ai function \
-n page_reader \
-a '{"url": "https://techblog.com/ai-trends-2024"}' \
-o blog.json
# Extract research article
z-ai function \
-n page_reader \
-a '{"url": "https://research.org/papers/quantum-computing"}' \
-o research.json
CLI Parameters
-
--name, -n: Required - Function name (use "page_reader") -
--args, -a: Required - JSON arguments object with:
url (string, required): The URL of the web page to read
--output, -o <path>: Optional - Output file path (JSON format)
Response Structure
The CLI returns a JSON object containing:
-
title: Page title -
html: Main content HTML -
text: Plain text content -
publish_time: Publication timestamp (if available) -
url: Original URL -
metadata: Additional page metadata
Example Response
{
"title": "Introduction to Machine Learning",
"html": "<article><h1>Introduction to Machine Learning</h1><p>Machine learning is...</p></article>",
"text": "Introduction to Machine Learning\n\nMachine learning is...",
"publish_time": "2024-01-15T10:30:00Z",
"url": "https://example.com/ml-intro",
"metadata": {
"author": "John Doe",
"description": "A comprehensive guide to ML"
}
}
Processing Multiple URLs
# Create a simple script to process multiple URLs
for url in \
"https://site1.com/article1" \
"https://site2.com/article2" \
"https://site3.com/article3"
do
filename=$(echo $url | md5sum | cut -d' ' -f1)
z-ai function -n page_reader -a "{\"url\": \"$url\"}" -o "${filename}.json"
done
When to Use CLI vs SDK
Use CLI for:
-
Quick content extraction
-
Testing URL accessibility
-
Simple web scraping tasks
-
One-off content retrieval
Use SDK for:
-
Batch URL processing with custom logic
-
Integration with web applications
-
Complex content processing pipelines
-
Production applications with error handling
How It Works
The Web Reader uses the page_reader function to:
-
Fetch the web page content
-
Extract main article content and metadata
-
Parse and clean the HTML
-
Return structured data including title, content, and publication time
Basic Web Reading Implementation
Simple Page Reading
import ZAI from 'z-ai-web-dev-sdk';
async function readWebPage(url) {
try {
const zai = await ZAI.create();
const result = await zai.functions.invoke('page_reader', {
url: url
});
console.log('Title:', result.data.title);
console.log('URL:', result.data.url);
console.log('Published:', result.data.publishedTime);
console.log('HTML Content:', result.data.html);
console.log('Tokens Used:', result.data.usage.tokens);
return result.data;
} catch (error) {
console.error('Page reading failed:', error.message);
throw error;
}
}
// Usage
const pageData = await readWebPage('https://example.com/article');
console.log('Page title:', pageData.title);
Extract Article Text Only
import ZAI from 'z-ai-web-dev-sdk';
async function extractArticleText(url) {
const zai = await ZAI.create();
const result = await zai.functions.invoke('page_reader', {
url: url
});
// Convert HTML to plain text (basic approach)
const plainText = result.data.html
.replace(/<[^>]*>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
return {
title: result.data.title,
text: plainText,
url: result.data.url,
publishedTime: result.data.publishedTime
};
}
// Usage
const article = await extractArticleText('https://news.example.com/story');
console.log(article.title);
console.log(article.text.substring(0, 200) + '...');
Read Multiple Pages
import ZAI from 'z-ai-web-dev-sdk';
async function readMultiplePages(urls) {
const zai = await ZAI.create();
const results = [];
for (const url of urls) {
try {
const result = await zai.functions.invoke('page_reader', {
url: url
});
results.push({
url: url,
success: true,
data: result.data
});
} catch (error) {
results.push({
url: url,
success: false,
error: error.message
});
}
}
return results;
}
// Usage
const urls = [
'https://example.com/article1',
'https://example.com/article2',
'https://example.com/article3'
];
const pages = await readMultiplePages(urls);
pages.forEach(page => {
if (page.success) {
console.log(`✓ ${page.data.title}`);
} else {
console.log(`✗ ${page.url}: ${page.error}`);
}
});
Advanced Use Cases
Web Content Analyzer
import ZAI from 'z-ai-web-dev-sdk';
class WebContentAnalyzer {
constructor() {
this.cache = new Map();
}
async initialize() {
this.zai = await ZAI.create();
}
async readPage(url, useCache = true) {
// Check cache
if (useCache && this.cache.has(url)) {
console.log('Returning cached result for:', url);
return this.cache.get(url);
}
// Fetch fresh content
const result = await this.zai.functions.invoke('page_reader', {
url: url
});
// Cache the result
if (useCache) {
this.cache.set(url, result.data);
}
return result.data;
}
async getPageMetadata(url) {
const data = await this.readPage(url);
return {
title: data.title,
url: data.url,
publishedTime: data.publishedTime,
contentLength: data.html.length,
wordCount: this.estimateWordCount(data.html)
};
}
estimateWordCount(html) {
const text = html.replace(/<[^>]*>/g, ' ');
const words = text.split(/\s+/).filter(word => word.length > 0);
return words.length;
}
async comparePages(url1, url2) {
const [page1, page2] = await Promise.all([
this.readPage(url1),
this.readPage(url2)
]);
return {
page1: {
title: page1.title,
wordCount: this.estimateWordCount(page1.html),
published: page1.publishedTime
},
page2: {
title: page2.title,
wordCount: this.estimateWordCount(page2.html),
published: page2.publishedTime
}
};
}
clearCache() {
this.cache.clear();
}
}
// Usage
const analyzer = new WebContentAnalyzer();
await analyzer.initialize();
const metadata = await analyzer.getPageMetadata('https://example.com/article');
console.log('Article Metadata:', metadata);
const comparison = await analyzer.comparePages(
'https://example.com/article1',
'https://example.com/article2'
);
console.log('Comparison:', comparison);
RSS Feed Reader
import ZAI from 'z-ai-web-dev-sdk';
class FeedReader {
constructor() {
this.articles = [];
}
async initialize() {
this.zai = await ZAI.create();
}
async fetchArticlesFromUrls(urls) {
const articles = [];
for (const url of urls) {
try {
const result = await this.zai.functions.invoke('page_reader', {
url: url
});
articles.push({
title: result.data.title,
url: result.data.url,
publishedTime: result.data.publishedTime,
content: result.data.html,
fetchedAt: new Date().toISOString()
});
console.log(`Fetched: ${result.data.title}`);
} catch (error) {
console.error(`Failed to fetch ${url}:`, error.message);
}
}
this.articles = articles;
return articles;
}
getRecentArticles(limit = 10) {
return this.articles
.sort((a, b) => {
const dateA = new Date(a.publishedTime || a.fetchedAt);
const dateB = new Date(b.publishedTime || b.fetchedAt);
return dateB - dateA;
})
.slice(0, limit);
}
searchArticles(keyword) {
return this.articles.filter(article => {
const searchText = `${article.title} ${article.content}`.toLowerCase();
return searchText.includes(keyword.toLowerCase());
});
}
}
// Usage
const reader = new FeedReader();
await reader.initialize();
const feedUrls = [
'https://example.com/article1',
'https://example.com/article2',
'https://example.com/article3'
];
await reader.fetchArticlesFromUrls(feedUrls);
const recent = reader.getRecentArticles(5);
console.log('Recent articles:', recent.map(a => a.title));
Content Aggregator
import ZAI from 'z-ai-web-dev-sdk';
async function aggregateContent(urls, options = {}) {
const zai = await ZAI.create();
const aggregated = {
sources: [],
totalWords: 0,
aggregatedAt: new Date().toISOString()
};
for (const url of urls) {
try {
const result = await zai.functions.invoke('page_reader', {
url: url
});
const text = result.data.html.replace(/<[^>]*>/g, ' ');
const wordCount = text.split(/\s+/).filter(w => w.length > 0).length;
aggregated.sources.push({
title: result.data.title,
url: result.data.url,
publishedTime: result.data.publishedTime,
wordCount: wordCount,
excerpt: text.substring(0, 200).trim() + '...'
});
aggregated.totalWords += wordCount;
if (options.delay) {
await new Promise(resolve => setTimeout(resolve, options.delay));
}
} catch (error) {
console.error(`Failed to fetch ${url}:`, error.message);
}
}
return aggregated;
}
// Usage
const sources = [
'https://example.com/news1',
'https://example.com/news2',
'https://example.com/news3'
];
const aggregated = await aggregateContent(sources, { delay: 1000 });
console.log(`Aggregated ${aggregated.sources.length} sources`);
console.log(`Total words: ${aggregated.totalWords}`);
Web Scraping Pipeline
import ZAI from 'z-ai-web-dev-sdk';
class ScrapingPipeline { constructor() { this.processors = []; }
async initialize() { this.zai = await ZAI.create(); }
addProcessor(name, processorFn) { this.processors.push({ name, fn: processorFn }); }
async scrape(url) { // Fetch the page const result = await this.zai.functions.invoke('page_reader', { url: url });
let data = {
raw: result.data,
processed: {}
};
// Run through processors
for (const processor of this.processors) {
try {
data.processed[processor.name] = await processor.fn(data.raw);
console.log(`✓ Processed with ${processor.name}`);
} catch (error) {
console.error(`✗ Failed ${processor.name}:`, error.message);
data.processed[processor.name] = null;
}
}
return data;
} }
// Processor functions function extractLinks(pageData) { const linkRegex = /href="'["']/g; <span c