
Content Creator
- 65 installs
- 93 repo stars
- Updated May 14, 2026
- thatrebeccarae/claude-marketing
Helps with marketing & seo tasks.
About
content-creator is a Claude Code skill for marketing & seo. It helps solo builders move faster with AI-assisted development.
- content-creator
- Marketing & SEO
- AI-coding skill
Content Creator by the numbers
- 65 all-time installs (skills.sh)
- +8 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,250 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thatrebeccarae/claude-marketing --skill content-creatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 65 |
|---|---|
| repo stars | ★ 93 |
| Last updated | May 14, 2026 |
| Repository | thatrebeccarae/claude-marketing ↗ |
What it does
Helps with marketing & seo tasks.
Files
Content Creator
Professional-grade brand voice analysis, SEO optimization, and platform-specific content frameworks with executable Python tools.
Install
git clone https://github.com/thatrebeccarae/claude-marketing.git && cp -r claude-marketing/skills/content-creator ~/.claude/skills/How this differs from seo-content-writer: The seo-content-writer skill focuses on deep SEO writing — keyword research, on-page optimization, meta tags, heading hierarchy. This skill is the broader content marketing toolkit — brand voice development, content calendars, multi-platform strategy, and includes executable Python scripts for analysis.
Core Workflows
Brand Voice Development
1. Analyze Existing Content (if available)
python scripts/brand_voice_analyzer.py existing_content.txt2. Define Voice Attributes
- Review brand personality archetypes in REFERENCE.md
- Select primary and secondary archetypes
- Choose 3-5 tone attributes
- Document in brand guidelines
3. Create Voice Sample
- Write 3 sample pieces in chosen voice
- Test consistency using analyzer
- Refine based on results
Creating SEO-Optimized Blog Posts
1. Keyword Research
- Identify primary keyword (search volume 500-5000/month)
- Find 3-5 secondary keywords
- List 10-15 LSI keywords
2. Content Structure
- Use blog template from REFERENCE.md
- Include keyword in title, first paragraph, and 2-3 H2s
- Aim for 1,500-2,500 words for comprehensive coverage
3. Optimization Check
python scripts/seo_optimizer.py blog_post.md "primary keyword" "secondary,keywords,list"4. Apply SEO Recommendations
- Adjust keyword density to 1-3%
- Ensure proper heading structure
- Add internal and external links
- Optimize meta description
Social Media Content Creation
1. Platform Selection — Identify primary platforms based on audience 2. Content Adaptation — Start with core message, use repurposing matrix from REFERENCE.md 3. Optimization — Platform-appropriate length, optimal posting time, correct image dimensions
Content Calendar Planning
1. Monthly Planning — Set goals and KPIs, identify key themes 2. Weekly Distribution — Follow 40/25/25/10 content pillar ratio 3. Batch Creation — Create all weekly content in one session
Key Scripts
brand_voice_analyzer.py
Analyzes text content for voice characteristics, readability, and consistency.
Usage: python scripts/brand_voice_analyzer.py <file> [json|text]
Returns: Voice profile (formality, tone, perspective), readability score, sentence structure analysis, improvement recommendations.
seo_optimizer.py
Analyzes content for SEO optimization and provides actionable recommendations.
Usage: python scripts/seo_optimizer.py <file> [primary_keyword] [secondary_keywords]
Returns: SEO score (0-100), keyword density analysis, structure assessment, meta tag suggestions, specific optimization recommendations.
Quality Indicators
- SEO score above 75/100
- Readability appropriate for audience
- Consistent brand voice throughout
- Clear value proposition
- Actionable takeaways
- Platform-optimized
Performance Metrics
Content Metrics
- Organic traffic growth
- Average time on page
- Bounce rate, social shares, backlinks
Engagement Metrics
- Comments, email CTR, engagement rate, downloads
Business Metrics
- Leads generated, conversion rate, CAC, revenue attribution, ROI per piece
Content Creator — Examples
Example 1: Brand Voice Development
Prompt
Analyze these 5 blog posts and create a brand voice guide for our company.
What the skill does
1. Runs scripts/brand_voice_analyzer.py on each post 2. Aggregates voice profiles: formality (3.2/5), tone (confident but approachable), perspective (first-person plural) 3. Identifies inconsistencies across posts (voice shifts, jargon handling) 4. Produces a brand voice guide with archetypes, tone attributes, and do/don't guidelines
---
Example 2: Monthly Content Calendar
Prompt
Create a content calendar for March with 3 posts per week across LinkedIn, blog, and newsletter. Theme: AI in marketing. Target: marketing directors at mid-market SaaS.
What the skill does
1. Plans 4 weeks following 40/25/25/10 pillar ratio 2. Maps topics to platforms based on REFERENCE.md specs 3. Suggests optimal posting times 4. Creates structured calendar with repurposing notes
---
Example 3: Blog-to-Social Repurposing
Prompt
Take this blog post about email segmentation and create platform-optimized versions for LinkedIn, Twitter, and Instagram.
What the skill does
1. Analyzes blog structure: key insight, supporting data, story element, practical takeaway 2. Creates LinkedIn post (1,400 chars, story-led hook, bullet framework, engagement question) 3. Creates Twitter thread (6 tweets, case study hook, one step per tweet, CTA) 4. Creates Instagram carousel concept (7 slides with bold stats and framework steps) 5. Runs SEO optimizer on original: python scripts/seo_optimizer.py blog-post.md "email segmentation" (Score: 82/100)
MIT License
Copyright (c) 2026 Rebecca Rae Barton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Content Creator — Reference
Brand Voice Archetypes
| Archetype | Traits | Best For |
|---|---|---|
| The Expert | Authoritative, data-driven, precise | B2B, SaaS, professional services |
| The Friend | Conversational, warm, relatable | DTC, lifestyle, community brands |
| The Innovator | Forward-thinking, bold, disruptive | Tech startups, thought leadership |
| The Guide | Helpful, patient, educational | Education, how-to, documentation |
| The Rebel | Provocative, contrarian, direct | Challenger brands, opinion pieces |
| The Storyteller | Narrative-driven, emotional, vivid | Personal brands, case studies |
Tone Matrix
| Dimension | Scale |
|---|---|
| Formality | Casual ←→ Formal |
| Energy | Calm ←→ Energetic |
| Humor | Serious ←→ Playful |
| Confidence | Humble ←→ Bold |
| Perspective | Personal (I/we) ←→ Objective (it/they) |
Content Frameworks
Blog Post Template
# [Title with Primary Keyword] (50-60 chars)
Meta description: [155 chars with primary keyword, benefit, CTA]
## Introduction (100-150 words)
- Hook: question, statistic, or bold statement
- Context: why this matters now
- Promise: what the reader will learn
## [H2 with Secondary Keyword] (200-400 words)
- Key point with supporting evidence
- Example or case study
- Practical takeaway
## [H2 with Secondary Keyword] (200-400 words)
[Repeat pattern]
## [H2: Practical Application] (200-400 words)
- Step-by-step instructions
- Code snippets or templates
- Common pitfalls to avoid
## Conclusion (100-150 words)
- Summarize key takeaways (3 bullets)
- CTA: what to do next
- Internal link to related contentLinkedIn Post Framework
[Hook — first 2 lines visible before "see more"] (max 210 chars)
[Story or insight — 3-5 short paragraphs]
[Use line breaks between each thought]
[One idea per paragraph]
[Key takeaway — bold or formatted differently]
[CTA — question or prompt for engagement]
[Hashtags — 3-5 relevant tags]Twitter/X Thread Framework
1/ [Hook tweet — must stand alone] (max 280 chars)
2/ [Context — why this matters]
3/ [Key insight #1]
4/ [Key insight #2 — with data if possible]
5/ [Counter-argument or nuance]
6/ [Practical takeaway]
7/ [CTA + link to full content]Newsletter Framework
Subject: [Curiosity gap or specific benefit] (40-60 chars)
Preview: [Extends subject, adds context] (40-90 chars)
## [Main Story]
- 300-500 words
- One key insight
- Personal angle or opinion
## [Quick Hits / Links] (3-5 items)
- [Title]: [One-line summary + link]
## [CTA]
- Reply prompt or resource linkSocial Media Platform Specs
| Platform | Optimal Length | Best Times (EST) | Image Size |
|---|---|---|---|
| 1,200-1,500 chars | Tue-Thu 8-10am | 1200x627 | |
| Twitter/X | 100-200 chars (threads: 5-7) | Mon-Fri 9am-12pm | 1200x675 |
| 125-150 chars (caption) | Tue-Fri 11am-1pm | 1080x1080 | |
| Newsletter | 300-500 words | Tue-Thu 6-10am | 600px wide |
Content Calendar Template
# Content Calendar — [Month Year]
## Monthly Goals
- [ ] [Goal 1 with metric]
- [ ] [Goal 2 with metric]
## Theme: [Monthly theme]
## Week 1 (MM/DD - MM/DD)
| Day | Platform | Type | Topic | Status |
|-----|----------|------|-------|--------|
| Mon | Blog | How-to | [Topic] | Draft |
| Tue | LinkedIn | Insight | [Topic] | Scheduled |
| Thu | Newsletter | Digest | [Topic] | Planned |
## Content Pillar Ratio (40/25/25/10)
- 40% Educational (how-tos, guides, tutorials)
- 25% Thought Leadership (opinions, predictions, analysis)
- 25% Social Proof (case studies, results, testimonials)
- 10% Promotional (products, services, offers)Repurposing Matrix
| Source | Newsletter | |||
|---|---|---|---|---|
| Blog post | Key insight + CTA | Thread (5-7 tweets) | Summary + link | Carousel (5-7 slides) |
| Case study | Results highlight | Before/after stat | Featured story | Quote graphic |
| Webinar | 3 takeaways | Live-tweet thread | Replay link + summary | Speaker quote |
| Data report | Chart + insight | Key stat thread | Deep dive section | Infographic |
#!/usr/bin/env python3
"""
Brand Voice Analyzer - Analyzes content to establish and maintain brand voice consistency
"""
import re
from typing import Dict, List, Tuple
import json
class BrandVoiceAnalyzer:
def __init__(self):
self.voice_dimensions = {
'formality': {
'formal': ['hereby', 'therefore', 'furthermore', 'pursuant', 'regarding'],
'casual': ['hey', 'cool', 'awesome', 'stuff', 'yeah', 'gonna']
},
'tone': {
'professional': ['expertise', 'solution', 'optimize', 'leverage', 'strategic'],
'friendly': ['happy', 'excited', 'love', 'enjoy', 'together', 'share']
},
'perspective': {
'authoritative': ['proven', 'research shows', 'experts agree', 'data indicates'],
'conversational': ['you might', 'let\'s explore', 'we think', 'imagine if']
}
}
def analyze_text(self, text: str) -> Dict:
"""Analyze text for brand voice characteristics"""
text_lower = text.lower()
word_count = len(text.split())
results = {
'word_count': word_count,
'readability_score': self._calculate_readability(text),
'voice_profile': {},
'sentence_analysis': self._analyze_sentences(text),
'recommendations': []
}
# Analyze voice dimensions
for dimension, categories in self.voice_dimensions.items():
dim_scores = {}
for category, keywords in categories.items():
score = sum(1 for keyword in keywords if keyword in text_lower)
dim_scores[category] = score
# Determine dominant voice
if sum(dim_scores.values()) > 0:
dominant = max(dim_scores, key=dim_scores.get)
results['voice_profile'][dimension] = {
'dominant': dominant,
'scores': dim_scores
}
# Generate recommendations
results['recommendations'] = self._generate_recommendations(results)
return results
def _calculate_readability(self, text: str) -> float:
"""Calculate Flesch Reading Ease score"""
sentences = re.split(r'[.!?]+', text)
words = text.split()
syllables = sum(self._count_syllables(word) for word in words)
if len(sentences) == 0 or len(words) == 0:
return 0
avg_sentence_length = len(words) / len(sentences)
avg_syllables_per_word = syllables / len(words)
# Flesch Reading Ease formula
score = 206.835 - 1.015 * avg_sentence_length - 84.6 * avg_syllables_per_word
return max(0, min(100, score))
def _count_syllables(self, word: str) -> int:
"""Count syllables in a word (simplified)"""
word = word.lower()
vowels = 'aeiou'
syllable_count = 0
previous_was_vowel = False
for char in word:
is_vowel = char in vowels
if is_vowel and not previous_was_vowel:
syllable_count += 1
previous_was_vowel = is_vowel
# Adjust for silent e
if word.endswith('e'):
syllable_count -= 1
return max(1, syllable_count)
def _analyze_sentences(self, text: str) -> Dict:
"""Analyze sentence structure"""
sentences = re.split(r'[.!?]+', text)
sentences = [s.strip() for s in sentences if s.strip()]
if not sentences:
return {'average_length': 0, 'variety': 'low'}
lengths = [len(s.split()) for s in sentences]
avg_length = sum(lengths) / len(lengths) if lengths else 0
# Calculate variety
if len(set(lengths)) < 3:
variety = 'low'
elif len(set(lengths)) < 5:
variety = 'medium'
else:
variety = 'high'
return {
'average_length': round(avg_length, 1),
'variety': variety,
'count': len(sentences)
}
def _generate_recommendations(self, analysis: Dict) -> List[str]:
"""Generate recommendations based on analysis"""
recommendations = []
# Readability recommendations
if analysis['readability_score'] < 30:
recommendations.append("Consider simplifying language for better readability")
elif analysis['readability_score'] > 70:
recommendations.append("Content is very easy to read - consider if this matches your audience")
# Sentence variety
if analysis['sentence_analysis']['variety'] == 'low':
recommendations.append("Vary sentence length for better flow and engagement")
# Voice consistency
if analysis['voice_profile']:
recommendations.append("Maintain consistent voice across all content")
return recommendations
def analyze_content(content: str, output_format: str = 'json') -> str:
"""Main function to analyze content"""
analyzer = BrandVoiceAnalyzer()
results = analyzer.analyze_text(content)
if output_format == 'json':
return json.dumps(results, indent=2)
else:
# Human-readable format
output = [
f"=== Brand Voice Analysis ===",
f"Word Count: {results['word_count']}",
f"Readability Score: {results['readability_score']:.1f}/100",
f"",
f"Voice Profile:"
]
for dimension, profile in results['voice_profile'].items():
output.append(f" {dimension.title()}: {profile['dominant']}")
output.extend([
f"",
f"Sentence Analysis:",
f" Average Length: {results['sentence_analysis']['average_length']} words",
f" Variety: {results['sentence_analysis']['variety']}",
f" Total Sentences: {results['sentence_analysis']['count']}",
f"",
f"Recommendations:"
])
for rec in results['recommendations']:
output.append(f" • {rec}")
return '\n'.join(output)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
content = f.read()
output_format = sys.argv[2] if len(sys.argv) > 2 else 'text'
print(analyze_content(content, output_format))
else:
print("Usage: python brand_voice_analyzer.py <file> [json|text]")
#!/usr/bin/env python3
"""
SEO Content Optimizer - Analyzes and optimizes content for SEO.
Scores content against industry-standard SEO benchmarks for keyword density,
readability, structure, and meta tags. Thresholds are based on widely cited
SEO research (Yoast, Moz, Backlinko) and Google's own documentation.
Usage:
python seo_optimizer.py <file> [primary_keyword] [secondary_keywords]
No network calls. Reads a local file and prints analysis to stdout.
"""
import re
from typing import Dict, List, Set
import json
class SEOOptimizer:
def __init__(self):
# Common English stop words excluded from keyword frequency analysis.
# Standard NLP stop list — these carry grammatical rather than topical weight.
self.stop_words = {
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'be',
'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',
'would', 'could', 'should', 'may', 'might', 'must', 'can', 'shall'
}
# SEO best practice thresholds from industry research:
# title_length: 50-60 chars — Google truncates titles beyond ~60 chars in SERPs
# meta_description: 150-160 chars — Google truncates beyond ~160 chars
# url_length: 50-60 chars — shorter URLs correlate with higher rankings (Backlinko)
# paragraph_length: 40-150 words — web readability best practice (Nielsen Norman Group)
# keyword_density: 1-3% — consensus range from Yoast/Moz; below 1% = underoptimized,
# above 3% = keyword stuffing risk per Google's spam policies
self.best_practices = {
'title_length': (50, 60),
'meta_description_length': (150, 160),
'url_length': (50, 60),
'paragraph_length': (40, 150),
'heading_keyword_placement': True,
'keyword_density': (0.01, 0.03)
}
def analyze(self, content: str, target_keyword: str = None,
secondary_keywords: List[str] = None) -> Dict:
"""Analyze content for SEO optimization"""
analysis = {
'content_length': len(content.split()),
'keyword_analysis': {},
'structure_analysis': self._analyze_structure(content),
'readability': self._analyze_readability(content),
'meta_suggestions': {},
'optimization_score': 0,
'recommendations': []
}
# Keyword analysis
if target_keyword:
analysis['keyword_analysis'] = self._analyze_keywords(
content, target_keyword, secondary_keywords or []
)
# Generate meta suggestions
analysis['meta_suggestions'] = self._generate_meta_suggestions(
content, target_keyword
)
# Calculate optimization score
analysis['optimization_score'] = self._calculate_seo_score(analysis)
# Generate recommendations
analysis['recommendations'] = self._generate_recommendations(analysis)
return analysis
def _analyze_keywords(self, content: str, primary: str,
secondary: List[str]) -> Dict:
"""Analyze keyword usage and density"""
content_lower = content.lower()
word_count = len(content.split())
results = {
'primary_keyword': {
'keyword': primary,
'count': content_lower.count(primary.lower()),
'density': 0,
'in_title': False,
'in_headings': False,
'in_first_paragraph': False
},
'secondary_keywords': [],
'lsi_keywords': []
}
# Calculate primary keyword metrics
if word_count > 0:
results['primary_keyword']['density'] = (
results['primary_keyword']['count'] / word_count
)
# Check keyword placement
first_para = content.split('\n\n')[0] if '\n\n' in content else content[:200]
results['primary_keyword']['in_first_paragraph'] = (
primary.lower() in first_para.lower()
)
# Analyze secondary keywords
for keyword in secondary:
count = content_lower.count(keyword.lower())
results['secondary_keywords'].append({
'keyword': keyword,
'count': count,
'density': count / word_count if word_count > 0 else 0
})
# Extract potential LSI keywords
results['lsi_keywords'] = self._extract_lsi_keywords(content, primary)
return results
def _analyze_structure(self, content: str) -> Dict:
"""Analyze content structure for SEO"""
lines = content.split('\n')
structure = {
'headings': {'h1': 0, 'h2': 0, 'h3': 0, 'total': 0},
'paragraphs': 0,
'lists': 0,
'images': 0,
'links': {'internal': 0, 'external': 0},
'avg_paragraph_length': 0
}
paragraphs = []
current_para = []
for line in lines:
# Count headings
if line.startswith('# '):
structure['headings']['h1'] += 1
structure['headings']['total'] += 1
elif line.startswith('## '):
structure['headings']['h2'] += 1
structure['headings']['total'] += 1
elif line.startswith('### '):
structure['headings']['h3'] += 1
structure['headings']['total'] += 1
# Count lists
if line.strip().startswith(('- ', '* ', '1. ')):
structure['lists'] += 1
# Count links
internal_links = len(re.findall(r'\[.*?\]\(/.*?\)', line))
external_links = len(re.findall(r'\[.*?\]\(https?://.*?\)', line))
structure['links']['internal'] += internal_links
structure['links']['external'] += external_links
# Track paragraphs
if line.strip() and not line.startswith('#'):
current_para.append(line)
elif current_para:
paragraphs.append(' '.join(current_para))
current_para = []
if current_para:
paragraphs.append(' '.join(current_para))
structure['paragraphs'] = len(paragraphs)
if paragraphs:
avg_length = sum(len(p.split()) for p in paragraphs) / len(paragraphs)
structure['avg_paragraph_length'] = round(avg_length, 1)
return structure
def _analyze_readability(self, content: str) -> Dict:
"""Analyze content readability"""
sentences = re.split(r'[.!?]+', content)
words = content.split()
if not sentences or not words:
return {'score': 0, 'level': 'Unknown'}
avg_sentence_length = len(words) / len(sentences)
# Readability scoring based on average sentence length.
# Thresholds derived from Flesch-Kincaid research:
# <15 words/sentence = grade 6-8 reading level (broad audience)
# 15-20 = grade 9-12 (general web content sweet spot)
# 20-25 = college level (acceptable for technical/B2B)
# >25 = academic/legal density (poor for web)
if avg_sentence_length < 15:
level = 'Easy'
score = 90
elif avg_sentence_length < 20:
level = 'Moderate'
score = 70
elif avg_sentence_length < 25:
level = 'Difficult'
score = 50
else:
level = 'Very Difficult'
score = 30
return {
'score': score,
'level': level,
'avg_sentence_length': round(avg_sentence_length, 1)
}
def _extract_lsi_keywords(self, content: str, primary_keyword: str) -> List[str]:
"""Extract potential LSI (semantically related) keywords"""
words = re.findall(r'\b[a-z]+\b', content.lower())
word_freq = {}
# Count word frequencies
for word in words:
if word not in self.stop_words and len(word) > 3:
word_freq[word] = word_freq.get(word, 0) + 1
# Sort by frequency and return top related terms
sorted_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)
# Filter out the primary keyword and return top 10
lsi_keywords = []
for word, count in sorted_words:
if word != primary_keyword.lower() and count > 1:
lsi_keywords.append(word)
if len(lsi_keywords) >= 10:
break
return lsi_keywords
def _generate_meta_suggestions(self, content: str, keyword: str = None) -> Dict:
"""Generate SEO meta tag suggestions"""
# Extract first sentence for description base
sentences = re.split(r'[.!?]+', content)
first_sentence = sentences[0] if sentences else content[:160]
suggestions = {
'title': '',
'meta_description': '',
'url_slug': '',
'og_title': '',
'og_description': ''
}
if keyword:
# Title suggestion
suggestions['title'] = f"{keyword.title()} - Complete Guide"
if len(suggestions['title']) > 60:
suggestions['title'] = keyword.title()[:57] + "..."
# Meta description
desc_base = f"Learn everything about {keyword}. {first_sentence}"
if len(desc_base) > 160:
desc_base = desc_base[:157] + "..."
suggestions['meta_description'] = desc_base
# URL slug
suggestions['url_slug'] = re.sub(r'[^a-z0-9-]+', '-',
keyword.lower()).strip('-')
# Open Graph tags
suggestions['og_title'] = suggestions['title']
suggestions['og_description'] = suggestions['meta_description']
return suggestions
def _calculate_seo_score(self, analysis: Dict) -> int:
"""Calculate overall SEO optimization score"""
score = 0
max_score = 100
# Content length scoring (20 points).
# 300-2500 words is the optimal range for most web content (HubSpot/Backlinko).
# Under 300 = thin content risk. Over 2500 = still good but diminishing returns.
if 300 <= analysis['content_length'] <= 2500:
score += 20
elif 200 <= analysis['content_length'] < 300:
score += 10
elif analysis['content_length'] > 2500:
score += 15
# Keyword optimization (30 points)
if analysis['keyword_analysis']:
kw_data = analysis['keyword_analysis']['primary_keyword']
# Density scoring: 1-3% is the widely accepted optimal range.
# 0.5-1% gets partial credit — present but underweighted.
if 0.01 <= kw_data['density'] <= 0.03:
score += 15
elif 0.005 <= kw_data['density'] < 0.01:
score += 8
# Placement scoring
if kw_data['in_first_paragraph']:
score += 10
if kw_data.get('in_headings'):
score += 5
# Structure scoring (25 points)
struct = analysis['structure_analysis']
if struct['headings']['total'] > 0:
score += 10
if struct['paragraphs'] >= 3:
score += 10
if struct['links']['internal'] > 0 or struct['links']['external'] > 0:
score += 5
# Readability scoring (25 points)
readability_score = analysis['readability']['score']
score += int(readability_score * 0.25)
return min(score, max_score)
def _generate_recommendations(self, analysis: Dict) -> List[str]:
"""Generate SEO improvement recommendations"""
recommendations = []
# Content length recommendations
if analysis['content_length'] < 300:
recommendations.append(
f"Increase content length to at least 300 words (currently {analysis['content_length']})"
)
elif analysis['content_length'] > 3000:
recommendations.append(
"Consider breaking long content into multiple pages or adding a table of contents"
)
# Keyword recommendations
if analysis['keyword_analysis']:
kw_data = analysis['keyword_analysis']['primary_keyword']
if kw_data['density'] < 0.01:
recommendations.append(
f"Increase keyword density for '{kw_data['keyword']}' (currently {kw_data['density']:.2%})"
)
elif kw_data['density'] > 0.03:
recommendations.append(
f"Reduce keyword density to avoid over-optimization (currently {kw_data['density']:.2%})"
)
if not kw_data['in_first_paragraph']:
recommendations.append(
"Include primary keyword in the first paragraph"
)
# Structure recommendations
struct = analysis['structure_analysis']
if struct['headings']['total'] == 0:
recommendations.append("Add headings (H1, H2, H3) to improve content structure")
if struct['links']['internal'] == 0:
recommendations.append("Add internal links to related content")
if struct['avg_paragraph_length'] > 150:
recommendations.append("Break up long paragraphs for better readability")
# Readability recommendations
if analysis['readability']['avg_sentence_length'] > 20:
recommendations.append("Simplify sentences for better readability")
return recommendations
def optimize_content(content: str, keyword: str = None,
secondary_keywords: List[str] = None) -> str:
"""Main function to optimize content"""
optimizer = SEOOptimizer()
# Parse secondary keywords from comma-separated string if provided
if secondary_keywords and isinstance(secondary_keywords, str):
secondary_keywords = [kw.strip() for kw in secondary_keywords.split(',')]
results = optimizer.analyze(content, keyword, secondary_keywords)
# Format output
output = [
"=== SEO Content Analysis ===",
f"Overall SEO Score: {results['optimization_score']}/100",
f"Content Length: {results['content_length']} words",
f"",
"Content Structure:",
f" Headings: {results['structure_analysis']['headings']['total']}",
f" Paragraphs: {results['structure_analysis']['paragraphs']}",
f" Avg Paragraph Length: {results['structure_analysis']['avg_paragraph_length']} words",
f" Internal Links: {results['structure_analysis']['links']['internal']}",
f" External Links: {results['structure_analysis']['links']['external']}",
f"",
f"Readability: {results['readability']['level']} (Score: {results['readability']['score']})",
f""
]
if results['keyword_analysis']:
kw = results['keyword_analysis']['primary_keyword']
output.extend([
"Keyword Analysis:",
f" Primary Keyword: {kw['keyword']}",
f" Count: {kw['count']}",
f" Density: {kw['density']:.2%}",
f" In First Paragraph: {'Yes' if kw['in_first_paragraph'] else 'No'}",
f""
])
if results['keyword_analysis']['lsi_keywords']:
output.append(" Related Keywords Found:")
for lsi in results['keyword_analysis']['lsi_keywords'][:5]:
output.append(f" • {lsi}")
output.append("")
if results['meta_suggestions']:
output.extend([
"Meta Tag Suggestions:",
f" Title: {results['meta_suggestions']['title']}",
f" Description: {results['meta_suggestions']['meta_description']}",
f" URL Slug: {results['meta_suggestions']['url_slug']}",
f""
])
output.extend([
"Recommendations:",
])
for rec in results['recommendations']:
output.append(f" • {rec}")
return '\n'.join(output)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
content = f.read()
keyword = sys.argv[2] if len(sys.argv) > 2 else None
secondary = sys.argv[3] if len(sys.argv) > 3 else None
print(optimize_content(content, keyword, secondary))
else:
print("Usage: python seo_optimizer.py <file> [primary_keyword] [secondary_keywords]")