南方科技大学院士分析

网页信息获取分析报告

202218093024 22计算广告李爱

1.Python获取页面信息

这里需要爬取的是南方科技大学研究生院-师资概况页面,使用的是requests和BeautifulSoup方法

以下是要爬取的页面
南方科技大学研究生院-师资概况

import requests
from bs4 import BeautifulSoup
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import re
# 1.Python获取页面内容

url = "https://www.sustech.edu.cn/zh/faculty/"

response = requests.get(url)
response.encoding = 'utf-8'  
html_content = response.text

soup = BeautifulSoup(html_content, 'html.parser')
# print(html_content)

由网页信息可以看出,里面存在很多规律的信息-院士和教授的介绍,也为我们后面提取内容提供了条件。

2.提取导航栏信息

导航栏信息如下图所示。
南方科技大学研究生院-导航栏

# 2. 提取导航栏信息(导航栏截图如前所示)
a_tags = soup.select('li > a')
# print("a_tags",a_tags)
texts = [a.text for a in a_tags]
print("导航栏信息:\n",texts)
导航栏信息:
 ['学校概况', '治理架构', '组织机构', '现任领导', '历任领导', '大学精神', '大事记', '统计数据', '学校标识', '南科视界', '联系我们', '院系概况', '理学院', '数学系', '物理系', '化学系', '地球与空间科学系', '统计与数据科学系', '工学院', '力学与航空航天工程系', '机械与能源工程系', '材料科学与工程系', '电子与电气工程系', '计算机科学与工程系', '海洋科学与工程系', '生物医学工程系', '环境科学与工程学院', '深港微电子学院', '系统设计与智能制造学院', '生命科学学院', ' 生物系', '基础免疫与微生物学系', '系统生物学系', '化学生物学系', '神经生物学系', '医学院', '南方科技大学伦敦国王学院医学院', '商学院', '人文社会科学学院', '人文科学中心', '社会科学中心暨社会科学高等研究院', '高等教育研究中心', '语言中心', '艺术中心', '未来教育研究中心', '创新创业学院', '创新创意设计学院', '师资概况', '国家项目人才', '省市项目人才', '院系师资', '全部教师列表', '教学概况', '本科教学', '研究生教学', '海外学习', '科研概况', '科研成果', '科研平台', '科研机构', '深圳国家应用数学中心', '深圳国际数学中心(杰曼诺夫数学中心)(筹)', '深圳格拉布斯研究院', '前沿与交叉科学研究院', '南方科技大学-北京大学植物与食品联合研究所', '先进光源科学中心', '深圳可持续发展研究院', '前沿生物技术研究院', '碳中和能源研究院', '纳米科学与应用研究院', '海洋高等研究院', '公共服务平台', '公共分析测试中心', '科学与工程计算中心', '冷冻电镜中心', '实验动物中心', '成果转化', '书院概况', '致仁书院', '树仁书院', '致诚书院', '树德书院', '致新书院', '树礼书院', '交流合作概况', '国内交流合作', '国际交流合作', '联合国教科文组织高等教育创新中心(中国深圳)', '港澳台交流合作', '招生概况', '本科招生', '留学生招生', '研究生招生', '就业', '新时代学习中心', '工会', '团委', '师资概况', '国家项目人才', '省市项目人才', '院系师资', '全部教师列表', '院士', '会士', '百千万人才工程国家级人选', '国家自然科学基金杰出青年基金', '国家自然科学基金优秀青年基金', '享受国务院特殊津贴人员', '继续了解>>', '本科招生', '人才招聘', '科研平台', '行政服务大厅', '人才招聘', '招标采购', '就业信息', '信息公开', '预决算公开', '校长信箱', '联系我们']

3.提取页面信息并分析

我们选择的是当前页面所有院士的信息,其中包括他的个人页面的url,我们将它作进一步爬虫,得到该院士的个人简介,然后合并信息后储存为csv格式,dataframe,便于进一步分析。

