python爬取新浪财经网站上行业板块股票信息的代码

在这个多行业持续高速发展的时代,科技正在改变着我们的生活。 在世界科技领域中,中国正占据越来越重要的位置。当下,每个行业都提到了区块链、人工智能、大数据、5G等科技力量,强调了科技在行业咨询与数据分析领域的重要意义。

随着大数据时代的到来,人工智能等前沿的科技在算法上深刻改变了各个行业,并成为未来行业发展的制高点。随着人工智能发展而风靡于世的Python,有着简单易学、运行速度快、可移植、可拓展、可嵌入以及第三方库丰富等特点,因而在数学、大数据分析以及行业数据和财务数据分析中都有着得天独厚的优势。

Python的语法很容易实现那些金融算法和数据计算,每个数学语句都能转变成一行Python代码,每行代码都能允许超过十万次的计算量。Python 效率较为明显的领域之一是交互式的数据分析,对于大数据来说,它无疑是一个极为合适的选择。这一领域从IPython、Jupyter Notebook等有力工具和Pandas之类的程序库中获益良多。

用几行代码就足以完成行业分析中的典型复杂任务------数据收集、复杂和重复的数学计算以及结果的可视化,令人觉得不可思议。我们在熟练应用Python工具和库的时候,应将焦点放在自身的领域上,而不用关心复杂的技术细节。分析者们可以快速反应,几乎可以实时地提供宝贵的意见,确保自己比竞争对手先行一步。这种效率的提高很容易转换为可度量的财务效果。

"巧妇难为无米之炊",找不到数据,量化分析也就无从谈起。对于行业分析来说,获取数据是量化分析的第一步。Python的强大功能之一就是数据获取(爬虫技术) 。例如,我们在做一些行业分析的时候,需要找到自己的同业竞争者,并且是有公开信息可查的同业竞争者,那就需要从众多上市公司中进行筛选。本项目的行业数据就是通过爬取新浪财经网站上行业板块的股票信息得到的,如图所示。

接下来我们就写一个爬虫,这个爬虫程序通过以下步骤获取新浪财经的行业板块股票信息:

  1. 首先请求行业板块页面并解析出各行业的链接
  2. 然后遍历每个行业板块,获取该行业下的所有股票信息
  3. 对获取的股票数据进行结构化处理
  4. 将最终结果保存为 CSV 文件

程序包含了完善的错误处理和日志记录功能,同时通过随机延时避免被目标网站封禁 IP。在使用时,可能需要根据新浪财经网站的实际结构调整 HTML 解析部分的选择器。

接下来我们就从原理、环境安装、库的选择与使用、代码组织以及具体实现等步骤来实现:

一、运行环境及安装

(一)运行环境要求
  1. Python 版本:建议 Python 3.7 及以上版本(兼容主流 Python 3.x 版本,确保对现代库的支持),当前的执行环境是在Python 3.13上完成的。
  2. 操作系统 :兼容 Windows、macOS、Linux(代码中文件路径处理使用os.path,跨平台适配)。
(二)环境安装步骤
  1. 安装 Python

    Python 官网下载对应系统的 Python 安装包,安装时勾选 "Add Python to PATH"(添加到环境变量),完成后通过python --versionpy --version验证安装成功。

  2. 安装依赖库

    代码依赖多个第三方库,通过pip命令统一安装:

    复制代码
    pip install selenium webdriver-manager beautifulsoup4 pandas lxml  

    其中beautifulsoup4 安装时也可以用名称bs4。(若安装失败,可尝试使用pip3或管理员权限运行命令)

二、使用的库及作用

(一)核心功能库
  1. selenium

    • Selenium是一款自动化测试利器,可以自动化模拟人操作浏览器的行为,所以也可以用于网络爬虫。Selenium调用浏览器必须有一个WebDriver驱动文件,下载好后把驱动程序放到Python安装目录里即可。
    • 作用:模拟浏览器行为,处理动态加载页面(新浪财经的股票数据通过 JavaScript 动态渲染,requests无法获取,需用浏览器模拟)。
    • 关键功能:自动打开浏览器、访问 URL、等待页面加载、获取渲染后的页面源码。
  2. webdriver-manager

    • 作用:自动管理浏览器驱动(如 ChromeDriver),无需手动下载和配置驱动路径,解决驱动版本与浏览器不匹配的问题。
  3. beautifulsoup4(bs4)

    • 为了向用户友好地显示,请求的网页返回数据中包含了大量的HTML 标签、CSS语句、JavaScript语句等,我们要在返回的文档中提取出所要的数据不是件容易的事情。Beautiful Soup可以通过解析返回的文档,提供一些简单的函数,为用户提供需要抓取的数据。有了它,我们可以很方便地提取出HTML或XML标签中的内容。
    • 作用:解析 HTML 页面源码,提取表格、字段等结构化数据(将复杂的 HTML 转换为可遍历的对象,便于定位和提取数据)。
  4. lxml

    • 作用:作为 BeautifulSoup 的解析器,提高 HTML 解析效率和准确性(比 Python 内置的html.parser更快,支持复杂选择器)。
