首页 / 推广博客 / 专题深度
专题深度

Python搭建SEO自动化流水线:从关键词到发布的全代码

手动做SEO效率太低?用Python搭建SEO自动化流水线,从关键词采集、内容分析、排名监控到数据报告,完整代码+部署指南。

2026-06-18 阅读约 57 分钟
目录

摘要:手动做SEO效率太低?用Python搭建SEO自动化流水线,从关键词采集、内容分析、排名监控到数据报告,完整代码+部署指南。

Python搭建SEO自动化流水线:从关键词到发布的全代码

手动做SEO效率太低?用Python搭建SEO自动化流水线,从关键词采集、内容分析、排名监控到数据报告,完整代码+部署指南。

引言:你的SEO工作,60%以上是重复劳动

先做一个灵魂拷问:你每天的SEO工作中,有多少时间花在了真正需要创意和判断的事情上?

  • 手动查排名、复制粘贴到Excel——每天30分钟
  • 逐个检查页面的标题、描述、关键词密度——每篇15分钟
  • 打开5个工具交叉对比数据——每周2小时
  • 写周报、月报,从各处截图拼数据——每周1小时
  • 检查内链、外链、死链——每周1.5小时

算下来,60%以上的SEO工作时间花在了重复性、机械性的操作上。 这些工作有价值吗?有,但不值得用人脑来做。

这就是SEO自动化的意义:不是替代SEO策略,而是把重复工作交给代码,让人专注于真正需要判断和创意的部分。

这篇文章,我将用Python搭建一套完整的SEO自动化流水线,覆盖关键词采集、内容分析、排名监控、数据报告4大模块,每个模块都提供可直接运行的代码示例。


一、SEO为什么需要自动化:重复工作占比60%+

在深入代码之前,先明确一个前提:SEO自动化不是”全自动SEO”,而是”把机械部分自动化”。

1.1 SEO工作的时间分配

| 工作类型 | 占比 | 是否可自动化 |

|———|——|————-|

| 数据采集(排名/流量/索引) | 20% | ✅ 完全可自动化 |

| 数据整理与报告 | 15% | ✅ 完全可自动化 |

| 页面技术检查 | 15% | ✅ 大部分可自动化 |

| 关键词研究与扩展 | 10% | ⚠️ 半自动化(需人工筛选) |

| 内容创作 | 20% | ❌ 需人工(AI辅助除外) |

| 策略制定与调整 | 10% | ❌ 需人工 |

| 外链建设 | 10% | ⚠️ 半自动化 |

结论:约60%的SEO工作可以被自动化或半自动化处理。 这意味着,如果你每天工作8小时,自动化可以帮你释放近5小时的时间。

1.2 自动化的收益量化

| 收益 | 说明 |

|——|——|

| 效率提升3倍+ | 原本1天的数据工作,1小时完成 |

| 减少人为错误 | 手动复制粘贴的错误率约3-5%,自动化接近0 |

| 数据实时性 | 从”每周看一次数据”变为”每天自动更新” |

| 规模化能力 | 从管理10个关键词到管理1000个关键词,成本不增加 |

| 标准化输出 | 报告格式统一,团队协作更高效 |


二、自动化流水线模块1:关键词采集

关键词是SEO的起点。手动收集关键词效率低、覆盖面窄,用Python可以实现自动化、规模化、多源采集。

2.1 百度下拉词采集

百度下拉词是反映用户真实搜索需求的”金矿”,这些词直接来自百度的搜索建议系统,搜索意图明确。

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)} 个关键词")
`

2.2 通过5118 API采集关键词

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)} 个关键词")
`

2.3 竞品关键词抓取

了解竞争对手在哪些关键词上获得排名,是关键词策略的重要输入。

`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)} 条记录")
`


三、自动化流水线模块2:内容分析

内容是SEO的核心,但手动检查每一页的SEO要素既耗时又容易遗漏。Python可以帮你快速完成批量内容分析。

3.1 标题与描述检查器

`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)} 个存在问题")
`

3.2 关键词密度计算器

`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))
`

3.3 内链结构检查器

`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")
`


四、自动化流水线模块3:排名监控

排名监控是SEO工作中最枯燥但也最必要的环节。用Python实现自动排名查询和变化追踪,可以彻底解放双手。

4.1 批量关键词排名查询

`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))
`

4.2 排名变化追踪

`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))
`