# 提取页面信息并分析-当前页面所有院士的信息
div_tage = soup.find_all('div',class_="clearfix fl list2")

# 创建一个空的列表来存储所有的院士信息
professors_info = []
profile_root = "https://www.sustech.edu.cn"

for li in div_tage:
    name = li.find('div', class_='name').text.strip() if li.find('div', class_='name') else None
    title = li.find('div', class_='p').text.strip() if li.find('div', class_='p') else None
    honor = li.find('div', class_='p2').text.strip() if li.find('div', class_='p2') else None
    position = li.find('div', class_='p1').text.strip() if li.find('div', class_='p1') else None
    department = li.find('div', class_='dep').text.strip() if li.find('div', class_='dep') else None
    profile_link = profile_root + li.find('a')['href'] if li.find('a') else None

    if profile_link is not None:
        response = requests.get(profile_link)
        response.encoding = 'utf-8'  
        html_content = response.text
        soup = BeautifulSoup(html_content, 'html.parser')

        # email
        uncleaned_email = soup.find_all("span",class_="font fl")
        email = uncleaned_email[-1].text.strip()
        # Bio
        uncleanedBio = soup.find_all("div",class_="message-right fr")
        Bio =  uncleanedBio[0].text.strip()
    
    # 将每个人的信息作为字典存储
    professors_info.append({
        '姓名': name,
        '职位': title,
        '头衔': honor,
        '职务': position,
        '系别': department,
        "简介":Bio,
        '邮箱':email,
        '链接': profile_link,
    })

df = pd.DataFrame(professors_info)

# 保存一下
# df.to_csv('南方科技大学教授信息.csv', index=False)

4.进行院士信息分析

我们将得到的dataframe进行数据预处理后,将“职位”“头衔”“国家”这三列进行可视化分析,由于csv中没有储存教授的所属国别信息,我们粗略的将教授的名字分为中文和外文,中文对应中国,外文对应外国。在实验过程中发现,教授的职位和头衔有意义相同但是写法不同的类别,所以先对一些特定列进行统一。合并头衔中为了控制类别数量,所以将一些荣誉进行了合并(具体合并方法看下方代码),得到结果如下。

# 数据预处理
# 重复意义但不同的词合并成一类
df['职位'] = df['职位'].replace({
    '长期访问杰出教授': '长期杰出访问教授',  # 合并为 '长期杰出访问教授'
    '讲席教授、医学院副院长': '讲席教授'   # 合并为 '讲席教授'
})

# 合并相似的头衔类别
df['头衔'] = df['头衔'].replace({
    # 中国科学院/工程院相关
    '中国科学院院士': '中国科学院/工程院院士',
    '中国工程院院士': '中国科学院/工程院院士',
    '中国科学院外籍院士': '中国科学院/工程院院士',
    '中国科学院院士、美国物理学会会士': '中国科学院/工程院院士',
    '中国工程院外籍院士、加拿大皇家科学院院士、加拿大工程院院士': '中国科学院/工程院院士',

    # 外国科学院/工程院院士及会士
    '欧洲科学院院士': '外国科学院/工程院院士及会士',
    '澳大利亚工程院院士': '外国科学院/工程院院士及会士',
    '德国国家科学与工程院院士': '外国科学院/工程院院士及会士',
    '英国皇家工程院院士、欧洲科学院院士(Academia Europaea)': '外国科学院/工程院院士及会士',
    '欧洲科学院(Academia Europaea)院士,美国物理学会(APS)会士': '外国科学院/工程院院士及会士',
    '俄罗斯工程院外籍院士': '外国科学院/工程院院士及会士',
    '加拿大工程院院士': '外国科学院/工程院院士及会士',
    '日本工程院院士,印度国家科学院院士,欧洲科学院院士': '外国科学院/工程院院士及会士',
    '俄罗斯工程院外籍院士、OSA/SPIE Fellow': '外国科学院/工程院院士及会士',
    '日本工程院外籍院士、IEEE Fellow': '外国科学院/工程院院士及会士',
    '澳大利亚国家工程院外籍院士': '外国科学院/工程院院士及会士',

    # 国际Fellow及相关
    '加拿大工程院院士、IEEE Fellow': '国际Fellow及相关',
    '美国科学院院士': '国际Fellow及相关',
    '乌克兰国家工程院外籍院士、美国骨与矿物质研究学会(ASBMR)会士、国际华人骨科研究学会(ICMRS)候任主席': '国际Fellow及相关',
    '美国国家工程院院士': '国际Fellow及相关',
    '世界艺术与科学学院院士': '国际Fellow及相关',

    # 多重院士及学术头衔
    '图灵奖得主、法国科学院院士、法国国家工程院院士、欧洲科学院院士、美国艺术与科学学院院士、美国国家工程院院士': '多重院士及学术头衔',

    # 特殊荣誉及奖项获得者
    '欧洲科学院院士、国家杰出青年科学基金获得者、国务院特殊津贴专家': '特殊荣誉及奖项获得者',

    # 无头衔
    '无': '无'
})

