玳瑁的嵌入式日记 --------API总结

一、文件 I/O 相关 API

  1. open

    • 函数原型:int open(const char *pathname, int flags, mode_t mode);
    • 核心功能:打开或创建文件,返回文件描述符(fd,后续 I/O 操作的唯一标识)。若操作失败返回 - 1。
    • 参数说明:
      • pathname:文件路径(绝对或相对路径);
      • flags:打开方式(如 O_RDONLY 只读、O_WRONLY 只写、O_RDWR 读写、O_CREAT 创建文件等);
      • mode:当flags包含 O_CREAT 时,指定新文件的权限(如 0644 表示所有者读写、组和其他只读)。
    • 典型场景:读取配置文件、创建日志文件、打开设备文件(如 /dev/tty)。
  2. close

    • 函数原型:int close(int fd);
    • 核心功能:关闭文件描述符,释放内核资源(避免资源泄漏)。成功返回 0,失败返回 - 1。
    • 参数说明:fdopen返回的文件描述符。
    • 典型场景:文件读写完成后、程序退出前释放所有打开的 fd。
  3. read

    • 函数原型:ssize_t read(int fd, void *buf, size_t count);
    • 核心功能:从文件描述符fd读取数据到缓冲区buf,返回实际读取的字节数(0 表示读到文件末尾,-1 表示失败)。
    • 参数说明:
      • buf:接收数据的缓冲区(需提前分配内存);
      • count:期望读取的最大字节数。
    • 典型场景:读取本地文本文件内容、从串口设备读取传感器数据。
  4. write

    • 函数原型:ssize_t write(int fd, const void *buf, size_t count);
    • 核心功能:将缓冲区buf中的数据写入文件描述符fd,返回实际写入的字节数(-1 表示失败)。
    • 参数说明:buf为待写入数据的缓冲区,count为写入的字节数。
    • 典型场景:向日志文件写入内容、向网络套接字发送数据、向打印机设备发送指令。
  5. lseek

    • 函数原型:off_t lseek(int fd, off_t offset, int whence);
    • 核心功能:调整文件读写指针的位置,支持随机读写,返回新的指针位置(-1 表示失败)。
    • 参数说明:
      • offset:偏移量(正数向前移,负数向后移);
      • whence:基准位置(SEEK_SET 从文件开头、SEEK_CUR 从当前位置、SEEK_END 从文件末尾)。
    • 典型场景:计算文件大小(lseek(fd, 0, SEEK_END))、修改文件中间内容。
  6. dup/dup2

    • 函数原型:int dup(int oldfd); / int dup2(int oldfd, int newfd);
    • 核心功能:复制文件描述符,实现 “重定向”(如将标准输出 stdout 重定向到文件)。
      • dup:返回一个新的未使用的最小 fd,与oldfd指向同一文件;
      • dup2:强制将newfd指向oldfd的文件(若newfd已打开则先关闭)。
    • 典型场景:将程序日志输出到文件(dup2(log_fd, STDOUT_FILENO))、多进程共享同一网络连接。
  7. fcntl

    • 函数原型:int fcntl(int fd, int cmd, ... /* arg */);
    • 核心功能:控制文件描述符的属性(功能多样,通过cmd指定操作)。
    • 常用cmd
      • F_GETFL:获取文件状态标志(如 O_NONBLOCK 非阻塞模式);
      • F_SETFL:设置文件状态标志(如fcntl(fd, F_SETFL, O_NONBLOCK)开启非阻塞 I/O);
      • F_DUPFD:复制 fd(类似dup)。
    • 典型场景:将网络套接字设为非阻塞模式、获取文件打开方式。