五、自动化流水线模块4:数据报告

数据报告是SEO工作的"成果展示",但手动制作报告是最耗时的工作之一。用Python可以自动生成专业级SEO报告。

5.1 自动生成周报/月报

`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")
`

5.2 异常告警系统

`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)
`


六、部署方式:让自动化真正跑起来

代码写好了,还需要选择合适的部署方式让它们定时运行。

6.1 本地运行

最简单的方式,适合个人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 # 依赖

requirements.txt

requests>=2.28.0
beautifulsoup4>=4.11.0
pandas>=1.5.0
lxml>=4.9.0
`

`python

main.py - 一键运行所有模块

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()

`

6.2 云服务器部署

适合需要7×24小时运行的场景,推荐使用轻量级云服务器。

`bash

使用crontab设置定时任务

每天早上8点执行排名监控

0 8 * cd /home/user/seo-automation && /usr/bin/python3 main.py >> /var/log/seo_automation.log 2>&1

每周一早上9点执行完整分析和报告

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元 | 轻量应用服务器即可 |

6.3 定时任务框架

对于更复杂的定时任务管理,推荐使用APScheduler:

`python

from apscheduler.schedulers.blocking import BlockingScheduler

import logging

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

scheduler = BlockingScheduler()

每天8:00执行排名监控

@scheduler.scheduled_job("cron", hour=8, minute=0)
def daily_rank_check():
logger.info("开始每日排名监控")
run_daily_tasks()

每周一9:00执行完整分析

@scheduler.scheduled_job("cron", day_of_week="mon", hour=9, minute=0)
def weekly_full_analysis():
logger.info("开始每周完整分析")
run_daily_tasks()

每月1号10:00执行关键词扩展

@scheduler.scheduled_job("cron", day=1, hour=10, minute=0)
def monthly_keyword_expansion():
logger.info("开始每月关键词扩展")
# 关键词扩展逻辑

if __name__ == "__main__":

logger.info("SEO自动化调度器启动")

scheduler.start()

`


七、自动化的边界:哪些可以自动化,哪些需要人工判断

自动化不是万能的。明确边界,才能让自动化发挥最大价值,避免"过度自动化"带来的风险。

7.1 可以自动化的工作

| 工作类型 | 自动化程度 | 说明 |

|---------|-----------|------|

| 数据采集 | 95% | 排名、流量、索引量等数据采集完全可自动化 |

| 数据可视化 | 90% | 自动生成图表和报告,只需人工解读 |

| 技术审计 | 80% | 死链、404、重复内容等技术问题可自动检测 |

| 关键词初筛 | 70% | 批量采集+初步筛选可自动化,最终选择需人工 |

| 竞品监控 | 75% | 自动检测竞品排名变化和内容更新 |

| 报告生成 | 85% | 数据报告自动生成,策略建议需人工审核 |

7.2 必须人工判断的工作

| 工作类型 | 人工必要性 | 原因 |

|---------|-----------|------|

| 内容创作 | 必须人工 | 内容需要创意、观点和行业深度 |

| 策略制定 | 必须人工 | 策略需要理解业务目标和竞争环境 |

| 关键词意图判断 | 必须人工 | 搜索意图需要人来判断和分类 |

| 外链质量评估 | 必须人工 | 低质量外链可能带来惩罚风险 |

| 算法更新应对 | 必须人工 | 需要理解算法变化并调整策略 |

| 客户沟通 | 必须人工 | SEO方案需要解释和说服 |

7.3 "过度自动化"的风险警示

| 风险 | 说明 | 预防措施 |

|------|------|----------|

| 数据误判 | 自动化脚本可能误读搜索结果 | 定期人工抽样验证 |

| 封禁风险 | 频繁请求可能触发反爬机制 | 控制请求频率,使用代理池 |

| 策略偏差 | 过度依赖数据,忽视业务逻辑 | 数据驱动+人工判断双轨制 |

| 维护成本 | 脚本需要随搜索引擎更新而调整 | 预留20%时间用于脚本维护 |

| 安全风险 | API密钥泄露、数据暴露 | 使用环境变量存储敏感信息 |


八、实操经验:自动化后SEO效率提升3倍

8.1 项目背景

