技能标识:contact-enrichment
Extract contact information from a website using a 3-tier approach. Direct HTML scraping, WHOIS lookup, then Hunter.io API domain search for verified business emails.
三层联系方式提取。优先运行最便宜的方法,仅在必要时升级。
第一层:直接HTML抓取(免费,始终优先尝试)
→ 检查:首页、/contact、/about、/about-us、/contact-us
→ 提取:所有邮箱、所有电话号码、企业名称
↓ (如果未找到邮箱)
第二层:WHOIS查询(免费)
→ 提取:注册组织、注册邮箱
↓ (如果仍未找到邮箱)
第三层:Hunter.io API(付费,每月25次免费)
→ 域名搜索 → 已验证邮箱 + 名/姓 + 职位
python
{
business_name: Green Valley Landscaping,
emails: [info@greenvalley.com, john@greenvalley.com],
primary_email: info@greenvalley.com,
phones: [(503) 555-0123],
primary_phone: (503) 555-0123,
owner_name: John Smith, # 来自Hunter.io或关于页面
whois_org: Green Valley LLC,
whois_email: registrant@email.com,
source: scrape, # scrape | whois | hunter
confidence: high # high | medium | low
}
python
import re, requests
from bs4 import BeautifulSoup
HEADERS = {
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
}
EMAIL_REGEX = re.compile(
r\b[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}\b
)
PHONE_REGEX = re.compile(
r(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
)
def isvalidemail(email: str) -> bool:
domain = email.split(@)[-1].lower()
return domain not in EMAIL_BLACKLIST and len(email) < 100
def scrape_page(url: str) -> tuple[str, str]:
获取页面。返回(html, final_url)。
try:
r = requests.get(url, headers=HEADERS, timeout=8, allow_redirects=True)
if r.status_code < 400:
return r.text, r.url
except Exception:
pass
return , url
def extractfromhtml(html: str) -> dict:
soup = BeautifulSoup(html, lxml)
text = soup.get_text(separator= )
# 提取邮箱
emails = list({e for e in EMAILREGEX.findall(text) if isvalid_email(e)})
# 提取电话
phonesraw = PHONEREGEX.findall(text)
phones = list({p[0] + p[1] if isinstance(p, tuple) else p for p in phones_raw})
# 清理:移除过短的匹配项
phones = [p.strip() for p in phones if len(re.sub(r\D, , p)) >= 10]
# 从标题提取企业名称
title = soup.find(title)
business_name =
if title:
t = title.text.strip()
# 取第一个有意义的片段
for sep in [|, –, -, ·, —, :]:
if sep in t:
t = t.split(sep)[0].strip()
break
business_name = t
return {emails: emails, phones: phones, businessname: businessname}
def scrapecontactpages(baseurl: str, existinghtml: str = ) -> dict:
抓取多个页面以获取联系信息。
existing_html:传入已由website-auditor获取的HTML,避免重复获取。
from urllib.parse import urljoin
all_emails = set()
all_phones = set()
business_name =
# 先尝试首页(如果已有HTML则使用)
if existing_html:
data = extractfromhtml(existing_html)
all_emails.update(data[emails])
all_phones.update(data[phones])
businessname = data[businessname]
# 尝试联系/关于页面
contact_paths = [/contact, /about, /contact-us, /about-us,
/get-in-touch, /reach-us, /find-us]
for path in contact_paths:
if all_emails:
break # 已找到邮箱——无需继续爬取
url = urljoin(base_url, path)
html, = scrapepage(url)
if html:
data = extractfromhtml(html)
all_emails.update(data[emails])
all_phones.update(data[phones])
if not business_name:
businessname = data[businessname]
emails = list(all_emails)
phones = list(all_phones)
return {
emails: emails,
primary_email: emails[0] if emails else None,
phones: phones,
primary_phone: phones[0] if phones else None,
businessname: businessname,
source: scrape,
confidence: high if emails else low
}
python
import whois # pip install python-whois
def whois_lookup(domain: str) -> dict:
查询WHOIS注册数据。
如果公开可用,返回注册组织+邮箱。
try:
w = whois.whois(domain)
# 提取邮箱(部分注册商隐藏了这些信息)
emails = w.emails if isinstance(w.emails, list) else ([w.emails] if w.emails else [])
emails = [e for e in emails if e and isvalidemail(e) and whoisprotect not in e.lower()
and privacy not in e.lower() and proxy not in e.lower()]
org = w.get(org) or w.get(registrant_organization) or
creation = w.get(creation_date)
if isinstance(creation, list):
creation = creation[0]
return {
whois_org: org,
whois_email: emails[0] if emails else None,
whois_emails: emails,
domain_created: str(creation)[:10] if creation else None,
registrar: w.get(registrar)
}
except Exception as e:
return {whoisorg: None, whoisemail: None, whois_error: str(e)}
python
import requests, os
def hunterdomainsearch(domain: str, limit: int = 5) -> dict:
在Hunter.io上搜索与域名关联的邮箱。
返回已验证的企业邮箱+联系人姓名。
需要设置HUNTERAPIKEY环境变量。
免费:每月25次搜索 | 入门版:每月34美元500次
apikey = os.environ.get(HUNTERAPI_KEY)
if not api_key:
return {hunteremails: [], huntersource: no_key}
params = {
domain: domain,
apikey: apikey,
limit: limit,
type: personal # personal | generic
}
try:
r = requests.get(https://api.hunter.io/v2/domain-search, params=params, timeout=10)
data = r.json().get(data, {})
contacts = []
for email_obj in data.get(emails, []):
contacts.append({
email: email_obj.get(value),
firstname: emailobj.get(first_name),
lastname: emailobj.get(last_name),
position: email_obj.get(position),
confidence: email_obj.get(confidence),
linkedin: email_obj.get(linkedin)
})
primary = contacts[0] if contacts else {}
return {
hunter_emails: [c[email] for c in contacts if c[email]],
primary_email: primary.get(email),
ownername: f{primary.get(firstname, )} {primary.get(last_name,
以下为平台配置的接入选项,并非逐项实测通过。能否安装取决于客户端支持、技能来源和运行环境:
帮我安装 SkillHub 和 contact-enrichment-1776279301 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 contact-enrichment-1776279301 技能
skillhub install contact-enrichment-1776279301
文件大小: 4.13 KB | 发布时间: 2026-4-16 16:11