二、进程管理相关 API

  1. fork

    • 函数原型:pid_t fork(void);
    • 核心功能:创建子进程(复制父进程的地址空间、文件描述符等资源)。
    • 返回值:父进程返回子进程的 PID(正数),子进程返回 0,失败返回 - 1。
    • 典型场景:服务器通过多进程并发处理多个客户端请求、后台程序创建子进程执行异步任务。
  2. exec 系列

    • 函数原型(以execl为例):int execl(const char *path, const char *arg, ... /* (char *) NULL */);
    • 核心功能:替换当前进程的代码段和数据段(加载新程序),原进程的 PID 不变。
    • 常见变种:execlp(自动从 PATH 查找程序)、execv(参数用数组传递)、execvp(结合execvexeclp)。
    • 典型场景:在程序中执行系统命令(如execlp("ls", "ls", "-l", NULL))、启动外部应用程序。
  3. wait

    • 函数原型:pid_t wait(int *wstatus);
    • 核心功能:父进程阻塞等待任意子进程退出,回收子进程资源(避免僵尸进程)。
    • 参数说明:wstatus用于存储子进程的退出状态(如正常退出码、信号终止原因),可传 NULL 忽略。
    • 典型场景:父进程等待子进程完成计算任务后再继续执行。
  4. waitpid

    • 函数原型:pid_t waitpid(pid_t pid, int *wstatus, int options);
    • 核心功能:更灵活的子进程等待(相比wait)。
    • 参数说明:
      • pid:指定等待的子进程 PID(-1 表示任意子进程);
      • options:WNOHANG(非阻塞等待,若子进程未退出则立即返回 0)、WUNTRACED(跟踪停止的子进程)等。
    • 典型场景:父进程批量管理多个子进程(如循环非阻塞检查子进程状态)。
  5. exit

    • 函数原型:void exit(int status);
    • 核心功能:终止当前进程,释放资源(关闭所有打开的 fd、清理临时文件),并向父进程传递退出状态码status(0 表示正常退出,非 0 表示异常)。
    • 典型场景:程序执行完成后退出、发生错误时主动终止(如exit(1)表示异常)。
  6. getpid/getppid

    • 函数原型:pid_t getpid(void); / pid_t getppid(void);
    • 核心功能:getpid返回当前进程的 PID,getppid返回父进程的 PID。
    • 典型场景:日志中标记当前进程 ID、进程间通过 PID 识别身份。

三、I/O 多路复用 API(含 select)

  1. select

    • 函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    • 核心功能:监听多个文件描述符的事件(读、写、异常),实现单进程 / 线程同时处理多个 I/O 操作。
    • 参数说明:
      • nfds:监听的最大 fd 值 + 1;
      • readfds:待监听 “读事件” 的 fd 集合(如 socket 有数据可读);
      • writefds:待监听 “写事件” 的 fd 集合(如 socket 可写入数据);
      • exceptfds:待监听 “异常事件” 的 fd 集合;
      • timeout:超时时间(NULL 表示永久阻塞,struct timeval{0,0}表示非阻塞)。
    • 返回值:就绪的 fd 数量(0 表示超时,-1 表示失败)。
    • 典型场景:简易 TCP 服务器同时处理多个客户端连接(避免为每个连接创建进程 / 线程)。
  2. FD_ZERO/FD_SET/FD_ISSET

    • 函数原型:
      • void FD_ZERO(fd_set *set);
      • void FD_SET(int fd, fd_set *set);
      • int FD_ISSET(int fd, fd_set *set);
    • 核心功能:配合select管理 fd 集合:
      • FD_ZERO:初始化集合(清空所有 fd);
      • FD_SET:将 fd 添加到集合中;
      • FD_ISSET:检查 fd 是否在就绪集合中(select返回后调用)。
    • 典型场景:select调用前初始化监听集合,调用后判断哪些 fd 就绪。
  3. poll

    • 函数原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    • 核心功能:类似select,但用struct pollfd结构体管理监听的 fd 和事件,避免select的 fd 数量限制(默认 1024)。
    • struct pollfd定义:

      c

      运行

      struct pollfd {
          int fd;         // 待监听的文件描述符
          short events;   // 期望监听的事件(如POLLIN表示读事件)
          short revents;  // 实际发生的事件(由内核填充)
      };
      
    • 参数说明:timeout为超时毫秒数(-1 表示永久阻塞,0 表示非阻塞)。
    • 典型场景:替代select处理超过 1024 个 fd 的场景(如高并发临时解决方案)。
  4. epoll 系列(Linux 特有)

    • epoll_createint epoll_create(int size);功能:创建 epoll 实例(返回 epoll 文件描述符 epfd),size为历史参数(现已忽略,传任意正数即可)。
    • epoll_ctlint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);功能:管理 epoll 监听的 fd(添加、修改、删除)。
      • op:EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除);
      • event:指定监听的事件(如 EPOLLIN 读事件)和触发模式(EPOLLLT 水平触发、EPOLLET 边缘触发)。
    • epoll_waitint epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);功能:等待 epoll 实例中的事件就绪,返回就绪的 fd 数量。
    • 典型场景:高并发服务器(如 Nginx、Redis),支持高效处理数万甚至数十万 fd。