(二)数据处理与工具库
  1. pandas

    • 作用:处理结构化数据,将提取的股票信息转换为 DataFrame,最终保存为 CSV 文件(简化数据存储流程,支持中文编码)。
  2. os

    • 作用:处理文件路径,确保所有生成的文件(CSV、日志、HTML 源码)保存到脚本所在目录(跨平台适配路径格式)。
  3. re

    • 作用:通过正则表达式清洗数据(去除数字中的逗号、百分号等符号,转换为标准数值类型)。
  4. time

    • 作用:添加延时,确保页面数据完全渲染(动态页面加载需要时间,避免提前获取未完成的内容)。
  5. logging

    • 作用:记录程序运行日志(包括成功信息、错误提示、调试详情),便于排查问题(比print更灵活,可保存到文件)。

三、代码树形结构

以下是我们实现的代码的完整树形结构:

复制代码
SinaStockScraper.py  # 主脚本文件  
├─ 全局变量定义  
│  └─ script_dir  # 脚本所在目录路径(通过os.path获取)  
├─ 日志配置(logging.basicConfig)  
│  └─ 日志文件:行业板块爬取日志.log(保存到script_dir)  
├─ SinaIndustryScraper类  # 核心类,封装所有功能  
│  ├─ __init__方法  # 初始化类属性  
│  ├─ 工具方法  
│  │  ├─ get_script_dir_file(filename)  # 生成脚本目录下的文件路径  
│  │  ├─ clean_number(text, is_float)  # 清洗数字(去除符号、转换类型)  
│  │  └─ clean_percent(text)  # 清洗百分比(去除%、转换为浮点数)  
│  ├─ 核心功能方法  
│  │  ├─ fetch_page()  # 获取动态页面并保存源码  
│  │  ├─ parse_table(html)  # 解析HTML,提取11个目标字段  
│  │  └─ save_data()  # 将提取的数据保存为CSV  
│  └─ 主运行方法  
│     └─ run()  # 串联所有步骤(调用fetch_page→parse_table→save_data)  
└─ 主程序入口(if __name__ == "__main__":)  
   └─ 实例化SinaIndustryScraper并调用run()方法  

以下是完整的程序代码

复制代码
import os
import re
import time
import logging
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
import pandas as pd

# 获取脚本所在目录(确保所有文件保存在此目录)
script_dir = os.path.dirname(os.path.abspath(__file__))

# 配置日志(保存到脚本目录)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename=os.path.join(script_dir, '行业板块爬取日志.log')
)
logger = logging.getLogger(__name__)

