
簡介本資源是一套完整的Python數據工程實踐項目面向計算機、數學、電子信息等專業的本科生及初學者聚焦疫情數據與社交媒體輿情的采集、治理與分析全流程。項目涵蓋疫情實時數據爬取、微博關鍵詞定向抓取含MySQL數據庫存儲、結構化數據清洗與特征構建、多維度可視化呈現含疫情趨勢圖與詞云以及基于情感詞典的微博文本細粒度情感分析適合作為課程設計、期末大作業或畢業設計參考。壓縮包共15個文件含7個核心Python腳本爬蟲、預處理、可視化、情感分析模塊、3個CSV數據集含標注樣本與情感關鍵詞庫、2個文本詞典正/負向詞表、1個JSON微博原始數據、1張結果示意圖及1份Markdown項目說明文檔整體大小23.87MB。已有728人學習下載提供開箱即用的完整代碼鏈路、清晰的模塊劃分與可復現的數據分析流程便于理解數據從采集到價值輸出的閉環邏輯。1. 項目概述一個數據驅動的疫情社會觀察系統最近在整理硬盤翻出來一個前兩年做的老項目當時主要是想看看社交媒體上的公眾情緒和官方發布的疫情數據之間有沒有什么有趣的關聯。這個項目麻雀雖小五臟俱全從數據抓取、清洗、存儲到分析和可視化走完了一個完整的數據分析閉環。它不是簡單的“爬蟲可視化”的堆砌核心在于將結構化的疫情統計數據與非結構化的社交媒體文本進行關聯分析試圖從數據層面理解特定時期的社會心態。簡單來說這個系統干了三件事第一從權威數據源定時抓取結構化的疫情數據如新增確診、累計治愈等第二同步從微博抓取與疫情相關的關鍵詞下的實時討論內容第三將這兩類數據清洗后存入數據庫并進行情感傾向分析最后通過可視化圖表呈現數據趨勢與公眾情緒的對比。這非常適合想通過一個實戰項目系統學習Python數據獲取、處理、分析和呈現全流程的朋友尤其是對社會計算或輿情分析感興趣的同學。2. 項目整體架構與技術選型解析2.1 核心模塊拆解與設計思路整個項目可以清晰地劃分為四個核心模塊它們之間通過數據庫進行數據流轉形成一個松耦合但邏輯緊密的管道。數據采集層這是項目的“觸手”。我將其設計為兩個獨立的爬蟲但共享同一套請求管理和異常處理機制。疫情數據爬蟲目標是各級衛健委官網或聚合數據平臺。這類數據通常以JSON接口或規整的HTML表格形式存在變化頻率低每日更新但要求數據準確、穩定。因此這個爬蟲的核心是穩健性需要處理好反爬策略如請求頭、頻率限制并設計重試和驗證邏輯確保抓取到的每個數字都是可靠的。微博關鍵詞爬蟲目標是微博的搜索頁面或移動端接口。微博數據是非結構化的文本、時間、用戶信息混合體且反爬嚴密動態參數、登錄態、滑塊驗證。這個爬蟲的核心是突破性與效率。我選擇模擬移動端請求并利用關鍵詞輪詢持續抓取最新發布的微博。這里的關鍵在于如何穩定地獲取數據而不是一味求快。注意微博爬蟲的合規性是紅線。本項目代碼僅用于學習網絡請求解析、動態頁面處理和數據抽取技術。實際應用中必須嚴格遵守robots.txt協議控制請求頻率避免對目標服務器造成壓力且所有數據僅應用于個人學習與研究分析。數據存儲層選用關系型數據庫MySQL作為數據倉庫。為什么不用更簡單的CSV或NoSQL因為我們需要處理兩類差異很大的數據并且后續需要頻繁地進行關聯查詢和聚合分析。表結構設計epidemic_data表存儲日期、地區、新增確診、新增疑似、累計確診、治愈、死亡等字段。以(date, region)作為復合主鍵。weibo_data表存儲微博ID、發布時間、用戶昵稱、微博正文、關鍵詞、抓取時間等。最重要的是我增加了一個cleaned_text字段用于存放預處理后的純文本為情感分析做準備。這種設計便于我們執行諸如“查詢某日全國新增確診數并關聯分析當天‘隔離’關鍵詞下的微博情感傾向”這樣的操作。數據處理與分析層這是項目的“大腦”。數據預處理這是最繁瑣但決定分析質量的一步。對于疫情數據主要是處理缺失值、異常值如某日數據為負。對于微博文本則復雜得多去除廣告、表情符號、URL鏈接進行中文分詞去除停用詞如“的”、“了”文本規范化。情感分析我采用了基于預訓練模型如SnowNLP或百度AI開放平臺的情感傾向分析API的方法。SnowNLP適合離線、大批量處理雖然精度不是最高但一致性較好。將微博cleaned_text字段的內容送入模型得到情感極性分值如0-1越接近1越積極存入數據庫的新字段sentiment_score中。數據可視化層使用MatplotlibSeaborn庫目標是生成能清晰對比趨勢的圖表。疫情數據通常用折線圖展示新增/累計趨勢用堆疊面積圖展示確診、治愈、死亡病例的構成變化。微博情感數據可以用每日情感平均分折線圖來觀察情緒波動用熱力圖展示不同關鍵詞在不同時間段的情感強度。最關鍵的是將兩條折線例如“當日新增確診數”和“當日‘疫情’關鍵詞微博平均情感分”放在同一個坐標系中觀察它們的走勢關聯這是整個項目視覺上的亮點。2.2 關鍵技術棧與工具選型理由Python 3.8: 生態豐富是數據科學和爬蟲領域的事實標準。Requests BeautifulSoup4: 用于疫情數據爬取和靜態頁面解析。Requests簡潔高效BeautifulSoup對新手友好足以應對大多數政府公開數據頁面。Selenium (可選): 作為微博爬蟲的備選方案。當接口逆向工程困難時可以用Selenium模擬瀏覽器行為直接獲取渲染后的數據缺點是速度慢、資源消耗大。本項目核心是學習因此我同時提供了Requests逆向和Selenium兩種思路的代碼注釋。PyMySQL / SQLAlchemy: 用于連接和操作MySQL數據庫。PyMySQL更直接SQLAlchemy的ORM模式能讓代碼更優雅適合中型項目。Pandas NumPy: 數據預處理的絕對核心。Pandas的DataFrame是處理表格數據的利器清洗、轉換、聚合操作都非常方便。Jieba: 最好的中文分詞Python庫之一用于對微博正文進行分詞。SnowNLP / TextBlob: 輕量級的情感分析庫。SnowNLP針對中文優化開箱即用適合學習入門。Matplotlib Seaborn:Matplotlib是基礎繪圖庫功能強大但配置繁瑣Seaborn基于前者提供了更美觀的統計圖表樣式和高級接口兩者結合使用效率最高。Schedule / APScheduler: 用于實現爬蟲的定時任務讓數據采集自動化運行。3. 核心模塊實現細節與實操要點3.1 雙爬蟲的穩健實現與防封禁策略疫情數據爬蟲的實現 關鍵在于找到穩定、結構化的數據源。我以某數據平臺的公開API為例。import requests import pandas as pd import time def fetch_epidemic_data(date_str): 獲取指定日期的疫情數據 url fhttps://api.example.com/epidemic/data # 示例URL實際需替換 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..., Accept: application/json } params {date: date_str} try: # 添加延遲避免請求過快 time.sleep(1) resp requests.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() # 檢查HTTP錯誤 data resp.json() # 解析JSON轉換為規整的列表 records [] for item in data[results]: record { date: item[date], region: item[region][name], confirmed_new: item[confirmed][new], confirmed_total: item[confirmed][total], cured_new: item[cured][new], # ... 其他字段 } records.append(record) return pd.DataFrame(records) except requests.exceptions.RequestException as e: print(f請求失敗: {e}) return pd.DataFrame() # 返回空DataFrame后續統一處理實操心得對于公開API務必仔細閱讀其文檔了解調用頻率限制。將請求頭User-Agent設置為常見的瀏覽器標識并合理使用time.sleep()是基本的禮貌。所有網絡請求都必須包裹在try-except塊中并記錄日志這是保證爬蟲長期穩定運行的基礎。微博關鍵詞爬蟲的實現 這里以通過解析移動端接口為例難度較高。import requests import re import json def fetch_weibo_by_keyword(keyword, max_page5): 通過關鍵詞搜索微博模擬移動端接口 base_url https://m.weibo.cn/api/container/getIndex headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15..., Referer: fhttps://m.weibo.cn/search?keyword{keyword} } weibo_list [] for page in range(1, max_page1): params { containerid: f100103type1q{keyword}, page_type: searchall, page: page } try: resp requests.get(base_url, headersheaders, paramsparams, timeout15) data resp.json() # 解析cards中的微博信息 if data.get(ok) 1: cards data.get(data, {}).get(cards, []) for card in cards: mblog card.get(mblog) if mblog: weibo_info { id: mblog.get(id), text: mblog.get(text), created_at: mblog.get(created_at), user_name: mblog.get(user, {}).get(screen_name), keyword: keyword } # 簡單清洗HTML標簽 weibo_info[text] re.sub(r[^], , weibo_info[text]) weibo_list.append(weibo_info) time.sleep(2) # 非常重要頁間延遲 except Exception as e: print(f抓取關鍵詞{keyword}第{page}頁失敗: {e}) break return weibo_list核心技巧微博的反爬策略會經常變動。containerid等參數可能需要通過瀏覽器開發者工具抓包分析獲取。User-Agent必須設置為移動端。頁間延遲(time.sleep)是避免IP被臨時封禁的最有效手段之一不要貪快。此外可以考慮使用IP代理池來應對更嚴格的限制但這超出了基礎學習的范圍。3.2 數據庫設計與高效數據入庫我使用SQLAlchemy來定義數據模型這樣代碼更清晰也便于維護。from sqlalchemy import create_engine, Column, String, Integer, Float, Date, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base declarative_base() class EpidemicData(Base): __tablename__ epidemic_data id Column(Integer, primary_keyTrue, autoincrementTrue) date Column(Date, nullableFalse) region Column(String(50), nullableFalse) confirmed_new Column(Integer) confirmed_total Column(Integer) cured_new Column(Integer) # ... 其他字段 # 設置復合唯一約束防止重復插入同一天同一地區的數據 __table_args__ (UniqueConstraint(date, region, nameuix_date_region),) class WeiboData(Base): __tablename__ weibo_data id Column(Integer, primary_keyTrue, autoincrementTrue) weibo_id Column(String(30), uniqueTrue, nullableFalse) # 微博唯一ID created_at Column(String(30)) user_name Column(String(100)) raw_text Column(Text) # 原始文本 cleaned_text Column(Text) # 清洗后文本 keyword Column(String(50)) sentiment_score Column(Float) # 情感分析得分 crawl_time Column(Date) # 抓取時間 # 數據庫連接與會話 engine create_engine(mysqlpymysql://user:passwordlocalhost:3306/epidemic_analysis?charsetutf8mb4) Base.metadata.create_all(engine) # 創建表 Session sessionmaker(bindengine)注意事項字符集微博文本包含大量Emoji和特殊符號MySQL的utf8mb4字符集是必須的標準的utf8無法存儲四字節的Emoji。唯一約束為微博ID設置UNIQUE約束可以避免在多次抓取中插入重復數據。疫情數據表則通過(date, region)的復合唯一約束來去重。批量插入無論是Pandas的to_sql方法設置if_existsappend還是使用SQLAlchemy的session.bulk_save_objects()都比逐條插入session.add()快一個數量級。處理爬蟲數據時務必采用批量操作。3.3 數據預處理的實戰流程數據預處理在單獨的腳本中完成它從數據庫讀取原始數據處理后再寫回或存入新表。疫情數據清洗import pandas as pd from sqlalchemy import create_engine engine create_engine(mysqlpymysql://...) df_epidemic pd.read_sql(SELECT * FROM epidemic_data WHERE date 2022-01-01, engine) # 1. 處理缺失值對于數值字段用前向填充或插值法 df_epidemic[confirmed_new].fillna(methodffill, inplaceTrue) # 2. 處理異常值假設新增確診不應為負將其置為0或標記 df_epidemic.loc[df_epidemic[confirmed_new] 0, confirmed_new] 0 # 3. 確保日期格式 df_epidemic[date] pd.to_datetime(df_epidemic[date]) # 4. 去重基于數據庫約束這里作為二次保險 df_epidemic.drop_duplicates(subset[date, region], keepfirst, inplaceTrue)微博文本清洗與情感分析 這是更核心的部分。import re import jieba from snownlp import SnowNLP import numpy as np def clean_weibo_text(text): 清洗單條微博文本 if not isinstance(text, str): return # 去除網頁標簽、URL、用戶、話題符號 text re.sub(r[^], , text) text re.sub(rhttp[s]?://\S, , text) text re.sub(r[\w\u4e00-\u9fa5], , text) text re.sub(r#([^#])#, r\1, text) # 保留話題內容去掉#號 # 去除Emoji和特殊符號簡單版 text re.sub(r[^\w\u4e00-\u9fa5。、“”‘’《》【】\s], , text) # 分詞并用空格連接SnowNLP可直接處理字符串但分詞后效果更好 words jieba.lcut(text) cleaned .join([w for w in words if w.strip()]) return cleaned def analyze_sentiment_batch(text_list): 批量情感分析返回分數列表 scores [] for text in text_list: if text and len(text.strip()) 5: # 過濾過短文本 try: s SnowNLP(text) scores.append(s.sentiments) # 情感積極度0-1 except: scores.append(0.5) # 分析失敗取中性值 else: scores.append(0.5) # 無意義文本取中性值 return scores # 從數據庫讀取原始微博數據 df_weibo pd.read_sql(SELECT id, raw_text FROM weibo_data WHERE cleaned_text IS NULL, engine) # 應用清洗函數 df_weibo[cleaned_text] df_weibo[raw_text].apply(clean_weibo_text) # 批量情感分析 texts_for_analysis df_weibo[cleaned_text].tolist() sentiment_scores analyze_sentiment_batch(texts_for_analysis) df_weibo[sentiment_score] sentiment_scores # 將清洗后的數據和情感分寫回數據庫 # ... (使用df_weibo.to_sql或ORM更新對應記錄)經驗之談文本清洗沒有“標準答案”。你需要根據分析目標調整清洗強度。例如如果關心用戶情緒保留表情符號可能更有價值需要專門的表情分析。SnowNLP的情感分析對于長文本和正式文本效果尚可但對于微博短文本、網絡流行語、反諷句的識別能力有限。這是所有情感分析工具的共性局限在解讀結果時必須心中有數。4. 數據可視化讓趨勢與關聯一目了然可視化不是為了畫圖而畫圖而是為了回答問題。我們的核心問題是疫情數據的變化與公眾情緒波動有關聯嗎4.1 疫情趨勢可視化首先我們繪制疫情本身的發展趨勢。import matplotlib.pyplot as plt import seaborn as sns sns.set_style(whitegrid) # 設置Seaborn樣式 plt.rcParams[font.sans-serif] [SimHei] # 解決中文顯示問題 plt.rcParams[axes.unicode_minus] False # 假設我們已經有一個按日期聚合的疫情DataFrame: df_epidemic_daily fig, axes plt.subplots(2, 1, figsize(14, 10)) # 子圖1新增確診與新增治愈的對比折線圖 axes[0].plot(df_epidemic_daily[date], df_epidemic_daily[confirmed_new], label新增確診, colorcoral, linewidth2) axes[0].plot(df_epidemic_daily[date], df_epidemic_daily[cured_new], label新增治愈, colorlightgreen, linewidth2) axes[0].set_title(每日新增確診與新增治愈趨勢對比) axes[0].set_xlabel(日期) axes[0].set_ylabel(人數) axes[0].legend() axes[0].fill_between(df_epidemic_daily[date], 0, df_epidemic_daily[confirmed_new], colorcoral, alpha0.3) # 子圖2累計確診、治愈、死亡的堆疊面積圖 axes[1].stackplot(df_epidemic_daily[date], df_epidemic_daily[confirmed_total], df_epidemic_daily[cured_total], df_epidemic_daily[dead_total], labels[累計確診, 累計治愈, 累計死亡], colors[lightcoral, lightgreen, gray]) axes[1].set_title(累計病例構成變化) axes[1].set_xlabel(日期) axes[1].set_ylabel(累計人數) axes[1].legend(locupper left) plt.tight_layout() plt.savefig(epidemic_trend.png, dpi300, bbox_inchestight) plt.show()4.2 微博情感趨勢與關聯分析接下來分析微博情緒并嘗試與疫情數據關聯。# 從數據庫讀取按日期和關鍵詞聚合的情感平均分 df_sentiment_daily pd.read_sql( SELECT DATE(created_at) as date, keyword, AVG(sentiment_score) as avg_sentiment FROM weibo_data WHERE sentiment_score IS NOT NULL GROUP BY DATE(created_at), keyword ORDER BY date , engine) # 假設我們關注“疫情”這個關鍵詞 df_keyword df_sentiment_daily[df_sentiment_daily[keyword] 疫情].copy() # 合并疫情數據與情感數據按日期連接 df_merged pd.merge(df_epidemic_daily, df_keyword, ondate, howinner) fig, ax1 plt.subplots(figsize(15, 7)) color tab:red ax1.set_xlabel(日期) ax1.set_ylabel(新增確診數, colorcolor) line1 ax1.plot(df_merged[date], df_merged[confirmed_new], colorcolor, label新增確診, linewidth2) ax1.tick_params(axisy, labelcolorcolor) # 創建第二個Y軸共享同一個X軸 ax2 ax1.twinx() color tab:blue ax2.set_ylabel(微博情感平均分, colorcolor) line2 ax2.plot(df_merged[date], df_merged[avg_sentiment], colorcolor, label情感分(疫情關鍵詞), linestyle--) ax2.tick_params(axisy, labelcolorcolor) # 情感分范圍是0-1可以固定Y軸范圍以便觀察波動 ax2.set_ylim(0, 1) # 添加圖例 lines line1 line2 labels [l.get_label() for l in lines] ax1.legend(lines, labels, locupper left) plt.title(新增確診趨勢與“疫情”關鍵詞微博情感趨勢對比) fig.tight_layout() plt.savefig(correlation_analysis.png, dpi300) plt.show()通過這張雙Y軸圖我們可以直觀地觀察兩條曲線的走勢。例如可能發現在新增確診數大幅攀升后的幾天微博情感平均分會出現一個明顯的低谷情緒轉向消極。這只是一個粗略的觀察嚴謹的相關性分析還需要計算統計指標如皮爾遜相關系數。5. 項目部署、優化與常見問題排查5.1 工程化與自動化部署一個學習項目不能只停留在Jupyter Notebook里。我將其模塊化并編寫了主調度腳本。spider_epidemic.py: 疫情數據爬蟲模塊。spider_weibo.py: 微博爬蟲模塊。data_processor.py: 數據清洗與情感分析模塊。visualizer.py: 可視化繪圖模塊。config.py: 配置文件存放數據庫連接信息、API密鑰如有、關鍵詞列表等。main_scheduler.py: 主調度腳本使用schedule庫定時運行任務。# main_scheduler.py 示例 import schedule import time from spider_epidemic import main as run_epidemic_spider from spider_weibo import main as run_weibo_spider from data_processor import main as run_data_processor def job(): print(開始每日數據任務...) run_epidemic_spider() # 抓取昨日疫情數據 run_weibo_spider() # 抓取最新微博 run_data_processor() # 處理新數據并分析 print(每日數據任務完成。) # 每天上午9點執行 schedule.every().day.at(09:00).do(job) while True: schedule.run_pending() time.sleep(60)可以將這個腳本放在服務器上用nohup或systemd在后臺運行實現全自動化數據流水線。5.2 性能優化與擴展思路異步爬蟲對于微博這類需要大量I/O等待的爬蟲可以使用aiohttpasyncio實現異步并發極大提升抓取效率。但要注意目標網站的壓力控制并發數。增量爬取微博爬蟲不應每次都從頭翻頁。可以記錄上次抓取到的最新微博ID下次請求時只抓取比這個ID更新的數據。情感分析升級SnowNLP是基線方案。可以嘗試使用更強大的預訓練模型如BERT在自己的標注數據上微調以獲得更精準的情感分類積極、消極、中性甚至情緒識別憤怒、恐懼、悲傷等。前端展示用Flask或Django搭建一個簡單的Web應用使用ECharts或Plotly庫生成交互式圖表讓分析結果可以通過網頁動態查看。5.3 常見問題與排查實錄在開發和運行過程中我遇到了不少坑這里記錄下最典型的幾個問題1疫情數據爬蟲突然返回空數據或403錯誤。排查首先檢查目標網站是否改版API地址或參數是否變化。其次檢查請求頭User-Agent和Referer是否被屏蔽嘗試更換。最后檢查IP是否被臨時封禁。解決更新請求頭信息在代碼中加入更長的隨機延遲考慮使用付費的代理IP服務針對高頻率抓取如果網站提供優先使用其官方數據接口或開放數據集。問題2微博爬蟲運行一段時間后返回的數據全是“加載中”或需要登錄。排查這是最典型的問題。微博的未登錄態訪問有嚴格限制和頻率限制。通過瀏覽器開發者工具檢查網絡請求中是否包含cookies或authorization等認證信息。解決方案A簡單但不穩定用Selenium模擬登錄獲取cookies后供Requests使用。但登錄可能觸發滑塊驗證。方案B推薦學習研究微博移動端或PC端的登錄接口逆向加密過程實現程序化登錄獲取令牌(access_token)。這涉及復雜的逆向工程是爬蟲技術進階的必經之路。忠告對于學習項目可以手動獲取一次cookies放入代碼中短期使用。長期運行必須考慮自動化更新認證信息的機制。問題3情感分析結果不準確很多反諷句被判斷為積極。排查這是自然語言處理特別是中文短文本情感分析的普遍難題。SnowNLP的訓練語料比較通用對網絡用語和反諷識別能力弱。解決降低期望理解工具的局限性將分析結果視為一種“情緒熱度”的粗略指標而非精確的情感判斷。后處理規則建立一份“負面詞”詞典如果文本中包含“無語”、“離譜”等詞即使模型打分高也手動調整為負面或中性。升級模型如5.2所述使用更先進的模型并在特定領域的語料上微調。問題4可視化圖表中文顯示為方框。排查Matplotlib默認字體不包含中文。解決代碼中已給出方案。確保系統存在中文字體如SimHei黑體并通過plt.rcParams[font.sans-serif]進行設置。在Linux服務器上部署時可能需要手動安裝中文字體。問題5數據庫寫入速度慢。排查是否在循環中逐條執行session.add()和session.commit()解決務必使用批量操作。對于Pandas用df.to_sql(..., if_existsappend, indexFalse, chunksize500)。對于SQLAlchemy ORM使用session.bulk_save_objects(list_of_objects)最后再統一session.commit()。這個項目從構思到實現幾乎踩遍了數據采集和分析各個階段的常見坑。它最大的價值不在于得出了某個驚天動地的結論而在于完整地實踐了一套數據驅動的解決方案。當你親手讓散落各處的數據經過自己的代碼管道最終變成一幅能講述故事的圖表時那種感覺遠比單純調用一個分析API要深刻得多。對于初學者我建議不要一開始就追求大而全可以先實現疫情數據爬取和可視化再逐步加入微博爬蟲和情感分析像搭積木一樣把這個系統構建起來每一步都吃透其中的原理和問題這樣收獲才是實實在在的。本文還有配套的精品資源點擊獲取