团队:3人SEO团队,负责一个B2B企业网站(约500个页面) 痛点

  • 每天花2小时手动查排名、做记录
  • 每周花3小时制作周报
  • 关键词扩展靠人工搜索,每月只能扩展50-80个词
  • 内链检查几乎不做,全靠"感觉"
  • 页面SEO问题靠随机发现,没有系统性检查

8.2 自动化实施过程

第1阶段(第1-2周):排名监控自动化

  • 部署RankMonitor,自动每日查询120个核心关键词排名
  • 配置RankTracker,自动对比排名变化
  • 搭建SEOAlertSystem,TOP5关键词掉出TOP10立即企业微信告警

效果:排名查询时间从每天2小时降为0,数据实时性从"隔天看"变为"每天更新"。

第2阶段(第3-4周):内容分析自动化

  • 部署ContentAnalyzer,每月自动检查全部500个页面的SEO要素
  • 发现了73个页面存在SEO问题(缺H1、描述过长、图片缺ALT等)
  • 用2周时间集中修复,修复后页面平均排名提升3.2位

效果:页面审计时间从每月5小时降为0.5小时(仅需审核报告)。

第3阶段(第5-6周):关键词扩展自动化

  • 部署百度下拉词采集+5118 API,每月自动扩展300-500个关键词
  • 人工筛选后,每月新增50-80个高价值关键词进入优化列表
  • 为每个新关键词创建对应的内容计划

效果:关键词扩展效率提升5倍,发现了很多之前忽略的长尾词机会。

第4阶段(第7-8周):报告自动化

  • 部署SEOReportGenerator,自动生成Markdown格式周报
  • 告警系统接入企业微信,异常情况实时通知
  • 月报自动汇总4周数据,生成趋势分析

效果:报告制作时间从每周3小时降为0.5小时,报告质量反而更高(数据更全、格式更统一)。

8.3 量化成果

| 指标 | 自动化前 | 自动化后 | 提升幅度 |

|------|---------|---------|---------|

| 日均SEO工作时间 | 8小时 | 2.5小时 | 效率提升3.2倍 |

| 排名数据时效性 | 隔天 | 当天 | 实时性提升 |

| 月度关键词发现量 | 50-80个 | 300-500个 | 提升5倍+ |

| 页面问题发现率 | 约30% | 95%+ | 提升3倍 |

| 周报制作时间 | 3小时 | 0.5小时 | 减少83% |

| SEO流量(3个月后) | 基准 | +47% | 自然流量增长47% |

8.4 关键经验

1. 不要一次全部自动化:分阶段实施,每个阶段跑通后再进入下一阶段

2. 人工验证不可少:自动化的第一周,每天人工抽查数据准确性

3. 脚本是活的:搜索引擎会改版,脚本需要定期维护,预留20%时间

4. 先做最痛的点:从最耗时的任务开始自动化,ROI最高

5. 数据安全第一:API密钥用环境变量,不在代码中硬编码

6. 保留灵活性:配置文件化,不重写代码就能调整监控关键词和告警阈值


下一步行动

1. 今天:安装Python环境,pip install requests beautifulsoup4 pandas`

2. 本周:选择排名监控模块作为第一个自动化项目,跑通RankMonitor代码

3. 本月:逐步部署内容分析和报告生成模块,形成完整的自动化流水线

4. 持续:每周抽查自动化数据的准确性,每月维护和优化脚本

记住:SEO自动化的目标不是取代人,而是让人做更有价值的事。 把重复工作交给代码,把策略和创意留给自己。


相关文章推荐

  • [S01-企业SEO实战:B2B网站从0到1的搜索流量增长指南](S01-企业SEO实战B2B网站从0到1的搜索流量增长指南.md)
  • [S02-电商SEO全链路优化:从商品页到结账页的转化提升策略](S02-电商SEO全链路优化从商品页到结账页的转化提升策略.md)
  • [S03-本地服务SEO:餐饮/装修/健身的同城获客方法论](S03-本地服务SEO餐饮装修健身的同城获客方法论.md)
SEO自动化 Python
10年网络推广实战经验,服务200+企业。专注企业网络推广外包与推广培训,擅长用系统化的方法论让推广投入产生可量化回报。
从阅读到行动 — 找到适合你的推广路径
真实验证 — 文章里的方法,我们在真实项目中验证过
全部案例 →

看完文章还是不知道怎么做?

免费获取一份针对你企业的推广诊断报告,包含现状分析+3条具体建议,帮你找到最适合的推广路径。

免费推广诊断 →