class SinaIndustryScraper:
    def __init__(self):
        # 浏览器配置
        self.options = webdriver.ChromeOptions()
        self.options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
        self.options.add_experimental_option("excludeSwitches", ["enable-automation"])
        self.driver = webdriver.Chrome(
            service=Service(ChromeDriverManager().install()),
            options=self.options
        )
        # 规避反爬检测
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
        })
        self.target_url = 'https://finance.sina.com.cn/stock/sl/#industry_1'
        self.data = []  # 存储提取的11个字段数据

    def get_script_dir_file(self, filename):
        """生成脚本目录下的文件路径"""
        return os.path.join(script_dir, filename)

    def fetch_page(self):
        """获取页面并保存源码到脚本目录"""
        try:
            self.driver.get(self.target_url)
            # 等待表格加载(最长30秒)
            WebDriverWait(self.driver, 30).until(
                EC.presence_of_element_located((By.TAG_NAME, 'table'))
            )
            # 确保数据完全渲染
            time.sleep(2)
            # 保存页面源码到脚本目录(用于调试)
            page_path = self.get_script_dir_file('页面源码.html')
            with open(page_path, 'w', encoding='utf-8') as f:
                f.write(self.driver.page_source)
            logger.info(f"页面源码已保存至:{page_path}")
            return self.driver.page_source
        except Exception as e:
            logger.error(f"页面加载失败:{str(e)}")
            return None
        finally:
            self.driver.quit()

    def clean_number(self, text, is_float=False):
        """清洗数字(去除逗号、空格,转换类型)"""
        if not text.strip():
            return None
        cleaned = re.sub(r'[,\s]', '', text.strip())
        try:
            return float(cleaned) if is_float else int(cleaned)
        except:
            return text  # 保留原始文本(用于调试)

    def clean_percent(self, text):
        """清洗涨跌幅(去除%,转换为浮点数)"""
        if not text.strip():
            return None
        cleaned = re.sub(r'[%\s]', '', text.strip())
        try:
            return float(cleaned)
        except:
            return text  # 保留原始文本

    def parse_table(self, html):
        """精准提取11个指定字段"""
        if not html:
            return
        
        soup = BeautifulSoup(html, 'lxml')
        # 定位数据表格(优先选择行数量最多的表格)
        tables = soup.find_all('table')
        if not tables:
            logger.error("未找到表格")
            return
        target_table = max(tables, key=lambda t: len(t.find_all('tr')))  # 选行数最多的表格
        rows = target_table.find_all('tr')
        if len(rows) < 2:
            logger.error("表格无数据行")
            return

        # 解析表头(用于校验字段顺序)
        header_cells = rows[0].find_all(['th', 'td'])
        headers = [cell.text.strip() for cell in header_cells]
        logger.info(f"表头字段:{headers}")  # 日志打印表头,方便核对

        # 解析数据行(从第二行开始)
        for row in rows[1:]:
            cells = row.find_all('td')
            if len(cells) < 11:  # 确保至少有11列(匹配11个字段)
                continue

            try:
                # 严格对应11个字段(按页面顺序)
                row_data = {
                    # 行业基础数据
                    '板块': cells[0].text.strip(),
                    '公司家数': self.clean_number(cells[1].text.strip()),
                    '平均价格': self.clean_number(cells[2].text.strip(), is_float=True),
                    '涨跌额': self.clean_number(cells[3].text.strip(), is_float=True),
                    '涨跌幅(%)': self.clean_percent(cells[4].text.strip()),
                    '总成交量(手)': self.clean_number(cells[5].text.strip()),
                    '总成交额(万元)': self.clean_number(cells[6].text.strip(), is_float=True),
                    # 领涨股数据(区分行业字段,避免重名)
                    '领涨股': cells[7].text.strip(),
                    '领涨股涨跌幅(%)': self.clean_percent(cells[8].text.strip()),
                    '领涨股当前价': self.clean_number(cells[9].text.strip(), is_float=True),
                    '领涨股涨跌额': self.clean_number(cells[10].text.strip(), is_float=True)
                }
                self.data.append(row_data)
                logger.debug(f"已提取:{row_data['板块']}")
            except Exception as e:
                logger.warning(f"行解析失败:{str(e)},行内容:{row.text[:100]}")

    def save_data(self):
        """保存CSV到脚本目录"""
        if not self.data:
            print("未提取到有效数据,请查看日志和【页面源码.html】")
            return
        # CSV文件保存到脚本目录
        csv_path = self.get_script_dir_file('行业板块数据.csv')
        df = pd.DataFrame(self.data)
        # 确保字段顺序与需求一致
        df = df[[
            '板块', '公司家数', '平均价格', '涨跌额', '涨跌幅(%)',
            '总成交量(手)', '总成交额(万元)', '领涨股',
            '领涨股涨跌幅(%)', '领涨股当前价', '领涨股涨跌额'
        ]]
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
        print(f"数据提取完成!共 {len(self.data)} 条记录")
        print(f"CSV文件已保存至:{csv_path}")
        logger.info(f"数据已保存至:{csv_path}")

    def run(self):
        print("开始爬取行业板块数据...")
        html = self.fetch_page()
        if not html:
            print("爬取失败,请查看日志:行业板块爬取日志.log")
            return
        self.parse_table(html)
        self.save_data()

if __name__ == "__main__":
    scraper = SinaIndustryScraper()
    scraper.run()

四、代码定义及函数作用

