简介:这是一套面向数据分析与自然语言处理初学者的微博评论情感分析实战项目,聚焦网络爬虫、中文文本处理与情感判别全流程,适用于高校课程设计、NLP入门实践及社交媒体舆情分析场景。资源包共21个文件,含2个核心Python脚本(weibo_comment_crawler.py与text_emotion.py)、8个中文词典类txt文件(涵盖正向词、负向词、程度词、停用词等)、4张可视化结果图(词云、评论数统计等)、1个CSV样本数据及MySQL建表说明,辅以README文档与依赖配置文件,结构清晰、模块解耦。压缩包仅569KB,轻量易部署,已吸引111人学习下载。读者可直接运行爬虫获取微博评论,调用SnowNLP/HanLP完成情感打分,结合jieba分词与词频统计生成可视化图表,并基于真实词典体系理解中文情感分析工程落地的关键细节,是少有的覆盖“采集—存储—清洗—分析—呈现”全链路的精简型教学级源码包。
1. 项目概述与核心价值
最近在做一个舆情分析的小项目,核心需求是从微博这个巨大的社交舆论场里,把用户对特定话题的评论“挖”出来,然后分析这些评论背后是喜是忧,是赞是弹。听起来像是“微博评论爬虫”加“情感分析”的组合拳,对吧?这确实是很多做市场分析、品牌监测、社会热点研究的朋友们常遇到的需求。微博作为中文互联网最重要的舆论平台之一,其评论数据蕴含着最直接、最鲜活的公众情绪和观点。但手动收集和分析海量评论无异于大海捞针,所以一套自动化的数据抓取与情感研判系统就显得至关重要。
这个项目的核心目标很明确:自动化、批量化地获取指定微博下的评论数据,并对其情感倾向进行量化分析。它适合谁呢?如果你是市场或公关从业者,想实时了解新品发布或危机事件后的用户口碑;如果你是社科研究者,需要分析某个社会议题的公众情绪演变;或者你是一名数据爱好者,想用真实数据练手爬虫和自然语言处理技术,那么这个项目都能为你提供一个非常贴近实战的练手场景。整个过程会涉及到网络爬虫如何绕过反爬机制、如何解析微博复杂的页面结构、以及如何运用自然语言处理模型对中文短文本进行情感判断,技术栈覆盖了Python爬虫、数据清洗和机器学习/深度学习应用,是一套综合性很强的数据工程实践。
2. 项目整体设计与思路拆解
2.1 核心需求解析与技术选型
接到“爬取微博评论并做情感分析”这个任务,首先要拆解成两个相对独立但又紧密衔接的模块:数据获取模块和情感分析模块。
对于数据获取模块,核心挑战在于微博的反爬策略相当成熟。直接使用requests库模拟请求,很快就会遇到登录验证、滑动验证码、请求频率限制等问题。因此,技术选型上必须更“聪明”一些。我放弃了早期常用的简单requests+BeautifulSoup方案,因为面对动态加载的评论(Ajax)和复杂的登录态维护,这套组合拳显得力不从心。最终选定的核心工具是Selenium配合ChromeDriver。为什么是它?因为Selenium可以模拟真实浏览器的行为,完美渲染JavaScript动态加载的内容,这对于获取需要滚动加载的微博评论流至关重要。虽然速度上比直接请求接口慢,但稳定性和绕过反爬的能力是首选考量。当然,为了提升效率,我会在登录成功后,尝试提取关键的cookies并移植到requests会话中,对部分静态接口进行混合请求。
情感分析模块的选型则是一个精度与复杂度权衡的过程。最简单的是基于情感词典的方法,比如匹配“好”、“开心”、“垃圾”、“失望”等正面或负面词汇,计算权重。这种方法速度快、规则透明,但缺点是对新兴网络用语(如“yyds”、“绝绝子”、“emo”)和反讽句式(如“这操作真是6”)识别能力极差。为了获得更可靠的分析结果,我选择了基于预训练模型的深度学习方案。SnowNLP是一个不错的入门选择,它是针对中文文本的情感分析库,但模型较旧。更优的方案是使用Transformers库加载在大量中文语料上预训练好的模型,例如bert-base-chinese,并对其进行下游任务(情感分类)的微调。考虑到本项目更多是示范完整流程,我会先展示基于SnowNLP的快速实现,然后阐述如何升级到BERT方案,这样能兼顾不同层次读者的需求。
2.2 系统架构与流程设计
整个系统的运行流程可以清晰地分为四个阶段,形成一个从数据源到洞察的完整闭环:
- 目标定位与模拟登录:首先需要确定要爬取的微博帖子(通过微博ID或URL)。然后,使用
Selenium自动化打开微博登录页,输入账号密码(或通过扫码)完成登录,获取有效的登录会话状态。这是后续所有数据抓取的通行证。 - 评论数据爬取与解析:在登录状态下,访问目标微博页面。通过
Selenium控制浏览器不断向下滚动,触发评论的异步加载。在这个过程中,需要编写精准的XPath或CSS选择器,从动态加载的HTML页面结构中,定位并提取每一条评论的发布者、内容、时间、点赞数等核心字段。同时,必须设计合理的等待时间和滚动策略,既要保证数据加载完整,又要避免操作过快被识别为机器人。 - 数据清洗与存储:爬取到的原始文本数据往往包含大量“噪声”,如表情符号([笑cry])、话题标签(#某话题#)、@用户、网页链接等。这些内容对于情感分析是干扰项,需要进行清洗。清洗后的结构化数据(如Pandas DataFrame)将被保存到本地文件(CSV或JSON)或数据库中,以便后续分析。
- 情感倾向分析与可视化:将清洗后的评论文本输入到情感分析模型中,得到每条评论的情感极性分数(如
SnowNLP输出0到1之间的值,越接近1越正面)或分类标签(正面/负面/中性)。最后,通过图表库(如Matplotlib,Seaborn)对分析结果进行可视化,例如绘制情感分布饼图、正面/负面评论词云、情感分数随时间的变化趋势等,让数据结论一目了然。
这个架构确保了项目的可扩展性。例如,爬虫模块可以改为爬取多个相关微博,形成话题数据集;分析模块可以替换更先进的模型,或增加主题聚类、关键词提取等更多分析维度。
3. 核心细节解析与实操要点
3.1 微博登录与反爬对抗策略
微博的登录是爬虫的第一道,也是最坚固的防线。直接使用requests发送登录请求,会面临加密参数和动态令牌的挑战,逆向分析成本较高。因此,采用Selenium模拟真人操作是目前最稳妥的方案。
实操中,我推荐使用“扫码登录”而非“账号密码登录”。原因有二:第一,避免了在代码中明文存储或输入密码,更安全;第二,微博对账号密码登录的异常检测更严格,容易触发验证码,而扫码登录相对顺畅。实现时,我们用Selenium打开微博的扫码登录页面,然后程序会暂停,等待用户手动用手机App扫码确认。确认登录后,浏览器便获得了有效的登录态cookies。
注意:务必为
Selenium配置合理的浏览器选项,以更好地模拟真人。例如,添加User-Agent,禁用Automation控制标志(excludeSwitches: [enable-automation]),并启用Stealth插件或添加反检测脚本,这能显著降低被识别为爬虫的风险。
登录成功后,一个关键技巧是提取并保存本次会话的cookies。你可以通过driver.get_cookies()获取,并将其转化为requests库可用的字典格式。这样,在后续需要快速、批量请求某些已知结构的API接口(如获取单条评论的详细信息)时,就可以切换到更轻量、更快的requests会话,实现“Selenium破门,requests搬运”的混合模式,提升整体效率。
3.2 评论数据抓取的动态加载处理
微博的评论是典型的“瀑布流”式无限滚动加载。这意味着,你第一次打开页面时,只能看到最顶部的几十条评论,随着鼠标滚动,才会通过Ajax请求加载更多。
使用Selenium处理这种情况,核心是模拟滚动和等待。我们不能一次性滚动到底,那样可能只触发最后一次加载,导致漏掉中间批次的数据。正确的做法是循环执行“滚动一段距离 -> 等待新内容加载”的操作。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 假设driver是已登录的Selenium WebDriver对象 comment_items = [] last_height = driver.execute_script(“return document.body.scrollHeight”) while True: # 滚动到页面底部 driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) # 等待新评论加载出现,这里以评论容器的特定CSS类为例 time.sleep(2) # 固定等待,简单但可能不高效 # 更优方案:使用显式等待,等待特定元素出现 # try: # WebDriverWait(driver, 5).until( # EC.presence_of_element_located((By.CSS_SELECTOR, “.new-comment-item”)) # ) # except: # break # 如果没有新元素,说明已加载完毕 # 解析当前页面中的所有评论项 current_items = driver.find_elements(By.CSS_SELECTOR, “[node-type=’comment_list’] .list_li”) if len(current_items) > len(comment_items): comment_items = current_items else: # 如果连续几次滚动都没有新内容,则认为加载完毕 break # 计算新的页面高度,用于判断是否到底 new_height = driver.execute_script(“return document.body.scrollHeight”) if new_height == last_height: break last_height = new_height这里有一个非常重要的细节:微博评论的HTML结构可能会更新,node-type或class名称会变。因此,在编写选择器前,必须使用浏览器的开发者工具(F12)对目标微博页面进行实时审查,找到当前评论列表项最稳定、最独特的标识属性。通常,寻找具有明确功能含义的node-type属性比依赖易变的CSS类名更可靠。
3.3 评论文本清洗的特定规则
从HTML中提取出的原始评论文本,夹杂着大量对情感分析无益甚至有害的“噪音”。清洗步骤至关重要,直接影响到后续分析的准确性。
清洗流程应遵循以下顺序:
- 移除HTML标签与特殊字符:使用
BeautifulSoup.get_text()或正则表达式去除残留的<a>,<span>等标签。 - 处理微博特有内容:
- 表情符号:如
[笑cry]、[doge]。可以选择直接删除(因为它们难以被通用NLP模型理解),或者尝试将其映射为情感词(如[笑cry]映射为“笑哭”,可能带有正面或复杂情感)。简单处理建议直接删除。 - 话题标签:如
#今日心情#。删除“#”符号,但可以保留中间的文字(“今日心情”),这部分文字可能包含情感信息。 - @用户:如
@张三。直接删除,因为这只是指向性信息,与情感无关。 - 网页链接:统一替换为“[链接]”标记或直接删除。
- 表情符号:如
- 规范化文本:包括去除多余的空格、换行符,将全角字符转换为半角(视情况而定)等。
import re def clean_weibo_text(raw_text): if not raw_text: return “” # 1. 移除@用户 text = re.sub(r’@[\w\u4e00-\u9fa5_-]+’, ‘’, raw_text) # 2. 移除话题标签的#号,保留内容 text = re.sub(r’#([^#]+)#’, r’\1’, text) # 3. 移除表情符号 [xxx] text = re.sub(r’\[.*?\]’, ‘’, text) # 4. 移除网页链接 text = re.sub(r’https?://\S+’, ‘[链接]’, text) # 5. 移除多余空白字符 text = re.sub(r’\s+’, ‘ ‘, text).strip() return text实操心得:清洗规则不是一成不变的。对于不同的分析目标,策略可以调整。例如,如果分析重点是“表情包文化”,那么保留并专门处理表情符号反而成了关键。因此,在编写清洗函数时,最好将其设计为可配置的,方便后续调整。
4. 实操过程与核心环节实现
4.1 环境准备与依赖安装
工欲善其事,必先利其器。首先需要搭建Python环境,并安装必要的库。建议使用conda或venv创建独立的虚拟环境。
# 创建并激活虚拟环境 (以conda为例) conda create -n weibo-sentiment python=3.9 conda activate weibo-sentiment # 安装核心依赖 pip install selenium beautifulsoup4 pandas numpy # 安装情感分析库(SnowNLP作为示例,Transformers用于进阶) pip install snownlp # 如需使用BERT,安装transformers和torch # pip install transformers torch # 安装可视化库 pip install matplotlib seaborn jieba wordcloud除了Python库,还需要下载与你的Chrome浏览器版本匹配的ChromeDriver。将其放在系统PATH路径下,或直接在代码中指定可执行文件路径。
4.2 爬虫核心代码实现与封装
我们将爬虫功能封装成一个类WeiboCommentCrawler,提高代码的可重用性和可维护性。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options import time import json class WeiboCommentCrawler: def __init__(self, headless=False): “”” 初始化爬虫,配置浏览器选项。 :param headless: 是否使用无头模式(不显示浏览器界面) “”” chrome_options = Options() if headless: chrome_options.add_argument(‘–headless’) # 添加反检测参数 chrome_options.add_argument(‘–disable-blink-features=AutomationControlled’) chrome_options.add_experimental_option(“excludeSwitches”, [“enable-automation”]) chrome_options.add_experimental_option(‘useAutomationExtension’, False) self.driver = webdriver.Chrome(options=chrome_options) # 执行CDP命令,隐藏WebDriver特征 self.driver.execute_cdp_cmd(‘Page.addScriptToEvaluateOnNewDocument’, { ‘source’: ‘'’ Object.defineProperty(navigator, ‘webdriver’, { get: () => undefined }); ‘'’ }) self.wait = WebDriverWait(self.driver, 10) def login_by_scan(self): “””通过扫码登录微博。“”” login_url = ‘https://weibo.com/login.php’ self.driver.get(login_url) print(“请使用手机微博APP扫描页面二维码进行登录…”) # 等待URL变化,表明登录成功(通常跳转到weibo.com首页) self.wait.until(EC.url_contains(‘weibo.com’)) print(“登录成功!”) # 保存cookies以备后用 self.cookies = {cookie[‘name’]: cookie[‘value’] for cookie in self.driver.get_cookies()} with open(‘weibo_cookies.json’, ‘w’) as f: json.dump(self.cookies, f) return True def get_comments_by_url(self, weibo_url, max_scroll=20): “”” 根据微博URL爬取评论。 :param weibo_url: 目标微博的完整URL :param max_scroll: 最大滚动次数,防止无限循环 :return: 评论数据列表 “”” self.driver.get(weibo_url) time.sleep(3) # 等待页面初步加载 comments_data = [] scroll_count = 0 while scroll_count < max_scroll: # 1. 滚动 self.driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) time.sleep(2) # 等待加载,可根据网络情况调整 # 2. 尝试查找并解析评论项 # 注意:此选择器需要根据微博页面实际结构更新! try: comment_elements = self.driver.find_elements(By.CSS_SELECTOR, “[node-type=’comment_list’] .list_li .WB_text”) except: comment_elements = [] for elem in comment_elements: try: comment_text = elem.text.strip() if comment_text and comment_text not in [c[‘text’] for c in comments_data]: # 简单去重 # 这里可以扩展提取用户、时间、点赞数等信息 # 例如,用户信息可能在兄弟节点或父节点中 comments_data.append({ ‘text’: comment_text, # ‘user’: user_name, # ‘time’: post_time, # ‘likes’: like_count }) except Exception as e: print(f”解析评论时出错: {e}”) print(f”已滚动 {scroll_count+1} 次,累计发现 {len(comments_data)} 条评论。”) # 3. 判断是否已加载到底(通过检查页面高度是否变化) new_height = self.driver.execute_script(“return document.body.scrollHeight”) if new_height == self.last_height: # 连续两次高度未变,可能已到底,再尝试滚动一次确认 time.sleep(3) self.driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) time.sleep(3) if new_height == self.driver.execute_script(“return document.body.scrollHeight”): print(“评论已全部加载完毕。”) break self.last_height = new_height scroll_count += 1 return comments_data def close(self): “””关闭浏览器。“”” self.driver.quit() # 使用示例 if __name__ == ‘__main__’: crawler = WeiboCommentCrawler(headless=False) # 调试时建议设为False try: # 如果已有cookies文件,可以尝试加载,避免每次扫码 # crawler.load_cookies(‘weibo_cookies.json’) # else: crawler.login_by_scan() target_url = ‘https://weibo.com/1234567890/xxxxxx’ # 替换为目标微博真实URL comments = crawler.get_comments_by_url(target_url, max_scroll=15) print(f”共爬取到 {len(comments)} 条评论。”) # 保存原始数据 import pandas as pd df = pd.DataFrame(comments) df.to_csv(‘raw_comments.csv’, index=False, encoding=‘utf-8-sig’) finally: crawler.close()关键点说明:
- 反检测配置:
ChromeOptions中的一系列设置和CDP命令,是降低被屏蔽概率的关键。 - 等待策略:代码中使用了固定的
time.sleep,这在实际应用中不够健壮。强烈建议将其替换为WebDriverWait配合expected_conditions的显式等待,例如等待“加载更多”按钮出现或特定数量的新评论元素被加载。这里为了代码简洁使用了sleep,但这是需要优化的地方。 - 数据去重:在滚动加载过程中,同一条评论可能被多次解析。代码中使用了简单的基于文本内容的列表检查去重,对于大规模爬取,可能需要更高效的方法(如使用
set存储评论ID)。
4.3 基于SnowNLP与BERT的情感分析实现
数据爬取并清洗后,我们进入情感分析阶段。
方案一:快速实现 - 使用SnowNLP
SnowNLP上手极其简单,但需要了解其输出含义:sentiments属性返回一个0到1之间的浮点数,越接近1表示越正面,越接近0表示越负面。通常,我们可以设定一个阈值(如0.6和0.4)将其划分为正面、中性、负面三类。
from snownlp import SnowNLP import pandas as pd def analyze_sentiment_snownlp(text): “””使用SnowNLP进行情感分析。“”” if not text or pd.isna(text): return None, None try: s = SnowNLP(text) sentiment_score = s.sentiments # 情感极性得分,0-1 # 简单分类 if sentiment_score > 0.6: sentiment_label = ‘正面’ elif sentiment_score < 0.4: sentiment_label = ‘负面’ else: sentiment_label = ‘中性’ return sentiment_score, sentiment_label except Exception as e: print(f”分析文本‘{text[:50]}…’时出错: {e}”) return None, None # 读取清洗后的数据 df = pd.read_csv(‘cleaned_comments.csv’) # 应用情感分析 df[[‘sentiment_score’, ‘sentiment_label’]] = df[‘cleaned_text’].apply( lambda x: pd.Series(analyze_sentiment_snownlp(x)) ) # 查看分布 print(df[‘sentiment_label’].value_counts())方案二:进阶实现 - 微调BERT模型
SnowNLP的模型可能无法很好地理解当下的网络用语。为了获得更精准的分析,我们可以使用Transformers库,基于预训练的中文BERT模型进行微调。这需要准备一个带有情感标签(正面/负面)的训练数据集。
# 这是一个简化的示例,展示流程。实际微调需要准备训练数据、划分数据集、编写训练循环等。 from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments import torch from datasets import Dataset import pandas as pd # 1. 准备数据 (假设我们有标注好的训练数据 train_df) # train_df 应包含 ‘text’ 和 ‘label’ 两列,label为0(负面)或1(正面) tokenizer = BertTokenizer.from_pretrained(‘bert-base-chinese’) def tokenize_function(examples): return tokenizer(examples[‘text’], padding=“max_length”, truncation=True, max_length=128) # 将DataFrame转换为Hugging Face Dataset格式 dataset = Dataset.from_pandas(train_df[[‘text’, ‘label’]]) tokenized_datasets = dataset.map(tokenize_function, batched=True) # 分割训练集和验证集 split_datasets = tokenized_datasets.train_test_split(test_size=0.1) train_dataset = split_datasets[‘train’] eval_dataset = split_datasets[‘test’] # 2. 加载模型 model = BertForSequenceClassification.from_pretrained(‘bert-base-chinese’, num_labels=2) # 3. 设置训练参数 training_args = TrainingArguments( output_dir=‘./sentiment_results’, evaluation_strategy=“epoch”, learning_rate=2e-5, per_device_train_batch_size=16, per_device_eval_batch_size=16, num_train_epochs=3, weight_decay=0.01, ) # 4. 创建Trainer并训练 trainer = Trainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=eval_dataset, ) trainer.train() # 5. 使用微调后的模型进行预测 def predict_sentiment_bert(text, model, tokenizer): inputs = tokenizer(text, return_tensors=“pt”, padding=True, truncation=True, max_length=128) with torch.no_grad(): outputs = model(**inputs) predictions = torch.nn.functional.softmax(outputs.logits, dim=-1) predicted_class = torch.argmax(predictions, dim=1).item() # 假设0:负面,1:正面 sentiment_label = ‘正面’ if predicted_class == 1 else ‘负面’ confidence = predictions[0][predicted_class].item() return sentiment_label, confidence # 对单条评论进行预测 label, confidence = predict_sentiment_bert(“这个产品真的太棒了,完全超出预期!”, model, tokenizer) print(f”情感: {label}, 置信度: {confidence:.2f}”)实操心得:对于大多数舆情监控场景,如果评论数据量巨大且对实时性要求高,建议采用“SnowNLP初筛 + 关键评论BERT复核”的策略。即先用
SnowNLP快速处理所有评论,对于得分在临界值附近(如0.4-0.6之间)或包含特定关键词(如“绝了”、“无语”)的评论,再用更精准的BERT模型进行二次判断。这样能在保证整体分析效率的同时,提升关键模糊语句的判断准确率。
4.4 数据可视化与洞察呈现
分析结果只有通过直观的可视化,才能转化为有价值的洞察。这里介绍两种最常用的图表。
1. 情感分布饼图
import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体 plt.rcParams[‘font.sans-serif’] = [‘SimHei’, ‘Arial Unicode MS’, ‘DejaVu Sans’] plt.rcParams[‘axes.unicode_minus’] = False # 计算情感标签分布 sentiment_counts = df[‘sentiment_label’].value_counts() plt.figure(figsize=(8, 8)) plt.pie(sentiment_counts.values, labels=sentiment_counts.index, autopct=‘%1.1f%%’, startangle=90, colors=[‘lightcoral’, ‘lightblue’, ‘lightgreen’]) plt.title(‘微博评论情感分布’) plt.show()2. 正面/负面评论词云词云能直观展示不同情感倾向评论中的高频词汇。
from wordcloud import WordCloud from collections import Counter import jieba def generate_wordcloud(texts, title): “””生成词云图。“”” # 将所有评论文本连接并分词 all_words = ‘ ‘.join(texts) word_list = jieba.lcut(all_words) # 过滤停用词和单字 with open(‘stopwords.txt’, ‘r’, encoding=‘utf-8’) as f: # 准备一个中文停用词表 stopwords = set([line.strip() for line in f]) filtered_words = [word for word in word_list if len(word) > 1 and word not in stopwords] word_freq = Counter(filtered_words) wc = WordCloud( font_path=‘msyh.ttc’, # 指定中文字体路径 width=800, height=600, background_color=‘white’, max_words=200 ).generate_from_frequencies(word_freq) plt.figure(figsize=(10, 8)) plt.imshow(wc, interpolation=‘bilinear’) plt.axis(‘off’) plt.title(title) plt.show() # 分别生成正面和负面词云 positive_texts = df[df[‘sentiment_label’]==‘正面’][‘cleaned_text’].tolist() negative_texts = df[df[‘sentiment_label’]==‘负面’][‘cleaned_text’].tolist() generate_wordcloud(positive_texts, ‘正面评论高频词’) generate_wordcloud(negative_texts, ‘负面评论高频词’)通过饼图,我们可以快速了解整体舆论的正面、中性、负面比例。通过对比正面和负面词云,我们能直观看到用户主要在夸什么(如“好用”、“流畅”、“颜值高”)或在吐槽什么(如“卡顿”、“发热”、“售后差”),这比单纯的分数更有业务指导意义。
5. 常见问题与排查技巧实录
在实际操作中,你几乎一定会遇到下面这些问题。这里我把踩过的坑和解决方案整理出来,希望能帮你节省大量时间。
5.1 爬虫被屏蔽或跳转到验证码页面
这是微博爬虫最常见的“拦路虎”。
- 现象:
Selenium打开的页面显示“账号存在异常”或直接弹出滑动验证码。 - 排查与解决:
- 检查浏览器指纹:确保已按照前面代码示例,添加了所有反检测的
ChromeOptions配置,特别是excludeSwitches和CDP命令。可以访问一些检测网站(如pixelscan.net)测试你的Selenium浏览器是否被识别。 - 降低操作频率:在
scroll和click操作之间增加随机的、更长的等待时间(time.sleep(random.uniform(2, 5))),模拟人类的不规律操作。避免使用固定间隔。 - 使用高质量代理IP:如果同一个IP地址在短时间内发出过多请求,极易被封锁。考虑使用付费的住宅代理IP池,并在
Selenium或requests中配置代理。 - 账号行为管理:不要用新注册或低活跃度的账号进行爬取。使用一个日常正常使用的“老号”,并在爬取前后进行一些浏览、点赞等正常操作。避免长时间、高强度的连续爬取,给账号“休息”时间。
- 验证码处理:如果弹出验证码,对于简单项目,最实用的方法是手动干预。可以在代码中检测到验证码元素出现时,暂停程序(
input(“请手动完成验证码后按回车继续…”)),手动完成验证后再继续。对于复杂项目,可以考虑接入打码平台API。
- 检查浏览器指纹:确保已按照前面代码示例,添加了所有反检测的
5.2 无法定位评论元素或爬取数据为空
- 现象:程序运行不报错,但
comment_elements列表始终为空。 - 排查与解决:
- 确认页面加载完成:在查找元素前,增加等待时间,或使用显式等待确保评论区域已加载。微博页面结构复杂,加载较慢。
- 更新元素选择器:微博前端代码经常更新,
node-type或class名可能已改变。必须使用浏览器开发者工具(F12)重新检查目标微博页面的HTML结构。尝试使用更通用的选择器,如通过find_elements(By.XPATH, “//div[@class=‘WB_text’ and @nick-name]”),或者寻找更稳定的父容器。 - 检查是否登录成功:确保
cookies有效,访问的页面是登录后的状态。可以尝试先打印当前页面的标题或某个已知的用户名元素,确认登录态。 - 处理iframe:极少数情况下,评论可能嵌套在
iframe中。如果是这样,需要使用driver.switch_to.frame(frame_element)切换到对应的iframe后才能定位元素。
5.3 SnowNLP情感分析结果不准确或倾向单一
- 现象:所有评论的情感分数都集中在0.5左右,或者明显与人工判断不符。
- 排查与解决:
- 数据清洗是否到位:检查清洗函数是否有效去除了表情、@用户、链接等无关内容。残留的“[哈哈]”可能会被错误解读。
- 理解SnowNLP的局限性:
SnowNLP的训练语料可能未充分覆盖最新的网络流行语和特定领域(如数码、美妆)的黑话。对于“yyds”(永远的神,正面)、“蚌埠住了”(崩不住了,负面)这类词,它可能无法理解。 - 调整分类阈值:默认的0.6和0.4阈值可能不适合你的数据集。可以尝试绘制情感得分的分布直方图,根据分布情况调整阈值。或者,对于中性区间(如0.45-0.55)的评论,进行人工抽样检查,决定是归入正面/负面还是单独作为“中性”。
- 考虑使用或微调更先进的模型:如前所述,对于质量要求高的分析,投资时间微调一个BERT模型是值得的。即使不微调,直接使用在中文情感分析任务上表现好的预训练模型(如
skep_ernie_1.0_l-12_h-768_a-12)进行零样本或少样本预测,效果也可能优于SnowNLP。
5.4 爬取速度过慢
- 现象:爬取几百条评论就需要很长时间。
- 优化方案:
- 混合请求模式:用
Selenium登录并加载出前几页评论后,通过浏览器开发者工具的“网络(Network)”选项卡,观察加载更多评论时触发的Ajax请求(通常是XHR类型)。尝试分析这个请求的URL、参数和Headers(特别是cookies和authorization等令牌)。如果可能,将这个请求复制到requests或aiohttp中直接调用,速度会快几个数量级。这是爬虫进阶的必备技能。 - 并行化处理:如果需要爬取多个不相关的微博,可以使用多线程或异步IO(
asyncio+aiohttp)并发处理。注意:对同一微博并发请求极易被封,此方法仅适用于不同目标。 - 优化等待策略:将固定的
time.sleep替换为针对特定元素出现的显式等待(WebDriverWait),可以避免无谓的等待,提升效率。
- 混合请求模式:用
5.5 数据存储与增量爬取
- 需求:如何避免每次重复爬取相同微博的旧评论?
- 解决方案:设计一个简单的增量爬取逻辑。
- 唯一标识:为每条评论生成一个唯一ID。微博评论通常有
comment_id,可以从HTML元素的id或data属性中提取。如果无法获取,可以用“用户ID+时间戳”的哈希值作为替代。 - 建立去重表:将已爬取的评论ID存储起来(如存入SQLite数据库或一个文本文件)。
- 爬取时检查:在解析评论时,先检查其ID是否已存在于去重表中,如果存在则跳过。
- 定时任务:结合
crontab(Linux)或schedule库(Python),定时运行爬虫脚本,只爬取新出现的评论。同时,可以记录每次爬取的时间点,方便后续按时间范围查询和分析情感趋势。
- 唯一标识:为每条评论生成一个唯一ID。微博评论通常有
这个项目从爬虫到分析,涉及了从数据获取、处理到建模、可视化的完整数据科学流程。最深的体会是,爬虫的难点十之八九在于对抗反爬策略和解析动态页面,而情感分析的效果则严重依赖于数据质量和模型的选择。在实际应用中,没有一劳永逸的代码,需要根据目标网站的变化和具体的分析需求,不断地调整和优化你的策略。比如,微博的接口可能某天就变了,你的选择器就要跟着变;网络新梗出来了,你的情感词典或模型就需要更新。保持代码的模块化和可配置性,能让这些维护工作变得轻松一些。最后,务必遵守网站的robots.txt协议,合理控制爬取频率,尊重数据版权和用户隐私,这是技术人的基本操守。
本文还有配套的精品资源,点击获取