# 缺失值处理
df = df.apply(lambda x: x.str.strip() if x.dtype == "object" else x)
df.replace('', pd.NA, inplace=True)
df.replace(' ', pd.NA, inplace=True)
df = df.fillna("无")

# 设置字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False  

# 通过看教授的名字是中文还是外语来看教授所属国家
def identify_name_language(name):
    if re.search('[\u4e00-\u9fff]', name):
        return 'Chinese'
    else:
        return 'Foreign'

# 添加新列 'Country'
df['Country'] = df['姓名'].apply(identify_name_language)

# 排序将“无”放在最后
country_order = df['Country'].value_counts().index.tolist()
if '无' in country_order:
    country_order.remove('无')
    country_order.append('无')

title_order = df['职位'].value_counts().index.tolist()
if '无' in title_order:
    title_order.remove('无')
    title_order.append('无')

honor_order = df['头衔'].value_counts().index.tolist()
if '无' in honor_order:
    honor_order.remove('无')
    honor_order.append('无')

# 创建 2x3 的子图(两行三列)
fig, axes = plt.subplots(2, 3, figsize=(18, 12))

# 1. 教授的国家分布
sns.countplot(data=df, x='Country', order=country_order, palette='Set2', ax=axes[0, 0])
axes[0, 0].set_title('教授的国家分布')
axes[0, 0].set_xlabel('国家')
axes[0, 0].set_ylabel('计数')
for p in axes[0, 0].patches:
    axes[0, 0].annotate(f'{int(p.get_height())}', 
                        (p.get_x() + p.get_width() / 2., p.get_height()), 
                        ha='center', va='baseline')

axes[0, 0].set_xticks(range(len(country_order)))
axes[0, 0].set_xticklabels(country_order, rotation=45)

# 2. 教授的职位分布
sns.countplot(data=df, x='职位', order=title_order, palette='Set3', ax=axes[0, 1])
axes[0, 1].set_title('教授的职位分布')
axes[0, 1].set_xlabel('职位')
axes[0, 1].set_ylabel('计数')
axes[0, 1].tick_params(axis='x', rotation=45)
for p in axes[0, 1].patches:
    axes[0, 1].annotate(f'{int(p.get_height())}', 
                        (p.get_x() + p.get_width() / 2., p.get_height()), 
                        ha='center', va='baseline')

axes[0, 1].set_xticks(range(len(title_order)))
axes[0, 1].set_xticklabels(title_order, rotation=45)

# 3. 教授的头衔分布
sns.countplot(data=df, x='头衔', order=honor_order, palette='Set1', ax=axes[0, 2])
axes[0, 2].set_title('教授的头衔分布')
axes[0, 2].set_xlabel('头衔')
axes[0, 2].set_ylabel('计数')

