????????在這個多行業持續高速發展的時代,科技正在改變著我們的生活。 在世界科技領域中,中國正占據越來越重要的位置。當下,每個行業都提到了區塊鏈、人工智能、大數據、5G等科技力量,強調了科技在行業咨詢與數據分析領域的重要意義。
????????隨著大數據時代的到來,人工智能等前沿的科技在算法上深刻改變了各個行業,并成為未來行業發展的制高點。隨著人工智能發展而風靡于世的Python,有著簡單易學、運行速度快、可移植、可拓展、可嵌入以及第三方庫豐富等特點,因而在數學、大數據分析以及行業數據和財務數據分析中都有著得天獨厚的優勢。
????????Python的語法很容易實現那些金融算法和數據計算,每個數學語句都能轉變成一行Python代碼,每行代碼都能允許超過十萬次的計算量。Python 效率較為明顯的領域之一是交互式的數據分析,對于大數據來說,它無疑是一個極為合適的選擇。這一領域從IPython、Jupyter Notebook等有力工具和Pandas之類的程序庫中獲益良多。? ?
????????用幾行代碼就足以完成行業分析中的典型復雜任務——數據收集、復雜和重復的數學計算以及結果的可視化,令人覺得不可思議。我們在熟練應用Python工具和庫的時候,應將焦點放在自身的領域上,而不用關心復雜的技術細節。分析者們可以快速反應,幾乎可以實時地提供寶貴的意見,確保自己比競爭對手先行一步。這種效率的提高很容易轉換為可度量的財務效果。
????????“巧婦難為無米之炊”,找不到數據,量化分析也就無從談起。對于行業分析來說,獲取數據是量化分析的第一步。Python的強大功能之一就是數據獲取(爬蟲技術) 。例如,我們在做一些行業分析的時候,需要找到自己的同業競爭者,并且是有公開信息可查的同業競爭者,那就需要從眾多上市公司中進行篩選。本項目的行業數據就是通過爬取新浪財經網站上行業板塊的股票信息得到的,如圖所示。
????????接下來我們就寫一個爬蟲,這個爬蟲程序通過以下步驟獲取新浪財經的行業板塊股票信息:
- 首先請求行業板塊頁面并解析出各行業的鏈接
- 然后遍歷每個行業板塊,獲取該行業下的所有股票信息
- 對獲取的股票數據進行結構化處理
- 將最終結果保存為 CSV 文件
????????程序包含了完善的錯誤處理和日志記錄功能,同時通過隨機延時避免被目標網站封禁 IP。在使用時,可能需要根據新浪財經網站的實際結構調整 HTML 解析部分的選擇器。
? ? ? ? 接下來我們就從原理、環境安裝、庫的選擇與使用、代碼組織以及具體實現等步驟來實現:
一、運行環境及安裝
(一)運行環境要求
- Python 版本:建議 Python 3.7 及以上版本(兼容主流 Python 3.x 版本,確保對現代庫的支持),當前的執行環境是在Python 3.13上完成的。
- 操作系統:兼容 Windows、macOS、Linux(代碼中文件路徑處理使用
os.path
,跨平臺適配)。
(二)環境安裝步驟
-
安裝 Python:
從Python 官網下載對應系統的 Python 安裝包,安裝時勾選 “Add Python to PATH”(添加到環境變量),完成后通過python --version
或py --version
驗證安裝成功。 -
安裝依賴庫:
代碼依賴多個第三方庫,通過pip
命令統一安裝:pip install selenium webdriver-manager beautifulsoup4 pandas lxml
其中beautifulsoup4 安裝時也可以用名稱bs4。(若安裝失敗,可嘗試使用
pip3
或管理員權限運行命令)
二、使用的庫及作用
(一)核心功能庫
-
selenium:
- Selenium是一款自動化測試利器,可以自動化模擬人操作瀏覽器的行為,所以也可以用于網絡爬蟲。Selenium調用瀏覽器必須有一個WebDriver驅動文件,下載好后把驅動程序放到Python安裝目錄里即可。
- 作用:模擬瀏覽器行為,處理動態加載頁面(新浪財經的股票數據通過 JavaScript 動態渲染,
requests
無法獲取,需用瀏覽器模擬)。 - 關鍵功能:自動打開瀏覽器、訪問 URL、等待頁面加載、獲取渲染后的頁面源碼。
-
webdriver-manager:
- 作用:自動管理瀏覽器驅動(如 ChromeDriver),無需手動下載和配置驅動路徑,解決驅動版本與瀏覽器不匹配的問題。
-
beautifulsoup4(bs4):
- 為了向用戶友好地顯示,請求的網頁返回數據中包含了大量的HTML 標簽、CSS語句、JavaScript語句等,我們要在返回的文檔中提取出所要的數據不是件容易的事情。Beautiful Soup可以通過解析返回的文檔,提供一些簡單的函數,為用戶提供需要抓取的數據。有了它,我們可以很方便地提取出HTML或XML標簽中的內容。
- 作用:解析 HTML 頁面源碼,提取表格、字段等結構化數據(將復雜的 HTML 轉換為可遍歷的對象,便于定位和提取數據)。
-
lxml:
- 作用:作為 BeautifulSoup 的解析器,提高 HTML 解析效率和準確性(比 Python 內置的
html.parser
更快,支持復雜選擇器)。
- 作用:作為 BeautifulSoup 的解析器,提高 HTML 解析效率和準確性(比 Python 內置的
(二)數據處理與工具庫
-
pandas:
- 作用:處理結構化數據,將提取的股票信息轉換為 DataFrame,最終保存為 CSV 文件(簡化數據存儲流程,支持中文編碼)。
-
os:
- 作用:處理文件路徑,確保所有生成的文件(CSV、日志、HTML 源碼)保存到腳本所在目錄(跨平臺適配路徑格式)。
-
re:
- 作用:通過正則表達式清洗數據(去除數字中的逗號、百分號等符號,轉換為標準數值類型)。
-
time:
- 作用:添加延時,確保頁面數據完全渲染(動態頁面加載需要時間,避免提前獲取未完成的內容)。
-
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_sourceexcept Exception as e:logger.error(f"頁面加載失敗:{str(e)}")return Nonefinally:self.driver.quit()def clean_number(self, text, is_float=False):"""清洗數字(去除逗號、空格,轉換類型)"""if not text.strip():return Nonecleaned = 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 Nonecleaned = re.sub(r'[%\s]', '', text.strip())try:return float(cleaned)except:return text # 保留原始文本def parse_table(self, html):"""精準提取11個指定字段"""if not html:returnsoup = BeautifulSoup(html, 'lxml')# 定位數據表格(優先選擇行數量最多的表格)tables = soup.find_all('table')if not tables:logger.error("未找到表格")returntarget_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個字段)continuetry:# 嚴格對應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")returnself.parse_table(html)self.save_data()if __name__ == "__main__":scraper = SinaIndustryScraper()scraper.run()
四、代碼定義及函數作用
(一)全局變量與日志配置
-
script_dir:
- 定義:
script_dir = os.path.dirname(os.path.abspath(__file__))
- 作用:通過
os.path
獲取腳本所在目錄的絕對路徑,確保所有生成的文件(CSV、日志等)統一保存至此目錄,避免路徑混亂。
- 定義:
-
日志配置(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
參數轉換為int
或float
。 - 作用:清洗 “公司家數”“總成交量” 等數值型數據(如將 “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_number
和clean_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 個行業板塊字段并保存到指定目錄。