(一)全局变量与日志配置
  1. script_dir

    • 定义:script_dir = os.path.dirname(os.path.abspath(__file__))
    • 作用:通过os.path获取脚本所在目录的绝对路径,确保所有生成的文件(CSV、日志等)统一保存至此目录,避免路径混乱。
  2. 日志配置(logging.basicConfig)

    • 定义:配置日志级别(INFO)、格式(包含时间、级别、信息)、输出文件(行业板块爬取日志.log)。
    • 作用:记录程序运行过程(如 "页面加载成功""解析失败"),便于调试和追踪问题(日志文件保存到脚本目录)。
(二)SinaIndustryScraper 类
1. __init__方法(初始化)
  • 定义:

    复制代码
    def __init__(self):  
        # 浏览器配置(规避反爬、设置User-Agent)  
        # 初始化驱动、目标URL、数据存储列表等  
  • 作用:

    • 配置浏览器参数(禁用自动化特征、设置模拟浏览器的 User-Agent),降低被反爬检测的概率。
    • 初始化 Selenium 浏览器驱动(通过 webdriver_manager 自动管理)。
    • 定义目标 URL(新浪财经行业板块页面)和数据存储列表(self.data)。
2. 工具方法

(1)get_script_dir_file(filename)

  • 定义:def get_script_dir_file(self, filename): return os.path.join(script_dir, filename)
  • 作用:接收文件名,返回脚本目录下的完整路径(确保所有文件保存到脚本所在目录)。

程序正确执行,会产生三个文件。

(2)clean_number(text, is_float=False)

  • 定义:通过正则表达式去除文本中的逗号、空格,根据is_float参数转换为intfloat
  • 作用:清洗 "公司家数""总成交量" 等数值型数据(如将 "1,234" 转换为 1234),确保数据格式规范。

(3)clean_percent(text)

  • 定义:去除文本中的百分号(%),转换为float(如将 "+2.34%" 转换为 2.34)。
  • 作用:清洗 "涨跌幅" 字段,统一为数值类型(便于后续数据分析)。
3. 核心功能方法

(1)fetch_page()

  • 定义:

    复制代码
    def fetch_page(self):  
        # 用Selenium打开目标URL,等待表格加载  
        # 保存页面源码到HTML文件,返回页面源码  
  • 作用:

    • 模拟浏览器访问页面,通过WebDriverWait等待表格加载完成(最长 30 秒)。
    • 保存页面源码到 "页面源码.html"(用于调试,确认是否包含数据),如下图。
    • 返回加载完成的页面 HTML,供后续解析。

(2)parse_table(html)

  • 定义:

    复制代码
    def parse_table(self, html):  
        # 用BeautifulSoup解析HTML,定位数据表格  
        # 提取11个目标字段,通过clean方法清洗后存入self.data  
  • 作用:

    • 定位页面中的数据表格(优先选择行数最多的表格,确保是目标数据)。
    • 解析表头和数据行,按顺序提取 11 个字段(板块、公司家数、平均价格等)。
    • 调用clean_numberclean_percent清洗数据,最终存入self.data列表。

(3)save_data()

  • 定义:

    复制代码
    def save_data(self):  
        # 将self.data转换为DataFrame,按指定顺序保存为CSV  
  • 作用:

    • 若数据为空,提示用户查看日志和源码文件。
    • 若数据有效,用 pandas 将self.data转换为 DataFrame,按 11 个字段的顺序保存为 "行业板块数据.csv"(确保字段顺序与需求一致)。
4. 主运行方法run()
  • 定义:

    复制代码
    def run(self):  
        # 串联流程:调用fetch_page获取页面→调用parse_table解析数据→调用save_data保存结果  
  • 作用:作为程序入口,依次执行 "获取页面→解析数据→保存结果" 的完整流程,并输出关键信息(如爬取成功 / 失败提示)。

(三)主程序入口
  • 定义:if __name__ == "__main__": scraper = SinaIndustryScraper(); scraper.run()
  • 作用:当脚本直接运行时,实例化SinaIndustryScraper类并调用run()方法,启动整个爬取流程。

运行效果如下:

得到如下表格:

总结

代码通过封装成SinaIndustryScraper类,实现了 "动态页面获取→数据解析清洗→结果保存" 的完整流程,依赖多库协同处理动态页面、HTML 解析、数据清洗和文件管理,最终精准提取 11 个行业板块字段并保存到指定目录。