技能标识:website-auditor
Audit any website across 8 quality signals to determine if it is outdated, broken, or neglected. Returns a structured audit dict used by the lead-scorer skill.
对任意URL运行8项自动化检查。返回由lead-scorer消费的结构化字典。
python
{
url: https://example.com,
domain: example.com,
statuscode: 200, # 或 DEAD, TIMEOUT, SSLERROR
is_live: True,
copyright_year: 2018,
years_outdated: 7,
last_modified: 2019-03-15, # 或 None
tech_stack: [WordPress 4.9, jQuery 1.11, PHP],
hasoutdatedcms: True,
pagespeed_mobile: 32, # 0–100 或 None(如果API失败)
pagespeed_seo: 61,
ismobilefriendly: False,
has_ssl: True,
designsignals: [tablelayout, noopengraph, no_favicon],
raw_html: ..., # 由contact-enrichment使用
audit_timestamp: 2025-03-02T17:00:00
}
python
import requests
HEADERS = {
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
}
def check_status(url: str) -> dict:
try:
r = requests.get(url, headers=HEADERS, timeout=10, allow_redirects=True)
return {
statuscode: r.statuscode,
islive: r.statuscode < 400,
final_url: r.url,
rawhtml: r.text if r.statuscode < 400 else ,
response_headers: dict(r.headers)
}
except requests.exceptions.SSLError:
return {statuscode: SSLERROR, islive: False, rawhtml: , has_ssl: False}
except requests.exceptions.ConnectionError:
return {statuscode: DEAD, islive: False, raw_html: }
except requests.exceptions.Timeout:
return {statuscode: TIMEOUT, islive: False, raw_html: }
python
import re
from datetime import datetime
def getcopyrightyear(html: str) -> dict:
# 匹配 © 2019, Copyright 2019, (c) 2019, © 2019
pattern = r(?:©|©|copyright|\(c\))\s(?:\d{4}\s[-–]\s*)?(\d{4})
matches = re.findall(pattern, html, re.IGNORECASE)
if not matches:
return {copyrightyear: None, yearsoutdated: None}
latest_year = max(int(y) for y in matches)
current_year = datetime.now().year
yearsoutdated = currentyear - latest_year
return {
copyrightyear: latestyear,
yearsoutdated: max(0, yearsoutdated)
}
python
from datetime import datetime
from email.utils import parsedatetodatetime
def getlastmodified(headers: dict) -> str | None:
lm = headers.get(Last-Modified) or headers.get(last-modified)
if not lm:
return None
try:
dt = parsedatetodatetime(lm)
return dt.strftime(%Y-%m-%d)
except Exception:
return lm # 如果解析失败则返回原始字符串
python
from Wappalyzer import Wappalyzer, WebPage
OUTDATED_TECH = {
Joomla, Drupal 6, Drupal 7, osCommerce,
Magento 1, vBulletin, phpBB, Flash,
Silverlight, ASP.NET WebForms
}
OUTDATED_JS = {jQuery 1., jQuery 2., MooTools, Prototype}
def detecttechstack(url: str, html: str, headers: dict) -> dict:
try:
wappalyzer = Wappalyzer.latest()
webpage = WebPage(url, html=html, headers=headers)
techs = wappalyzer.analyzewithcategories(webpage)
tech_list = list(techs.keys())
has_outdated = any(
any(bad in t for bad in OUTDATED_TECH) or
any(t.startswith(js) for js in OUTDATED_JS)
for t in tech_list
)
# 检查非常旧的WordPress版本
for t in tech_list:
if t.startswith(WordPress):
try:
ver = float(t.split( )[1][:3])
if ver < 5.0:
has_outdated = True
except:
pass
return {techstack: techlist, hasoutdatedcms: has_outdated}
except Exception as e:
return {techstack: [], hasoutdatedcms: False, techerror: str(e)}
python
import requests, os
def get_pagespeed(url: str) -> dict:
apikey = os.environ.get(PAGESPEEDAPI_KEY)
if not api_key:
return {pagespeedmobile: None, pagespeedseo: None}
endpoint = https://www.googleapis.com/pagespeedonline/v5/runPagespeed
params = {url: url, key: api_key, strategy: mobile}
try:
r = requests.get(endpoint, params=params, timeout=30)
data = r.json()
cats = data.get(lighthouseResult, {}).get(categories, {})
return {
pagespeed_mobile: round(cats.get(performance, {}).get(score, 0) * 100),
pagespeed_seo: round(cats.get(seo, {}).get(score, 0) * 100),
pagespeed_accessibility: round(cats.get(accessibility, {}).get(score, 0) * 100),
}
except Exception as e:
return {pagespeedmobile: None, pagespeedseo: None, pagespeed_error: str(e)}
python
from bs4 import BeautifulSoup
def checkmobilefriendly(html: str) -> bool:
soup = BeautifulSoup(html, lxml)
viewport = soup.find(meta, attrs={name: lambda x: x and x.lower() == viewport})
return viewport is not None
python
import ssl, socket, tldextract
def check_ssl(url: str) -> bool:
如果存在有效SSL返回True,如果没有SSL或证书无效返回False。
if url.startswith(http://):
return False
ext = tldextract.extract(url)
domain = f{ext.domain}.{ext.suffix}
try:
ctx = ssl.createdefaultcontext()
conn = ctx.wrapsocket(socket.socket(), serverhostname=domain)
conn.settimeout(5)
conn.connect((domain, 443))
conn.close()
return True
except Exception:
return False
python
from bs4 import BeautifulSoup
def detectdesignsignals(html: str, url: str) -> list[str]:
soup = BeautifulSoup(html, lxml)
signals = []
# 1. 基于表格的布局(老派)
tables = soup.find_all(table)
nondatatables = [t for t in tables if not t.find_parent([table, th, td])]
if len(nondatatables) > 3:
signals.append(table_layout)
# 2. Flash或Silverlight
if soup.find(object) or soup.find(embed):
if any(x in html.lower() for x in [swf, flash, silverlight]):
signals.append(flash_detected)
# 3. 大量内联样式(前CSS时代)
inlinecount = len(soup.findall(style=True))
if inline_count > 15:
signals.append(heavyinlinestyles)
# 4. 没有Open Graph标签(没有社交/现代营销)
if not soup.find(
以下为平台配置的接入选项,并非逐项实测通过。能否安装取决于客户端支持、技能来源和运行环境:
帮我安装 SkillHub 和 website-auditor-1776279326 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 website-auditor-1776279326 技能
skillhub install website-auditor-1776279326
文件大小: 4.47 KB | 发布时间: 2026-4-16 17:01