# 设置x坐标标签
axes[0, 2].set_xticks(range(len(honor_order)))
axes[0, 2].set_xticklabels(honor_order, rotation=45, ha='right')

# 标签间距调整
plt.setp(axes[0, 2].get_xticklabels(), rotation=45, ha='right')

# 移除未使用的子图(如果有)
for ax in axes.flat[3:]:
    fig.delaxes(ax)

# 调整布局
plt.tight_layout()
plt.show()

png

根据初步的EDA分析,可以得出以下结论:

南方科技大学的院士团队以中国籍院士为主,但相较于其他学校,外国教授的比例也相对较大,显示出学校在国际化发展方面的突出特点。从教授的职位分布来看,讲席教授占据了最大的比例,其次是长期杰出访问教授和兼职教授,体现了学校在吸引高端人才方面的多样化策略。在教授的头衔分布上,大部分教授是中国科学院或中国工程院院士,约有四分之一是外国科学院或工程院院士及会士,这反映了学校在吸引具有国际声誉的学者方面的显著成就。此外,还包括了国际Fellow及相关荣誉获得者、特殊荣誉及学术头衔获得者,这进一步展示了南方科技大学在全球范围内吸引优秀学者的实力。

南方科技大学研究生院成功汇聚了众多中外顶尖学者,一些院士不仅在国内外的学术界享有很高声望,还获得了诸多荣誉,是一所很好的学校。

需要注意的是,这只是初步的数据挖掘,且没有包括南方科技大学所有的院士,具有一定的局限性,未来,可以考虑将南方科技大学数据科学系和计算机科学与技术系的所有导师做一个综合分析,把他们的简介做一个聚类来看该学校该专业的导师的主要研究方法,再结合研究生导师评价网做一个综合的导师推荐引擎,可以为以后升学提供参考,未来会考虑进一步完善这个作业。

5.使用SQLAIchemy存储到SQLite3中

%pip install sqlalchemy
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: sqlalchemy in ./opt/anaconda3/lib/python3.9/site-packages (1.4.39)
Requirement already satisfied: greenlet!=0.4.17 in ./opt/anaconda3/lib/python3.9/site-packages (from sqlalchemy) (1.1.1)

[notice] A new release of pip is available: 24.1.2 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import SQLAlchemyError

# 定义数据模型
Base = declarative_base()

class Professor(Base):
    __tablename__ = 'professors'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50))
    title = Column(String(100))
    honor = Column(String(100))
    position = Column(String(100))
    department = Column(String(100))
    bio = Column(String(100))
    email = Column(String(100))
    profile_link = Column(String(100))

# 创建数据库连接
engine = create_engine('sqlite:///test0916.db')
Base.metadata.create_all(engine)

# 创建 session
Session = sessionmaker(bind=engine)
session = Session()


# 因为之前是dataframe格式,所以直接插入到数据库
try:
    for _, row in df.iterrows():
        professor = Professor(
            name=row['姓名'],
            title=row['职位'],
            honor=row['头衔'],
            position=row['职务'],
            department=row['系别'],
            bio=row['简介'],
            email=row['邮箱'],
            profile_link=row['链接']
        )
        session.add(professor)
    session.commit()
    # #增
    # new_professtor = Professor(name='xx',title='xx')
    # session.add(new_professtor)
    # #删
    # session.query(Professor).filter(Professor.title == '中国工程院院士').delete()
    # #改
    # session.query(Professor).filter(Professor.honor == 'xxx').update({Professor.honor: "update_xxx"})

    # session.commit()
    # #查
    # people = session.query(Professor).all()
    print("DataFrame has been stored in the SQLite database.")
except SQLAlchemyError as e:
    print(f"An error occurred: {e}")
    session.rollback()

finally:
    session.close()

DataFrame has been stored in the SQLite database.

posted @ 2024-09-08 13:53  cucliai  阅读(27)  评论(0)    收藏  举报