四、信号处理相关 API

  1. signal

    • 函数原型:void (*signal(int signum, void (*handler)(int)))(int);
    • 核心功能:为指定信号signum注册处理函数handler(简易接口)。
    • 参数说明:
      • signum:信号编号(如 SIGINT 表示 Ctrl+C,SIGTERM 表示终止信号);
      • handler:处理函数(或 SIG_IGN 忽略信号、SIG_DFL 使用默认处理)。
    • 典型场景:捕获 SIGINT 信号,在程序退出前执行清理操作(如保存数据)。
  2. sigaction

    • 函数原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    • 核心功能:更灵活的信号处理(替代signal),支持信号掩码、保留信号上下文。
    • struct sigaction定义:

      c

      运行

      struct sigaction {
          void (*sa_handler)(int);  // 处理函数(同signal)
          sigset_t sa_mask;         // 信号处理期间阻塞的信号集
          int sa_flags;             // 标志(如SA_RESTART自动重启被中断的系统调用)
      };
      
    • 典型场景:复杂信号处理(如忽略 SIGPIPE 信号避免网络程序崩溃)。
  3. kill

    • 函数原型:int kill(pid_t pid, int sig);
    • 核心功能:向指定 PID 的进程发送信号sig
    • 参数说明:pid为目标进程 PID(-1 表示向所有同组进程发送),sig为信号编号(如 SIGKILL 强制终止)。
    • 典型场景:脚本中终止后台进程(kill -9 12345)、进程间发送控制信号(如通知子进程退出)。
  4. sigprocmask

    • 函数原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    • 核心功能:控制进程的信号掩码(阻塞或解除阻塞指定信号)。
    • 参数说明:
      • how:SIG_BLOCK(添加阻塞信号)、SIG_UNBLOCK(解除阻塞)、SIG_SETMASK(替换掩码);
      • set:待操作的信号集(sigset_t类型,需用sigemptyset/sigaddset初始化)。
    • 典型场景:数据库写入等原子操作期间,阻塞 SIGINT 等信号避免操作被打断。

五、网络通信相关 API(TCP/UDP)

  1. socket

    • 函数原型:int socket(int domain, int type, int protocol);
    • 核心功能:创建网络套接字(用于网络通信的 fd),失败返回 - 1。
    • 参数说明:
      • domain:地址族(AF_INET=IPv4,AF_INET6=IPv6);
      • type:套接字类型(SOCK_STREAM=TCP,SOCK_DGRAM=UDP);
      • protocol:协议(通常为 0,由domaintype自动确定)。
    • 典型场景:初始化 TCP 客户端 / 服务器、UDP 通信端点。
  2. bind

    • 函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    • 核心功能:将套接字sockfd绑定到指定的 IP 地址和端口(TCP 服务器必须绑定,客户端可选)。
    • 参数说明:
      • addr:存放 IP 和端口的结构体(IPv4 用sockaddr_in,需转换为sockaddr*);
      • addrlen:结构体长度(sizeof(struct sockaddr_in))。
    • 典型场景:TCP 服务器绑定端口 8080(struct sockaddr_in.sin_port = htons(8080))。
  3. listen

    • 函数原型:int listen(int sockfd, int backlog);
    • 核心功能:将 TCP 套接字设为监听状态,准备接收客户端连接。
    • 参数说明:backlog为等待连接队列的最大长度(超过则新连接被拒绝)。
    • 典型场景:TCP 服务器启动后调用,进入 “等待连接” 状态。
  4. accept

    • 函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    • 核心功能:TCP 服务器阻塞等待客户端连接,返回 “新的连接套接字”(用于与该客户端通信)。
    • 参数说明:addraddrlen用于获取客户端的 IP 和端口(可传 NULL 忽略)。
    • 典型场景:TCP 服务器接收客户端连接,用新套接字单独处理通信。
  5. connect

    • 函数原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    • 核心功能:TCP 客户端向服务器发起连接请求(三次握手),失败返回 - 1。
    • 参数说明:addr为服务器的 IP 和端口结构体。
    • 典型场景:客户端连接远程服务器(如connect(sockfd, &server_addr, sizeof(server_addr)))。
  6. send/recv

    • 函数原型:
      • ssize_t send(int sockfd, const void *buf, size_t len, int flags);
      • ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    • 核心功能:TCP 套接字发送 / 接收数据(flags通常为 0,MSG_DONTWAIT 表示非阻塞)。
    • 典型场景:TCP 客户端与服务器之间收发文本、二进制数据。
  7. sendto/recvfrom

    • 函数原型:
      • ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
      • ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
    • 核心功能:UDP 套接字发送 / 接收数据(无需连接,每次通信需指定目标 IP 和端口)。
    • 典型场景:DNS 查询(UDP 协议)、实时音视频传输。
  8. close(网络场景)

    • 函数原型:int close(int sockfd);
    • 核心功能:关闭套接字(TCP 会触发四次挥手,释放连接资源)。
    • 典型场景:客户端 / 服务器断开连接后释放套接字。

