摘要:手动做SEO效率太低?用Python搭建SEO自动化流水线,从关键词采集、内容分析、排名监控到数据报告,完整代码+部署指南。
手动做SEO效率太低?用Python搭建SEO自动化流水线,从关键词采集、内容分析、排名监控到数据报告,完整代码+部署指南。
先做一个灵魂拷问:你每天的SEO工作中,有多少时间花在了真正需要创意和判断的事情上?
算下来,60%以上的SEO工作时间花在了重复性、机械性的操作上。 这些工作有价值吗?有,但不值得用人脑来做。
这就是SEO自动化的意义:不是替代SEO策略,而是把重复工作交给代码,让人专注于真正需要判断和创意的部分。
这篇文章,我将用Python搭建一套完整的SEO自动化流水线,覆盖关键词采集、内容分析、排名监控、数据报告4大模块,每个模块都提供可直接运行的代码示例。
在深入代码之前,先明确一个前提:SEO自动化不是”全自动SEO”,而是”把机械部分自动化”。
| 工作类型 | 占比 | 是否可自动化 |
|———|——|————-|
| 数据采集(排名/流量/索引) | 20% | ✅ 完全可自动化 |
| 数据整理与报告 | 15% | ✅ 完全可自动化 |
| 页面技术检查 | 15% | ✅ 大部分可自动化 |
| 关键词研究与扩展 | 10% | ⚠️ 半自动化(需人工筛选) |
| 内容创作 | 20% | ❌ 需人工(AI辅助除外) |
| 策略制定与调整 | 10% | ❌ 需人工 |
| 外链建设 | 10% | ⚠️ 半自动化 |
结论:约60%的SEO工作可以被自动化或半自动化处理。 这意味着,如果你每天工作8小时,自动化可以帮你释放近5小时的时间。
| 收益 | 说明 |
|——|——|
| 效率提升3倍+ | 原本1天的数据工作,1小时完成 |
| 减少人为错误 | 手动复制粘贴的错误率约3-5%,自动化接近0 |
| 数据实时性 | 从”每周看一次数据”变为”每天自动更新” |
| 规模化能力 | 从管理10个关键词到管理1000个关键词,成本不增加 |
| 标准化输出 | 报告格式统一,团队协作更高效 |
关键词是SEO的起点。手动收集关键词效率低、覆盖面窄,用Python可以实现自动化、规模化、多源采集。
百度下拉词是反映用户真实搜索需求的”金矿”,这些词直接来自百度的搜索建议系统,搜索意图明确。
“python
import requests
import json
import time
import pandas as pd
def fetch_baidu_suggestions(keyword):
"""
采集百度下拉词
:param keyword: 种子关键词
:return: 建议词列表
"""
url = "https://suggestion.baidu.com/su"
params = {
"wd": keyword,
"cb": "" # 不使用JSONP回调
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
try:
response = requests.get(url, params=params, headers=headers, timeout=10)
# 百度返回格式: window.baidu.sug({q:"关键词",p:false,s:["词1","词2"]})
text = response.text
# 提取JSON部分
start = text.index("(") + 1
end = text.rindex(")")
data = json.loads(text[start:end])
return data.get("s", [])
except Exception as e:
print(f"采集百度下拉词出错: {e}")
return []
def expand_keywords(seed_keywords, depth=2):
"""
多层扩展关键词
:param seed_keywords: 种子关键词列表
:param depth: 扩展深度(1=只取下拉词,2=对下拉词再取下拉词)
:return: 所有关键词的DataFrame
"""
all_keywords = []
for seed in seed_keywords:
print(f"采集种子词: {seed}")
suggestions = fetch_baidu_suggestions(seed)
for sug in suggestions:
all_keywords.append({
"种子词": seed,
"扩展词": sug,
"来源": "百度下拉",
"层级": 1
})
# 第二层扩展
if depth >= 2:
time.sleep(0.5) # 避免请求过快
sub_suggestions = fetch_baidu_suggestions(sug)
for sub_sug in sub_suggestions:
all_keywords.append({
"种子词": seed,
"扩展词": sub_sug,
"来源": "百度下拉-二级",
"层级": 2
})
time.sleep(1) # 礼貌性延迟
df = pd.DataFrame(all_keywords)
df = df.drop_duplicates(subset=["扩展词"])
return df
if __name__ == "__main__":
seeds = ["SEO优化", "网站排名", "关键词优化"]
keyword_df = expand_keywords(seeds, depth=2)
keyword_df.to_csv("keywords_baidu.csv", index=False, encoding="utf-8-sig")
print(f"共采集 {len(keyword_df)} 个关键词")
`
5118是国内最常用的SEO数据分析平台之一,其API提供丰富的关键词数据,包括搜索量、竞争度等。
`python
import requests
import pandas as pd
import time
class Keyword5118:
"""5118关键词采集器"""
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://apis.5118.com"
def fetch_related_keywords(self, keyword, page=1):
"""
获取相关关键词
:param keyword: 种子关键词
:param page: 页码
:return: 关键词列表
"""
url = f"{self.base_url}/keyword/related"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
params = {
"keyword": keyword,
"page": page
}
try:
response = requests.get(url, headers=headers, params=params, timeout=30)
data = response.json()
if data.get("errcode") == 0:
keywords = []
for item in data.get("data", []):
keywords.append({
"关键词": item.get("keyword", ""),
"搜索量": item.get("search_volume", 0),
"竞争度": item.get("competition", 0),
"竞价价格": item.get("cpc", 0),
"来源": "5118相关词"
})
return keywords
else:
print(f"5118 API错误: {data.get('errmsg', '未知错误')}")
return []
except Exception as e:
print(f"5118请求出错: {e}")
return []
def batch_fetch(self, seed_keywords, max_pages=3):
"""
批量采集关键词
:param seed_keywords: 种子关键词列表
:param max_pages: 每个种子词最大采集页数
:return: 汇总DataFrame
"""
all_keywords = []
for seed in seed_keywords:
for page in range(1, max_pages + 1):
print(f"采集: {seed} - 第{page}页")
result = self.fetch_related_keywords(seed, page)
all_keywords.extend(result)
time.sleep(1) # API限流
df = pd.DataFrame(all_keywords)
if not df.empty:
df = df.drop_duplicates(subset=["关键词"])
df = df.sort_values("搜索量", ascending=False)
return df
if __name__ == "__main__":
API_KEY = "your_5118_api_key_here"
collector = Keyword5118(API_KEY)
seeds = ["SEO优化", "网站推广", "搜索引擎优化"]
keyword_df = collector.batch_fetch(seeds, max_pages=2)
keyword_df.to_csv("keywords_5118.csv", index=False, encoding="utf-8-sig")
print(f"5118共采集 {len(keyword_df)} 个关键词")
`
了解竞争对手在哪些关键词上获得排名,是关键词策略的重要输入。
`python
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
import time
def scrape_competitor_keywords(domain, api_key=None):
"""
通过分析竞品网站的title和meta标签获取其目标关键词
:param domain: 竞品域名
:return: 关键词列表
"""
keywords_found = []
# 方法1:爬取网站sitemap获取所有页面标题
sitemap_url = f"https://{domain}/sitemap.xml"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
try:
# 获取sitemap
response = requests.get(sitemap_url, headers=headers, timeout=15)
if response.status_code == 200:
soup = BeautifulSoup(response.text, "xml")
urls = [loc.text for loc in soup.find_all("loc")][:100] # 限制100个页面
for url in urls:
try:
page_resp = requests.get(url, headers=headers, timeout=10)
page_soup = BeautifulSoup(page_resp.text, "html.parser")
# 提取title
title = page_soup.title.string if page_soup.title else ""
# 提取meta keywords
meta_kw = page_soup.find("meta", attrs={"name": "keywords"})
meta_kw_content = meta_kw.get("content", "") if meta_kw else ""
# 提取meta description
meta_desc = page_soup.find("meta", attrs={"name": "description"})
meta_desc_content = meta_desc.get("content", "") if meta_desc else ""
# 提取H1
h1 = page_soup.find("h1")
h1_text = h1.get_text(strip=True) if h1 else ""
keywords_found.append({
"URL": url,
"Title": title,
"Meta Keywords": meta_kw_content,
"H1": h1_text,
"来源域名": domain
})
time.sleep(0.5)
except Exception as e:
continue
except Exception as e:
print(f"无法获取sitemap: {e}")
return pd.DataFrame(keywords_found)
if __name__ == "__main__":
competitors = ["competitor1.com", "competitor2.com"]
all_dfs = []
for comp in competitors:
print(f"分析竞品: {comp}")
df = scrape_competitor_keywords(comp)
all_dfs.append(df)
result = pd.concat(all_dfs, ignore_index=True)
result.to_csv("competitor_keywords.csv", index=False, encoding="utf-8-sig")
print(f"竞品关键词分析完成,共 {len(result)} 条记录")
`
内容是SEO的核心,但手动检查每一页的SEO要素既耗时又容易遗漏。Python可以帮你快速完成批量内容分析。
`python
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
class ContentAnalyzer:
"""SEO内容分析器"""
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
def analyze_page(self, url):
"""
分析单个页面的SEO要素
:param url: 页面URL
:return: 分析结果字典
"""
result = {
"URL": url,
"Title": "",
"Title长度": 0,
"Title问题": [],
"Description": "",
"Description长度": 0,
"Description问题": [],
"H1": "",
"H1数量": 0,
"H1问题": [],
"图片总数": 0,
"图片缺ALT": 0,
"内链数": 0,
"外链数": 0,
"Canonical": "",
"状态码": 0
}
try:
response = requests.get(url, headers=self.headers, timeout=15)
result["状态码"] = response.status_code
if response.status_code != 200:
result["Title问题"].append(f"页面无法访问({response.status_code})")
return result
soup = BeautifulSoup(response.text, "html.parser")
domain = re.search(r"https?://([^/]+)", url).group(1)
# Title检查
title_tag = soup.find("title")
if title_tag:
result["Title"] = title_tag.string or ""
result["Title长度"] = len(result["Title"])
if result["Title长度"] < 10:
result["Title问题"].append("标题过短(<10字)")
elif result["Title长度"] > 60:
result["Title问题"].append("标题过长(>60字)")
else:
result["Title问题"].append("缺少Title标签")
# Description检查
meta_desc = soup.find("meta", attrs={"name": "description"})
if meta_desc:
result["Description"] = meta_desc.get("content", "")
result["Description长度"] = len(result["Description"])
if result["Description长度"] < 50:
result["Description问题"].append("描述过短(<50字)")
elif result["Description长度"] > 160:
result["Description问题"].append("描述过长(>160字)")
else:
result["Description问题"].append("缺少Description标签")
# H1检查
h1_tags = soup.find_all("h1")
result["H1数量"] = len(h1_tags)
if h1_tags:
result["H1"] = h1_tags[0].get_text(strip=True)
if len(h1_tags) == 0:
result["H1问题"].append("缺少H1标签")
elif len(h1_tags) > 1:
result["H1问题"].append(f"存在{len(h1_tags)}个H1标签(应为1个)")
# 图片ALT检查
images = soup.find_all("img")
result["图片总数"] = len(images)
result["图片缺ALT"] = sum(1 for img in images if not img.get("alt"))
if result["图片缺ALT"] > 0:
result["Title问题"].append(f"{result['图片缺ALT']}张图片缺少ALT")
# 链接检查
links = soup.find_all("a", href=True)
for link in links:
href = link["href"]
if domain in href or href.startswith("/"):
result["内链数"] += 1
elif href.startswith("http"):
result["外链数"] += 1
# Canonical检查
canonical = soup.find("link", rel="canonical")
result["Canonical"] = canonical.get("href", "未设置") if canonical else "未设置"
except Exception as e:
result["Title问题"].append(f"分析出错: {str(e)[:50]}")
return result
def batch_analyze(self, urls):
"""批量分析页面"""
results = []
for i, url in enumerate(urls):
print(f"分析第 {i+1}/{len(urls)} 页: {url}")
result = self.analyze_page(url)
results.append(result)
df = pd.DataFrame(results)
# 生成问题汇总列
df["所有问题"] = df.apply(
lambda row: " | ".join(
filter(None, row["Title问题"] + row["Description问题"] + row["H1问题"])
), axis=1
)
return df
if __name__ == "__main__":
analyzer = ContentAnalyzer()
urls = [
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3"
]
report = analyzer.batch_analyze(urls)
report.to_csv("content_analysis.csv", index=False, encoding="utf-8-sig")
# 输出问题统计
problem_pages = report[report["所有问题"] != ""]
print(f"\n共分析 {len(report)} 个页面,{len(problem_pages)} 个存在问题")
`
`python
import requests
from bs4 import BeautifulSoup
import re
from collections import Counter
def calculate_keyword_density(url, target_keywords):
"""
计算页面的关键词密度
:param url: 页面URL
:param target_keywords: 目标关键词列表
:return: 关键词密度报告
"""
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
try:
response = requests.get(url, headers=headers, timeout=15)
soup = BeautifulSoup(response.text, "html.parser")
# 移除script和style标签
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
text = soup.get_text(separator=" ", strip=True)
# 中文分词简易处理(按字符级别统计)
total_chars = len(text.replace(" ", ""))
results = []
for keyword in target_keywords:
count = text.count(keyword)
# 密度 = 关键词出现次数 × 关键词字数 / 总字数 × 100%
density = (count len(keyword) / total_chars 100) if total_chars > 0 else 0
status = "✅ 正常"
if density < 0.5:
status = "⚠️ 偏低"
elif density > 3:
status = "⚠️ 偏高(可能过度优化)"
results.append({
"关键词": keyword,
"出现次数": count,
"密度": f"{density:.2f}%",
"状态": status
})
return pd.DataFrame(results)
except Exception as e:
print(f"分析出错: {e}")
return pd.DataFrame()
if __name__ == "__main__":
url = "https://example.com/seo-guide"
keywords = ["SEO优化", "关键词排名", "搜索引擎优化", "网站流量"]
density_report = calculate_keyword_density(url, keywords)
print(density_report.to_string(index=False))
`
`python
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import pandas as pd
from collections import defaultdict
class InternalLinkChecker:
"""内链结构检查器"""
def __init__(self, base_domain):
self.base_domain = base_domain
self.visited = set()
self.link_graph = defaultdict(list)
self.broken_links = []
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
def crawl_page_links(self, url, max_pages=50):
"""
爬取网站内链结构
:param url: 起始URL
:param max_pages: 最大爬取页面数
"""
to_visit = [url]
while to_visit and len(self.visited) < max_pages:
current_url = to_visit.pop(0)
if current_url in self.visited:
continue
self.visited.add(current_url)
print(f"爬取: {current_url} ({len(self.visited)}/{max_pages})")
try:
response = requests.get(current_url, headers=self.headers, timeout=10)
if response.status_code != 200:
self.broken_links.append({
"URL": current_url,
"状态码": response.status_code,
"来源": "直接访问"
})
continue
soup = BeautifulSoup(response.text, "html.parser")
links = soup.find_all("a", href=True)
for link in links:
href = link["href"]
full_url = urljoin(current_url, href)
# 只处理同域名链接
if self.base_domain in full_url:
anchor_text = link.get_text(strip=True)[:50]
self.link_graph[current_url].append({
"目标URL": full_url,
"锚文本": anchor_text
})
if full_url not in self.visited:
to_visit.append(full_url)
except Exception as e:
continue
def find_orphan_pages(self):
"""查找孤立页面(没有被任何内链指向的页面)"""
all_linked = set()
for source, links in self.link_graph.items():
for link in links:
all_linked.add(link["目标URL"])
orphan_pages = self.visited - all_linked
return list(orphan_pages)
def get_link_stats(self):
"""获取内链统计"""
stats = []
for page, links in self.link_graph.items():
stats.append({
"页面": page,
"出链数": len(links),
"唯一出链": len(set(l["目标URL"] for l in links))
})
df = pd.DataFrame(stats)
# 被链接数统计
link_counts = Counter()
for links in self.link_graph.values():
for link in links:
link_counts[link["目标URL"]] += 1
return df, dict(link_counts)
if __name__ == "__main__":
checker = InternalLinkChecker("example.com")
checker.crawl_page_links("https://example.com", max_pages=30)
orphans = checker.find_orphan_pages()
print(f"\n孤立页面({len(orphans)}个):")
for page in orphans:
print(f" - {page}")
stats, incoming = checker.get_link_stats()
stats.to_csv("internal_links.csv", index=False, encoding="utf-8-sig")
`
排名监控是SEO工作中最枯燥但也最必要的环节。用Python实现自动排名查询和变化追踪,可以彻底解放双手。
`python
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import re
import json
from datetime import datetime
class RankMonitor:
"""关键词排名监控器"""
def __init__(self, target_domain):
self.target_domain = target_domain
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
def check_rank(self, keyword, search_engine="baidu", pages=5):
"""
查询单个关键词排名
:param keyword: 关键词
:param search_engine: 搜索引擎
:param pages: 检查前几页
:return: 排名信息
"""
result = {
"关键词": keyword,
"排名": "未找到",
"URL": "",
"查询时间": datetime.now().strftime("%Y-%m-%d %H:%M"),
"搜索引擎": search_engine
}
if search_engine == "baidu":
for page in range(pages):
pn = page * 10
url = f"https://www.baidu.com/s?wd={keyword}&pn={pn}"
try:
response = requests.get(url, headers=self.headers, timeout=15)
soup = BeautifulSoup(response.text, "html.parser")
# 百度搜索结果容器
items = soup.find_all("div", class_=re.compile(r"result.*"))
for i, item in enumerate(items):
links = item.find_all("a", href=True)
for link in links:
href = link["href"]
# 检查是否是目标域名的链接
if self.target_domain in href:
actual_rank = page * 10 + i + 1
result["排名"] = actual_rank
result["URL"] = href
return result
time.sleep(2) # 避免频繁请求
except Exception as e:
print(f"查询出错: {e}")
continue
return result
def batch_check(self, keywords, search_engine="baidu"):
"""批量查询排名"""
results = []
total = len(keywords)
for i, keyword in enumerate(keywords):
print(f"查询 {i+1}/{total}: {keyword}")
result = self.check_rank(keyword, search_engine)
results.append(result)
time.sleep(3) # 礼貌性延迟,避免被封
return pd.DataFrame(results)
if __name__ == "__main__":
monitor = RankMonitor("yourdomain.com")
keywords = ["SEO优化", "网站排名提升", "关键词优化方案", "搜索引擎优化教程"]
ranking_df = monitor.batch_check(keywords)
timestamp = datetime.now().strftime("%Y%m%d")
ranking_df.to_csv(f"ranking_{timestamp}.csv", index=False, encoding="utf-8-sig")
print(ranking_df.to_string(index=False))
`
`python
import pandas as pd
from datetime import datetime
import os
class RankTracker:
"""排名变化追踪器"""
def __init__(self, data_dir="rank_data"):
self.data_dir = data_dir
os.makedirs(data_dir, exist_ok=True)
def save_daily_data(self, rank_df):
"""保存每日排名数据"""
date_str = datetime.now().strftime("%Y%m%d")
filepath = os.path.join(self.data_dir, f"rank_{date_str}.csv")
rank_df.to_csv(filepath, index=False, encoding="utf-8-sig")
print(f"排名数据已保存: {filepath}")
def track_changes(self, days=7):
"""
追踪排名变化
:param days: 对比最近多少天的变化
"""
files = sorted([f for f in os.listdir(self.data_dir) if f.startswith("rank_")])
if len(files) < 2:
print("数据不足,至少需要2天的排名数据")
return pd.DataFrame()
# 读取最新和对比数据
latest_file = os.path.join(self.data_dir, files[-1])
compare_file = os.path.join(self.data_dir, files[-min(days+1, len(files))])
latest_df = pd.read_csv(latest_file)
compare_df = pd.read_csv(compare_file)
# 合并计算变化
latest_df["排名_最新"] = pd.to_numeric(latest_df["排名"], errors="coerce")
compare_df["排名_对比"] = pd.to_numeric(compare_df["排名"], errors="coerce")
merged = pd.merge(
latest_df[["关键词", "排名_最新"]],
compare_df[["关键词", "排名_对比"]],
on="关键词",
how="outer"
)
merged["变化"] = merged["排名_对比"] - merged["排名_最新"]
merged["变化趋势"] = merged["变化"].apply(
lambda x: "⬆️ 上升" if x > 0 else ("⬇️ 下降" if x < 0 else "➡️ 持平")
)
merged["变化幅度"] = merged["变化"].apply(
lambda x: f"+{int(x)}位" if x > 0 else (f"{int(x)}位" if x < 0 else "不变")
)
# 标记异常波动
merged["异常"] = merged["变化"].apply(
lambda x: "⚠️ 大幅波动" if abs(x) >= 10 else ""
)
return merged.sort_values("变化", ascending=False)
if __name__ == "__main__":
tracker = RankTracker()
# 假设已有今日排名数据
# today_ranking = monitor.batch_check(keywords)
# tracker.save_daily_data(today_ranking)
changes = tracker.track_changes(days=7)
print(changes.to_string(index=False))
`
数据报告是SEO工作的"成果展示",但手动制作报告是最耗时的工作之一。用Python可以自动生成专业级SEO报告。
`python
import pandas as pd
from datetime import datetime, timedelta
import os
import json
class SEOReportGenerator:
"""SEO数据报告生成器"""
def __init__(self, site_name, output_dir="reports"):
self.site_name = site_name
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
def generate_weekly_report(self, rank_data_dir="rank_data"):
"""
生成周报
:param rank_data_dir: 排名数据目录
:return: 报告内容字典
"""
report = {
"网站": self.site_name,
"报告类型": "周报",
"报告周期": f"{(datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')} 至 {datetime.now().strftime('%Y-%m-%d')}",
"生成时间": datetime.now().strftime("%Y-%m-%d %H:%M"),
"核心指标": {},
"排名变化Top5": [],
"异常告警": [],
"建议动作": []
}
# 读取排名数据
tracker = RankTracker(rank_data_dir)
changes = tracker.track_changes(days=7)
if not changes.empty:
# 核心指标
total_keywords = len(changes)
ranked_keywords = changes[changes["排名_最新"].notna()]
top10_keywords = ranked_keywords[ranked_keywords["排名_最新"] <= 10]
top3_keywords = ranked_keywords[ranked_keywords["排名_最新"] <= 3]
improved = changes[changes["变化"] > 0]
declined = changes[changes["变化"] < 0]
report["核心指标"] = {
"监控关键词总数": total_keywords,
"有排名关键词数": len(ranked_keywords),
"TOP10关键词数": len(top10_keywords),
"TOP3关键词数": len(top3_keywords),
"排名上升词数": len(improved),
"排名下降词数": len(declined),
"排名不变词数": len(changes[changes["变化"] == 0])
}
# 排名变化Top5
if not improved.empty:
top_improved = improved.nlargest(5, "变化")[["关键词", "变化幅度"]].to_dict("records")
report["排名变化Top5"] = top_improved
# 异常告警
abnormal = changes[changes["异常"] != ""]
if not abnormal.empty:
report["异常告警"] = abnormal[["关键词", "变化幅度", "异常"]].to_dict("records")
# 自动生成建议
if len(declined) > len(improved):
report["建议动作"].append("📊 本周下降词数多于上升词数,建议重点检查下降关键词的页面内容")
if len(top3_keywords) < total_keywords * 0.1:
report["建议动作"].append("🎯 TOP3关键词占比低于10%,建议集中资源优化高潜力词")
if len(abnormal) > 0:
report["建议动作"].append(f"⚠️ 有{len(abnormal)}个关键词出现大幅波动,需排查原因")
return report
def save_report(self, report, format="markdown"):
"""
保存报告
:param report: 报告内容
:param format: 输出格式 (markdown/json)
"""
date_str = datetime.now().strftime("%Y%m%d")
if format == "json":
filepath = os.path.join(self.output_dir, f"seo_report_{date_str}.json")
with open(filepath, "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
elif format == "markdown":
filepath = os.path.join(self.output_dir, f"seo_report_{date_str}.md")
md_content = self._to_markdown(report)
with open(filepath, "w", encoding="utf-8") as f:
f.write(md_content)
print(f"报告已保存: {filepath}")
return filepath
def _to_markdown(self, report):
"""将报告转为Markdown格式"""
lines = [
f"# {report['网站']} SEO周报",
f"",
f"报告周期:{report['报告周期']}",
f"生成时间:{report['生成时间']}",
f"",
f"## 核心指标",
f"",
f"| 指标 | 数值 |",
f"|------|------|"
]
for key, value in report["核心指标"].items():
lines.append(f"| {key} | {value} |")
if report["排名变化Top5"]:
lines.extend(["", "## 排名上升Top5", "", "| 关键词 | 变化 |", "|--------|------|"])
for item in report["排名变化Top5"]:
lines.append(f"| {item['关键词']} | {item['变化幅度']} |")
if report["异常告警"]:
lines.extend(["", "## ⚠️ 异常告警", ""])
for alert in report["异常告警"]:
lines.append(f"- {alert['关键词']}:{alert['变化幅度']} {alert['异常']}")
if report["建议动作"]:
lines.extend(["", "## 建议动作", ""])
for action in report["建议动作"]:
lines.append(f"- {action}")
return "\n".join(lines)
if __name__ == "__main__":
generator = SEOReportGenerator("我的网站")
report = generator.generate_weekly_report()
filepath = generator.save_report(report, format="markdown")
# 同时保存JSON格式供其他系统调用
generator.save_report(report, format="json")
`
`python
import smtplib
from email.mime.text import MIMEText
import json
import os
class SEOAlertSystem:
"""SEO异常告警系统"""
def __init__(self, config_file="alert_config.json"):
self.config = self._load_config(config_file)
def _load_config(self, config_file):
"""加载告警配置"""
default_config = {
"alert_rules": {
"rank_drop_threshold": 10, # 排名下降超过10位告警
"rank_drop_top5": True, # TOP5关键词掉出TOP10告警
"new_page_not_indexed_days": 7, # 新页面7天未被收录告警
"traffic_drop_percent": 30 # 流量下降超过30%告警
},
"notification": {
"email": {
"enabled": False,
"smtp_server": "",
"smtp_port": 465,
"sender": "",
"password": "",
"recipients": []
},
"webhook": {
"enabled": False,
"url": "" # 企业微信/钉钉webhook
}
}
}
if os.path.exists(config_file):
with open(config_file, "r", encoding="utf-8") as f:
return json.load(f)
else:
with open(config_file, "w", encoding="utf-8") as f:
json.dump(default_config, f, ensure_ascii=False, indent=2)
return default_config
def check_alerts(self, rank_changes_df):
"""
检查是否触发告警
:param rank_changes_df: 排名变化DataFrame
:return: 告警列表
"""
alerts = []
rules = self.config["alert_rules"]
# 规则1:排名大幅下降
threshold = rules.get("rank_drop_threshold", 10)
big_drops = rank_changes_df[rank_changes_df["变化"] <= -threshold]
for _, row in big_drops.iterrows():
alerts.append({
"级别": "🔴 严重",
"类型": "排名大幅下降",
"详情": f"关键词「{row['关键词']}」下降{abs(int(row['变化']))}位",
"建议": "检查该关键词对应页面是否有技术问题或内容被篡改"
})
# 规则2:TOP5关键词掉出TOP10
if rules.get("rank_drop_top5", True):
fell_out = rank_changes_df[
(rank_changes_df["排名_对比"] <= 5) &
(rank_changes_df["排名_最新"] > 10)
]
for _, row in fell_out.iterrows():
alerts.append({
"级别": "🟡 警告",
"类型": "核心词掉出TOP10",
"详情": f"关键词「{row['关键词']}」从第{int(row['排名_对比'])}位降至第{int(row['排名_最新'])}位",
"建议": "优先排查:竞品是否有新动作、页面是否有技术异常"
})
return alerts
def send_alerts(self, alerts):
"""发送告警通知"""
if not alerts:
return
# 构建告警消息
message_lines = ["🚨 SEO异常告警\n"]
for alert in alerts:
message_lines.append(f"{alert['级别']} {alert['类型']}")
message_lines.append(f" 详情:{alert['详情']}")
message_lines.append(f" 建议:{alert['建议']}\n")
message = "\n".join(message_lines)
print(message)
# Webhook通知(企业微信/钉钉)
webhook_config = self.config["notification"].get("webhook", {})
if webhook_config.get("enabled") and webhook_config.get("url"):
self._send_webhook(webhook_config["url"], message)
def _send_webhook(self, url, message):
"""发送Webhook通知"""
import requests
try:
payload = {
"msgtype": "text",
"text": {"content": message}
}
requests.post(url, json=payload, timeout=10)
except Exception as e:
print(f"Webhook发送失败: {e}")
if __name__ == "__main__":
alert_system = SEOAlertSystem()
# 假设有排名变化数据
# changes = tracker.track_changes(days=7)
# alerts = alert_system.check_alerts(changes)
# alert_system.send_alerts(alerts)
`
代码写好了,还需要选择合适的部署方式让它们定时运行。
最简单的方式,适合个人SEO从业者或小团队。
`bash
seo-automation/
├── config/
│ └── settings.json # 配置文件
├── modules/
│ ├── keyword_collector.py # 关键词采集
│ ├── content_analyzer.py # 内容分析
│ ├── rank_monitor.py # 排名监控
│ └── report_generator.py # 报告生成
├── data/
│ ├── keywords/ # 关键词数据
│ ├── rank_data/ # 排名数据
│ └── reports/ # 生成的报告
├── main.py # 主入口
└── requirements.txt # 依赖
requests>=2.28.0
beautifulsoup4>=4.11.0
pandas>=1.5.0
lxml>=4.9.0
`
`python
import json
from datetime import datetime
from modules.keyword_collector import expand_keywords, Keyword5118
from modules.content_analyzer import ContentAnalyzer
from modules.rank_monitor import RankMonitor
from modules.report_generator import SEOReportGenerator, RankTracker
def run_daily_tasks():
"""每日执行任务"""
print(f"=== SEO自动化任务开始 {datetime.now()} ===")
# 加载配置
with open("config/settings.json", "r") as f:
config = json.load(f)
# 任务1:排名监控
print("\n📊 任务1:排名监控")
monitor = RankMonitor(config["target_domain"])
rank_df = monitor.batch_check(config["monitor_keywords"])
tracker = RankTracker()
tracker.save_daily_data(rank_df)
# 任务2:内容分析(每周一执行)
if datetime.now().weekday() == 0:
print("\n📝 任务2:内容分析")
analyzer = ContentAnalyzer()
content_report = analyzer.batch_analyze(config["site_pages"])
content_report.to_csv(f"data/content_analysis_{datetime.now().strftime('%Y%m%d')}.csv",
index=False, encoding="utf-8-sig")
# 任务3:生成周报(每周一执行)
if datetime.now().weekday() == 0:
print("\n📋 任务3:生成周报")
generator = SEOReportGenerator(config["site_name"])
report = generator.generate_weekly_report()
generator.save_report(report, format="markdown")
print(f"\n=== 任务完成 {datetime.now()} ===")
if __name__ == "__main__":
run_daily_tasks()
`
适合需要7×24小时运行的场景,推荐使用轻量级云服务器。
`bash
0 8 * cd /home/user/seo-automation && /usr/bin/python3 main.py >> /var/log/seo_automation.log 2>&1
0 9 1 cd /home/user/seo-automation && /usr/bin/python3 main.py --full >> /var/log/seo_automation.log 2>&1
`
云服务器推荐配置:
| 配置项 | 推荐值 | 说明 |
|-------|--------|------|
| CPU | 1核 | SEO自动化不需要高算力 |
| 内存 | 2GB | pandas处理数据足够 |
| 硬盘 | 40GB SSD | 数据存储 |
| 带宽 | 1Mbps | API请求为主,不需要大带宽 |
| 系统 | Ubuntu 22.04 LTS | 稳定且Python生态完善 |
| 月成本 | 约30-50元 | 轻量应用服务器即可 |
对于更复杂的定时任务管理,推荐使用APScheduler:
`python
from apscheduler.schedulers.blocking import BlockingScheduler
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
scheduler = BlockingScheduler()
@scheduler.scheduled_job("cron", hour=8, minute=0)
def daily_rank_check():
logger.info("开始每日排名监控")
run_daily_tasks()
@scheduler.scheduled_job("cron", day_of_week="mon", hour=9, minute=0)
def weekly_full_analysis():
logger.info("开始每周完整分析")
run_daily_tasks()
@scheduler.scheduled_job("cron", day=1, hour=10, minute=0)
def monthly_keyword_expansion():
logger.info("开始每月关键词扩展")
# 关键词扩展逻辑
if __name__ == "__main__":
logger.info("SEO自动化调度器启动")
scheduler.start()
`
自动化不是万能的。明确边界,才能让自动化发挥最大价值,避免"过度自动化"带来的风险。
| 工作类型 | 自动化程度 | 说明 |
|---------|-----------|------|
| 数据采集 | 95% | 排名、流量、索引量等数据采集完全可自动化 |
| 数据可视化 | 90% | 自动生成图表和报告,只需人工解读 |
| 技术审计 | 80% | 死链、404、重复内容等技术问题可自动检测 |
| 关键词初筛 | 70% | 批量采集+初步筛选可自动化,最终选择需人工 |
| 竞品监控 | 75% | 自动检测竞品排名变化和内容更新 |
| 报告生成 | 85% | 数据报告自动生成,策略建议需人工审核 |
| 工作类型 | 人工必要性 | 原因 |
|---------|-----------|------|
| 内容创作 | 必须人工 | 内容需要创意、观点和行业深度 |
| 策略制定 | 必须人工 | 策略需要理解业务目标和竞争环境 |
| 关键词意图判断 | 必须人工 | 搜索意图需要人来判断和分类 |
| 外链质量评估 | 必须人工 | 低质量外链可能带来惩罚风险 |
| 算法更新应对 | 必须人工 | 需要理解算法变化并调整策略 |
| 客户沟通 | 必须人工 | SEO方案需要解释和说服 |
| 风险 | 说明 | 预防措施 |
|------|------|----------|
| 数据误判 | 自动化脚本可能误读搜索结果 | 定期人工抽样验证 |
| 封禁风险 | 频繁请求可能触发反爬机制 | 控制请求频率,使用代理池 |
| 策略偏差 | 过度依赖数据,忽视业务逻辑 | 数据驱动+人工判断双轨制 |
| 维护成本 | 脚本需要随搜索引擎更新而调整 | 预留20%时间用于脚本维护 |
| 安全风险 | API密钥泄露、数据暴露 | 使用环境变量存储敏感信息 |
团队:3人SEO团队,负责一个B2B企业网站(约500个页面) 痛点:
第1阶段(第1-2周):排名监控自动化
效果:排名查询时间从每天2小时降为0,数据实时性从"隔天看"变为"每天更新"。
第2阶段(第3-4周):内容分析自动化
效果:页面审计时间从每月5小时降为0.5小时(仅需审核报告)。
第3阶段(第5-6周):关键词扩展自动化
效果:关键词扩展效率提升5倍,发现了很多之前忽略的长尾词机会。
第4阶段(第7-8周):报告自动化
效果:报告制作时间从每周3小时降为0.5小时,报告质量反而更高(数据更全、格式更统一)。
| 指标 | 自动化前 | 自动化后 | 提升幅度 |
|------|---------|---------|---------|
| 日均SEO工作时间 | 8小时 | 2.5小时 | 效率提升3.2倍 |
| 排名数据时效性 | 隔天 | 当天 | 实时性提升 |
| 月度关键词发现量 | 50-80个 | 300-500个 | 提升5倍+ |
| 页面问题发现率 | 约30% | 95%+ | 提升3倍 |
| 周报制作时间 | 3小时 | 0.5小时 | 减少83% |
| SEO流量(3个月后) | 基准 | +47% | 自然流量增长47% |
1. 不要一次全部自动化:分阶段实施,每个阶段跑通后再进入下一阶段
2. 人工验证不可少:自动化的第一周,每天人工抽查数据准确性
3. 脚本是活的:搜索引擎会改版,脚本需要定期维护,预留20%时间
4. 先做最痛的点:从最耗时的任务开始自动化,ROI最高
5. 数据安全第一:API密钥用环境变量,不在代码中硬编码
6. 保留灵活性:配置文件化,不重写代码就能调整监控关键词和告警阈值
1. 今天:安装Python环境,pip install requests beautifulsoup4 pandas`
2. 本周:选择排名监控模块作为第一个自动化项目,跑通RankMonitor代码
3. 本月:逐步部署内容分析和报告生成模块,形成完整的自动化流水线
4. 持续:每周抽查自动化数据的准确性,每月维护和优化脚本
记住:SEO自动化的目标不是取代人,而是让人做更有价值的事。 把重复工作交给代码,把策略和创意留给自己。