如何使用 Python 从不同公司官网提取领导团队姓名和职务?
可以用 Python 完成。需要先确认公司官网并找到相关页面,再提取姓名和职务,最后验证结果。官网布局和栏目名称各不相同,单一 CSS selector 难以可靠覆盖所有网站。
采集前还要明确范围:Leadership、Executive Team 和 Board Members 可能分别对应管理团队、高管团队和董事会。董事不一定参与日常管理,不能把这些页面上的人员全部归为同一类。
1. 先确认官网,再寻找团队页面
如果运行时只有公司名称,需要先通过搜索服务或可信的公司资料找到候选官网,再核对公司全称、地区和业务等信息。同名公司可能对应不同域名,不建议直接把搜索结果的第一项当成官网。
确认域名后,可以从首页导航、页脚、About 页面以及网站提供的 sitemap 中寻找相关链接。匹配词可以包括:
Leadership、Management、Executive Team。Board、Directors、Governance。- “领导团队”“管理团队”“董事会”。
这些词只能用来发现候选页面,还要结合页面标题和正文判断它介绍的是哪类人员。
2. 优先读取结构化数据,再处理页面布局
如果目标页面的 JSON-LD 中包含 Person、name 和 jobTitle,可以先提取这些字段。没有这类数据时,再分析人员卡片、列表或表格,将同一区域中的姓名与职务配对。
不要分别收集整个页面的所有姓名和职务,再按顺序拼接。隐藏元素、重复导航或缺失字段都可能导致错配。
下面的示例会发现当前页面中的相关链接,并提取 JSON-LD 中带有姓名和职务的 Person 候选记录。它只处理服务器返回的 HTML,不执行 JavaScript。
安装依赖:
pip install requests beautifulsoup4
import json
from urllib.parse import urljoin, urlsplit
import requests
from bs4 import BeautifulSoup
TERMS = (
"leadership", "management", "executive", "board",
"directors", "governance", "team", "about",
"领导", "管理团队", "董事", "关于",
)
def walk_json(value):
if isinstance(value, dict):
yield value
for child in value.values():
yield from walk_json(child)
elif isinstance(value, list):
for child in value:
yield from walk_json(child)
def inspect_page(url):
response = requests.get(url, timeout=20)
response.raise_for_status()
page_url = response.url
soup = BeautifulSoup(response.content, "html.parser")
candidate_pages = set()
for link in soup.select("a[href]"):
target = urljoin(page_url, link["href"])
parsed = urlsplit(target)
label = f"{link.get_text(' ', strip=True)} {target}".casefold()
if (
parsed.scheme in {"http", "https"}
and parsed.hostname == urlsplit(page_url).hostname
and any(term in label for term in TERMS)
):
candidate_pages.add(parsed._replace(fragment="").geturl())
people = {}
for script in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(script.get_text())
except (ValueError, TypeError):
continue
for item in walk_json(data):
types = item.get("@type", [])
if isinstance(types, str):
types = [types]
if not isinstance(types, list) or "Person" not in types:
continue
name = item.get("name")
title = item.get("jobTitle")
if isinstance(name, str) and isinstance(title, str):
name, title = name.strip(), title.strip()
if name and title:
people[(name, title)] = {
"name": name,
"designation": title,
"source_url": page_url,
}
return {
"candidate_pages": sorted(candidate_pages),
"person_candidates": list(people.values()),
}
if __name__ == "__main__":
# 替换为已经确认的公司官网地址。
result = inspect_page("https://company.example/")
print(json.dumps(result, ensure_ascii=False, indent=2))
先检查返回的 candidate_pages,确认相关页面后,再将其地址传给 inspect_page()。示例只接受同一主机名下的链接;如果官网把投资者关系或董事会信息放在独立子域名,需要核实后将该主机加入允许范围。
person_candidates 中的记录也需要核对。JSON-LD 中的 Person 可能是文章作者或其他人员,必须结合页面内容判断其是否属于目标团队。没有返回记录,只表示这段代码没有找到符合条件的数据,不能据此认定公司没有领导团队信息。
3. 没有 JSON-LD 时,为页面结构设置提取规则
如果页面使用重复的人员卡片,可以先定位每张卡片,再在卡片内部寻找姓名、职务和个人简介链接。例如:
# 选择器仅演示提取方式,需要根据目标页面的实际 HTML 修改。
for card in soup.select(".leader-card"):
name_node = card.select_one(".name")
title_node = card.select_one(".job-title")
if name_node is None or title_node is None:
continue
person = {
"name": name_node.get_text(" ", strip=True),
"designation": title_node.get_text(" ", strip=True),
}
如果团队页面只有姓名和简介链接,可能需要进入个人页面才能找到职务。跨公司采集时,可以复用页面发现和字段验证逻辑,再为无法通用处理的网站维护单独规则。
对于任意未知官网,程序应允许返回“未找到”“需要渲染”或“需要复核”,避免为了填满字段而猜测姓名和职务。
4. 页面依赖 JavaScript 时,再考虑浏览器渲染
如果浏览器中能看到人员信息,而 HTTP 响应中没有,页面可能通过 JavaScript 加载数据。此时可以检查页面实际使用的数据请求,或使用 Playwright 获取渲染后的页面内容,再执行提取。
浏览器渲染会增加耗时和资源消耗,适合在普通 HTTP 请求无法获得内容时启用。抓取失败、访问受限和页面没有相关信息,也应分别记录状态,方便后续判断。
5. 验证结果时保留上下文
即使信息只在内存中用于验证,处理过程中也应保留来源页面和原始职务文本。至少核对以下内容:
- 页面属于已确认的公司官网,且描述的是目标公司或目标业务实体。
- 姓名和职务来自同一人员区域,具有明确对应关系。
- 页面介绍的是现任成员,而不是历史公告、新闻报道或离任人员。
- 董事会成员与管理团队成员按实际角色区分。
如果需要统一职位名称,可以另外生成标准化字段,同时保留原文。例如,CEO 与 Chief Executive Officer 可以按业务规则映射,但 Chair、Director 和 Managing Director 不宜仅凭关键词归为同一职位。
采集时应检查网站的访问规则,设置合理频率,并避免在日志中意外保存原本不打算留存的人员信息。即使采集仅用于验证,也需要检查网站的访问要求。
备注:内容仅供参考。