六、其他常用工具类 API

  1. gettimeofday

    • 函数原型:int gettimeofday(struct timeval *tv, struct timezone *tz);
    • 核心功能:获取当前时间(精确到微秒),tv存储秒和微秒(struct timeval { time_t tv_sec; suseconds_t tv_usec; }),tz通常传 NULL。
    • 典型场景:生成日志时间戳、计算程序执行耗时(前后两次调用差值)。
  2. localtime

    • 函数原型:struct tm *localtime(const time_t *timep);
    • 核心功能:将时间戳(time_t类型,秒级)转换为本地时间结构体(年、月、日、时、分、秒等)。
    • 典型场景:格式化输出时间(如printf("%d-%02d-%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday))。
  3. malloc/free

    • 函数原型:void *malloc(size_t size); / void free(void *ptr);
    • 核心功能:malloc动态分配size字节的内存(返回指针,失败返回 NULL),free释放之前分配的内存(避免内存泄漏)。
    • 典型场景:动态创建数组、分配结构体实例(如struct User *user = malloc(sizeof(struct User)))。
  4. memset/memcpy

    • 函数原型:
      • void *memset(void *ptr, int value, size_t num);
      • void *memcpy(void *dest, const void *src, size_t n);
    • 核心功能:memsetptr指向的内存块前num字节设为value(常用于初始化);memcpysrcn字节数据复制到dest(需确保内存不重叠)。
    • 典型场景:初始化缓冲区(memset(buf, 0, sizeof(buf)))、复制字符串或结构体数据。
  5. getenv

    • 函数原型:char *getenv(const char *name);
    • 核心功能:获取环境变量的值(如 “PATH”“HOME”),失败返回 NULL。
    • 典型场景:读取系统配置(如getenv("HOME")获取用户主目录)、程序动态加载配置参数。
  6. perror/strerror

    • 函数原型:void perror(const char *s); / char *strerror(int errnum);
    • 核心功能:perror打印 s 前缀 + 当前errno对应的错误描述(如 “open: No such file or directory”);strerror将错误码errnum转换为描述字符串。
    • 典型场景:调试程序(如open失败后用perror("open failed")定位原因)。
import requests from bs4 import BeautifulSoup import re import jieba import matplotlib.pyplot as plt import networkx as nx import csv import matplotlib.colors as mcolors import os # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False def fetch_html(url): """获取指定URL的HTML内容""" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } response = requests.get(url, headers=headers) if response.status_code == 200: return response.content.decode('utf-8') else: raise Exception(f"Failed to fetch page, status code: {response.status_code}") def extract_plot(url): """从豆瓣剧情页面提取剧情内容""" html_content = fetch_html(url) soup = BeautifulSoup(html_content, 'html.parser') # 获取剧情简介 plot_text = "" plot_div = soup.find('div', id="link-report") if plot_div: plot_text = plot_div.get_text(strip=True).replace("\u3000", "").replace("\\n", "\n") # 如果剧情太短,尝试从评论中获取 if len(plot_text) < 500: reviews = soup.find_all('div', class_='review-short') for review in reviews[:5]: plot_text += review.get_text(strip=True).replace("\u3000", "") + "\n\n" return plot_text def extract_characters(url): """从豆瓣演职员页面提取角色列表""" html_content = fetch_html(url) soup = BeautifulSoup(html_content, 'html.parser') # 提取角色列表 characters = [] actor_list = soup.find_all('div', class_='list-wrapper') for actor_section in actor_list: for li in actor_section.find_all('li'): # 提取角色名 role_span = li.find('span', class_='role') if role_span: role_text = role_span.get_text(strip=True) # 清洗角色名称 role_text = re.sub(r'[饰演].*|[饰演].*', '', role_text) role_text = re.sub(r'[\[\(].*?[\]\)]', '', role_text) if len(role_text) > 1 and role_text not in characters: characters.append(role_text) return characters def preprocess_plot(plot_text): """预处理剧情文本""" # 替换剧集中提到的剧名,避免被识别为角色 plot_text = plot_text.replace("芈月", "主角").replace("芈月传", "本剧") return plot_text # 豆瓣电影《芈月传》的相关URL plot_url = "https://movie.douban.com/subject/25742289/" # 剧情简介页面 character_url = "https://movie.douban.com/subject/25742289/celebrities" # 演职员页面 # 1. 获取剧情内容 print("获取剧情内容...") plot_text = extract_plot(plot_url) if not plot_text: # 从备份文件中获取(如果网络请求失败) if os.path.exists("芈月传剧情备份.txt"): with open("芈月传剧情备份.txt", "r", encoding="utf-8") as f: plot_text = f.read() else: # 保存备份 with open("芈月传剧情备份.txt", "w", encoding="utf-8") as f: f.write(plot_text) # 预处理剧情文本 plot_text = preprocess_plot(plot_text) with open('芈月传剧情.txt', 'w', encoding='utf-8') as f: f.write(plot_text) # 2. 获取角色列表 print("获取角色列表...") characters = extract_characters(character_url) if not characters: # 使用预设角色列表(如果网络请求失败) characters = ['芈月', '芈姝', '嬴驷', '黄歇', '翟骊', '魏琰', '嬴稷', '芈槐', '芈茵', '芈戎', '孟嬴', '香儿', '葵姑', '威后', '莒姬', '葵姑', '穆监', '绿萝', '赢华', '樗里子', '芈姝母', '芈月母', '张仪', '苏秦', '庸芮', '魏冉', '芈戎'] # 保存角色列表 with open('芈月传角色名单.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(characters)) print(f"角色列表: {', '.join(characters)}") print(f"剧情文本长度: {len(plot_text)} 字符") # 3. 为分词添加角色名 for name in characters: jieba.add_word(name) # 对于长名字添加分词组合 if len(name) == 3: jieba.add_word(name[:2]) jieba.add_word(name[1:]) elif len(name) >= 4: jieba.add_word(name[:2]) jieba.add_word(name[2:]) jieba.add_word(name[1:3]) # 4. 人物出场次数统计 print("统计人物出场次数...") with open('芈月传剧情.txt', 'r', encoding='utf-8') as f: plot_summary = f.read() # 分割句子 sentences = re.split(r'[。!?;\n]', plot_summary) sentences = [s for s in sentences if len(s.strip()) > 5] # 统计各角色出场次数 name_counts = {name: 0 for name in characters} for sentence in sentences: words = jieba.cut(sentence) for word in words: if word in name_counts: name_counts[word] += 1 # 过滤没有出场或出场次数过低的角色 active_characters = [name for name, count in name_counts.items() if count > 0] print(f"有效角色数量: {len(active_characters)}") # 按出场次数排序 sorted_name_counts = sorted(name_counts.items(), key=lambda item: item[1], reverse=True) top_characters = [item[0] for item in sorted_name_counts[:min(15, len(sorted_name_counts))] if item[1] > 0] top_counts = [item[1] for item in sorted_name_counts[:min(15, len(sorted_name_counts))] if item[1] > 0] # 绘制出场次数统计图 plt.figure(figsize=(14, 8)) plt.bar(top_characters, top_counts, color='#1f77b4', alpha=0.8) plt.xlabel('角色名称', fontsize=12, fontweight='bold') plt.ylabel('出场次数', fontsize=12, fontweight='bold') plt.title('《芈月传》人物出场次数统计TOP15', fontsize=16, fontweight='bold') plt.xticks(rotation=25) plt.grid(axis='y', alpha=0.5) plt.tight_layout() plt.savefig('芈月传人物出场次数统计.png', dpi=150) plt.close() # 5. 构建人物关系网络 print("构建人物关系网络...") G = nx.Graph() # 添加节点(只添加活跃角色) for name in active_characters: G.add_node(name, size=name_counts[name]) # 预设关系增强(基于剧情分析) enhanced_relationships = { ('芈月', '芈姝'): -5, # 敌对关系 ('芈月', '黄歇'): 8, # 爱人 ('芈月', '翟骊'): 7, # 爱人 ('芈月', '嬴驷'): 6, # 君臣/夫妻 ('芈月', '嬴稷'): 9, # 母子 ('芈姝', '嬴驷'): 6, # 夫妻 ('芈姝', '魏琰'): -3, # 亦敌亦友 ('黄歇', '翟骊'): -4, # 情敌 ('嬴驷', '魏琰'): 4, # 君臣 ('芈月', '芈茵'): -5, # 敌对 ('芈月', '威后'): -6, # 敌对 ('芈月', '张仪'): 7, # 盟友 } # 统计句子中共现情况 sentence_cooccurrence = {} for sentence in sentences: # 找出当前句子中出现的角色 current_chars = set() words = jieba.cut(sentence) for word in words: if word in active_characters: current_chars.add(word) # 为当前句子中出现的人物建立关系 char_list = list(current_chars) for i in range(len(char_list)): for j in range(i+1, len(char_list)): char1, char2 = sorted([char_list[i], char_list[j]]) pair = (char1, char2) # 累加关系权重 sentence_cooccurrence[pair] = sentence_cooccurrence.get(pair, 0) + 1 # 将关系添加到图中 for pair, count in sentence_cooccurrence.items(): char1, char2 = pair weight = count # 检查是否在增强字典中 if pair in enhanced_relationships: sign = 1 if enhanced_relationships[pair] > 0 else -1 weight = max(2, count) * (abs(enhanced_relationships[pair]) / 5) weight *= sign # 检查反向关系对 elif (char2, char1) in enhanced_relationships: sign = 1 if enhanced_relationships[(char2, char1)] > 0 else -1 weight = max(2, count) * (abs(enhanced_relationships[(char2, char1)]) / 5) weight *= sign else: # 默认处理:少于2次共现关系不强 if count <= 1: continue # 添加或更新关系 if G.has_edge(char1, char2): current_weight = G[char1][char2].get('weight', 0) G[char1][char2]['weight'] = current_weight + weight else: G.add_edge(char1, char2, weight=weight) # 6. 导出CSV文件 with open('芈月传人物关系_节点.csv', 'w', newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) writer.writerow(['Id', 'Label', 'Size']) for node, data in G.nodes(data=True): writer.writerow([node, node, data.get('size', 1)]) with open('芈月传人物关系_边.csv', 'w', newline='', encoding='utf-8-sig') as f: writer = csv.writer(f) writer.writerow(['Source', 'Target', 'Weight', 'Type']) for u, v, data in G.edges(data=True): edge_type = 'Positive' if data['weight'] > 0 else 'Negative' writer.writerow([u, v, abs(data['weight']), edge_type]) # 7. 可视化人物关系图 if G.number_of_edges() > 0: plt.figure(figsize=(20, 16)) # 节点大小基于出场次数 node_sizes = [data.get('size', 1) * 100 for _, data in G.nodes(data=True)] # 边的宽度和颜色基于关系强度 edge_widths = [] edge_colors = [] for u, v, data in G.edges(data=True): weight = abs(data['weight']) if data['weight'] > 0: # 正向关系 - 红色 edge_colors.append('red') else: # 负向关系 - 蓝色 edge_colors.append('blue') edge_widths.append(min(weight, 5)) # 使用布局算法 pos = nx.spring_layout(G, k=0.3, iterations=50, seed=42) # 绘制图形 nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='#ffcc99', alpha=0.9) nx.draw_networkx_edges(G, pos, edge_color=edge_colors, width=edge_widths, alpha=0.7) nx.draw_networkx_labels(G, pos, font_size=12, font_family='SimHei') # 添加图例 plt.scatter([], [], c='#ffcc99', s=100, label='人物节点') plt.plot([], [], c='red', linewidth=3, label='正向关系') plt.plot([], [], c='blue', linewidth=3, label='负向关系') plt.legend(loc='best', fontsize=12) plt.title('《芈月传》人物关系网络', fontsize=20) plt.axis('off') plt.tight_layout() plt.savefig('芈月传人物关系图.png', dpi=150) plt.close() print("分析完成!已保存关系图和统计数据文件。") 以上述代码为模版,爬取https://www.tvmao.com/drama/KSExaik=/episode 电视猫中的数据,做人物关系图
06-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值