当前位置: 首页 > news >正文

Selenium爬取微博热搜完整实战:从环境搭建到反爬绕过的全流程踩坑指南


做爬虫这么多年,最头疼的就是动态网页。尤其是微博这种大厂的产品,反爬手段层出不穷,能把人逼疯。

前几天有个粉丝找我,说他用requests爬微博热搜,爬了半天只拿到一堆空标签。问我怎么回事。

我一看就笑了。现在的微博,早就不是当年那个静态页面的时代了。所有数据都是通过AJAX动态加载的,你直接请求HTML,能拿到数据才怪。

而且微博的反爬做得特别严,请求头稍微不对,Cookie过期,或者请求频率高一点,直接就给你弹验证码,甚至封IP。

很多人遇到这种情况,第一反应就是去抓包分析API。但微博的API参数都是加密的,而且经常变,你花了好几天破解出来,可能过一周就失效了。

这时候,Selenium就是你的救命稻草。

虽然Selenium速度慢,资源消耗大,但它有一个无可替代的优势:它是真正的浏览器。它能像真人一样渲染页面,执行JavaScript,处理各种动态内容。

只要你能在浏览器里看到的数据,Selenium就能拿到。

今天我就以微博热搜为例,给大家分享一个完整的Selenium爬虫实战。从环境搭建到反爬绕过,再到数据存储,一步一步带你写一个能稳定运行的爬虫。

为什么requests搞不定微博热搜?

在开始写代码之前,我们先搞清楚一个问题:为什么requests爬不到微博热搜的数据?

打开微博热搜页面,按F12查看网页源代码。你会发现,热搜列表的位置,只有一个空的div标签,里面什么内容都没有。

这是因为微博采用了前后端分离的架构。页面加载的时候,只加载一个空的HTML骨架,然后通过JavaScript向服务器发送AJAX请求,获取数据后再动态渲染到页面上。

requests只能获取到最初的HTML文档,不会执行里面的JavaScript代码,所以自然拿不到动态加载的数据。

而且微博的反爬措施非常完善:

  • 请求头必须包含正确的User-Agent、Referer、Cookie等信息
  • API参数有复杂的加密算法,且定期更新
  • 对请求频率有严格的限制,短时间内大量请求会被封IP
  • 会检测浏览器指纹,识别自动化工具
  • 遇到异常请求会弹出验证码

如果你非要用requests去硬刚,那你需要花大量的时间去破解加密参数,维护Cookie池和代理池,而且随时可能失效。

而Selenium直接模拟真实浏览器的行为,完美避开了这些问题。

环境搭建:一步到位,告别驱动烦恼

很多人对Selenium望而却步,就是因为浏览器驱动的配置太麻烦了。

以前用Selenium,你需要手动下载对应浏览器版本的驱动,然后配置环境变量,或者在代码里指定驱动路径。只要浏览器一更新,驱动就失效了,非常烦人。

但从Selenium 4.6版本开始,这一切都成为了历史。

Selenium 4.6内置了Selenium Manager,会自动检测你电脑上安装的浏览器版本,然后自动下载对应的驱动。你什么都不用管,直接写代码就行。

这绝对是Selenium近年来最大的改进,没有之一。

环境搭建只需要两步:

  1. 安装Selenium库
pipinstallselenium==4.21.0
  1. 安装Chrome浏览器(Edge也可以,代码几乎一样)

就这么简单。不需要下载任何驱动,不需要配置任何环境变量。

完整实战:手把手教你爬取微博热搜

这是我写的一个完整的微博热搜爬虫,经过多次优化,能稳定运行,基本不会被检测到。

先给大家看一下整体的流程图:

初始化浏览器

配置反爬参数

访问微博热搜页面

显式等待页面加载完成

定位热搜列表元素

遍历提取每条热搜数据

数据清洗与格式化

保存数据到CSV文件

关闭浏览器

异常处理

第一步:初始化浏览器并配置反爬参数

这是最关键的一步。如果你的反爬参数配置得不好,一打开页面就会被检测到是自动化工具。

fromseleniumimportwebdriverfromselenium.webdriver.chrome.optionsimportOptionsfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECimporttimeimportrandomimportcsvdefinit_browser():chrome_options=Options()# 基础反爬设置chrome_options.add_argument("--start-maximized")# 最大化窗口chrome_options.add_argument("--disable-blink-features=AutomationControlled")# 禁用自动化控制特征chrome_options.add_experimental_option("excludeSwitches",["enable-automation"])# 排除自动化开关chrome_options.add_experimental_option("useAutomationExtension",False)# 禁用自动化扩展# 隐藏浏览器指纹chrome_options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")chrome_options.add_argument("--disable-extensions")# 禁用扩展chrome_options.add_argument("--disable-plugins-discovery")# 禁用插件发现chrome_options.add_argument("--disable-notifications")# 禁用通知# 无头模式(可选,后台运行)# chrome_options.add_argument("--headless=new")# 初始化浏览器driver=webdriver.Chrome(options=chrome_options)# 执行JavaScript,覆盖window.navigator.webdriver属性driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{"source":""" Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) """})returndriver

这些参数都是我踩了无数坑总结出来的。少了任何一个,都可能被微博检测到。

特别是disable-blink-features=AutomationControlled和覆盖navigator.webdriver这两个设置,是绕过Selenium检测的核心。

第二步:访问页面并等待元素加载

很多人写Selenium代码喜欢用time.sleep()来等待页面加载。这是一个非常不好的习惯。

time.sleep()会让程序固定等待一段时间,不管页面有没有加载完成。如果设置的时间太短,元素还没加载出来,就会报错;如果设置的时间太长,又会浪费时间。

正确的做法是使用显式等待。显式等待会一直等待,直到指定的元素加载出来,或者超时。

defcrawl_weibo_hot():driver=init_browser()hot_list=[]try:# 访问微博热搜页面driver.get("https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=")# 显式等待热搜列表加载完成,最多等待10秒wait=WebDriverWait(driver,10)hot_items=wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,"div[class*='HotTopic_topic_']")))print(f"成功获取到{len(hot_items)}条热搜")# 随机等待1-2秒,模拟人类行为time.sleep(random.uniform(1,2))# 遍历每条热搜,提取数据forindex,iteminenumerate(hot_items,1):try:# 提取热搜标题title=item.find_element(By.CSS_SELECTOR,"h3[class*='HotTopic_title_']").text.strip()# 提取热搜热度heat=item.find_element(By.CSS_SELECTOR,"span[class*='HotTopic_num_']").text.strip()# 提取热搜标签(置顶、爆、新、热等)tag=""try:tag_element=item.find_element(By.CSS_SELECTOR,"span[class*='HotTopic_tag_']")tag=tag_element.text.strip()except:passhot_data={"排名":index,"标题":title,"热度":heat,"标签":tag,"爬取时间":time.strftime("%Y-%m-%d %H:%M:%S")}hot_list.append(hot_data)print(f"第{index}条:{title}[{heat}]{tag}")# 每条数据之间随机延迟0.5-1秒time.sleep(random.uniform(0.5,1))exceptExceptionase:print(f"提取第{index}条热搜失败:{e}")continueexceptExceptionase:print(f"爬取失败:{e}")finally:# 无论成功还是失败,都要关闭浏览器driver.quit()returnhot_list

这里我用了CSS选择器来定位元素。相比XPath,CSS选择器更简洁,执行速度更快,而且不容易因为页面结构的微小变化而失效。

第三步:保存数据到CSV文件

爬取到数据后,我们把它保存到CSV文件里,方便后续分析。

defsave_to_csv(data,filename="weibo_hot.csv"):ifnotdata:print("没有数据可保存")return# 获取所有字段名fieldnames=data[0].keys()withopen(filename,"w",newline="",encoding="utf-8-sig")asf:writer=csv.DictWriter(f,fieldnames=fieldnames)writer.writeheader()writer.writerows(data)print(f"数据已保存到{filename}")if__name__=="__main__":print("开始爬取微博热搜...")hot_data=crawl_weibo_hot()save_to_csv(hot_data)print("爬取完成")

注意这里我用了utf-8-sig编码,而不是utf-8。这样用Excel打开CSV文件的时候,中文就不会乱码了。

核心反爬技巧:让Selenium看起来更像真人

很多人用Selenium爬取微博,跑不了几次就被封了。这不是Selenium的问题,而是你没有做好反爬措施。

我总结了几个核心的反爬技巧,能让你的Selenium爬虫被检测的概率降低90%以上。

1. 随机化请求间隔

这是最基本也是最重要的一点。

真人浏览网页的时候,操作间隔是随机的,而不是固定的。如果你写的爬虫每隔0.1秒就点击一次,傻子都能看出来是机器人。

我一般会在每个操作之间加入随机延迟:

  • 页面加载后等待1-3秒
  • 每条数据提取之间等待0.5-1秒
  • 翻页后等待2-5秒

2. 模拟人类滚动行为

很多网站会检测页面的滚动行为。如果你的页面一打开就直接定位到最底部,那肯定会被怀疑。

我们可以用JavaScript模拟人类的滚动行为:

defscroll_down(driver,scroll_times=3):for_inrange(scroll_times):# 随机滚动距离scroll_height=random.randint(300,800)driver.execute_script(f"window.scrollBy(0,{scroll_height})")time.sleep(random.uniform(0.5,1.5))

3. 定期更换User-Agent

不要一直用同一个User-Agent。你可以准备一个User-Agent池,每次启动浏览器的时候随机选一个。

USER_AGENTS=["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/125.0.0.0 Safari/537.36"]# 在初始化浏览器的时候随机选择chrome_options.add_argument(f"--user-agent={random.choice(USER_AGENTS)}")

4. 避免重复的操作模式

真人的操作模式是多样化的。不要每次都按照完全相同的顺序执行相同的操作。

比如,你可以随机点击一些无关的链接,或者在页面上停留随机的时间。

5. 使用代理池

如果需要频繁爬取,一定要使用代理池。不然你的IP很容易被封。

Selenium配置代理也很简单:

chrome_options.add_argument(f"--proxy-server=http://{proxy_ip}:{proxy_port}")

常见问题与解决方案

我把大家在实际运行代码的时候最容易遇到的问题整理了一下,每个问题都给出了具体的解决方案。

问题1:元素定位失败,提示NoSuchElementException

这是最常见的问题。原因主要有三个:

  1. 页面还没加载完成就开始查找元素
  2. 元素定位器写错了
  3. 微博更新了页面结构

解决方案:

  • 一定要使用显式等待,不要用time.sleep()
  • 尽量使用相对定位,不要使用绝对路径
  • 如果页面结构更新了,重新在F12里复制元素的选择器

问题2:被检测到是自动化工具,页面空白或者弹验证码

这是因为你的反爬参数配置得不够好。

解决方案:

  • 确保添加了所有我上面提到的反爬参数
  • 升级Selenium到最新版本
  • 尝试使用Edge浏览器代替Chrome
  • 增加随机延迟,降低爬取频率

问题3:爬取的数据不完整,只有前几条

这是因为微博热搜页面是懒加载的。当你滚动到页面底部的时候,才会加载更多的内容。

解决方案:

  • 在提取数据之前,先模拟滚动页面到底部
  • 等待新的内容加载完成后再提取

问题4:浏览器闪退,没有任何报错

这通常是因为浏览器驱动和浏览器版本不兼容。

解决方案:

  • 升级Selenium到4.6以上版本,让Selenium Manager自动管理驱动
  • 如果还是不行,手动下载对应版本的驱动

进阶优化:让你的爬虫更强大

上面的代码已经可以满足基本的爬取需求了。如果你想让你的爬虫更强大,可以做以下几个优化。

1. 实现定时爬取

我们可以用schedule库实现定时爬取,比如每小时爬取一次微博热搜。

importscheduledefjob():print(f"开始定时爬取,当前时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")hot_data=crawl_weibo_hot()save_to_csv(hot_data,f"weibo_hot_{time.strftime('%Y%m%d_%H%M%S')}.csv")# 每小时执行一次schedule.every().hour.do(job)whileTrue:schedule.run_pending()time.sleep(1)

2. 存储数据到数据库

如果需要长期存储大量数据,建议使用MySQL或者MongoDB。

3. 实现分布式爬取

如果需要爬取大量数据,可以结合Redis和多线程,实现分布式爬取。

4. 集成验证码识别

如果遇到验证码,可以集成第三方验证码识别服务,比如打码平台。

写在最后

Selenium不是万能的,但对于动态网页来说,它绝对是最简单最有效的解决方案。

很多人看不起Selenium,觉得它速度慢,性能差。但在实际工作中,稳定性和可维护性远比速度重要。

你花了一个星期破解了微博的API,结果过了一周就失效了。而用Selenium写的爬虫,只要页面结构没有大的变化,就能一直运行。

当然,Selenium也有它的局限性。它不适合爬取海量数据,也不适合对速度要求很高的场景。

在实际项目中,我们应该根据具体情况选择合适的工具。对于简单的静态页面,用requests就够了;对于复杂的动态页面,用Selenium;对于大规模爬取,用Scrapy+分布式架构。

最后提醒大家一句,爬虫只是一种技术手段,一定要遵守法律法规,尊重网站的robots协议,不要用于非法用途。

http://www.jsqmd.com/news/886802/

相关文章:

  • AutoDock-Vina终极指南:5步掌握免费分子对接神器
  • 研0导师不教你 但你要会的组会汇报
  • claude code的替代
  • 别再手动拼Prompt了!LangChain4j的ChatMemory和AiServices才是Java聊天机器人的正确打开方式
  • DeepSeek代码风格检查实战手册,从零配置到生产级规则定制全流程
  • 告别async/await测试焦虑:用pytest-asyncio插件搞定Python异步代码测试(附完整示例)
  • DIY高精度GPS驯服钟:用OCXO与单片机打造实验室级频率基准
  • DeepSeek边缘安全沙箱深度拆解(含SEV-SNP启用失败根因分析与SGX2迁移路径)
  • DeepSeek v3升级迫在眉睫?立即启用这套已验证的灰度集成测试方案——支撑日均200万请求的稳定性护城河
  • Qt项目里图片加载太慢?试试用QOpenGLWidget+GPU加速,性能提升不止一点点
  • 抖音下载器终极指南:如何快速批量下载无水印视频
  • 0.2毫秒快速启动的操作系统
  • 大麦网智能抢票神器:Python自动化解决方案深度解析
  • 全球2026年GEO优化公司TOP榜单!最新最全榜单带你找到综合实力最强的GEO服务商 - 互联网科技品牌测评
  • Arduino I2C温度传感器读取避坑指南:二进制补码处理与LCD1602显示
  • 重构决策不再拍脑袋,DeepSeek模式推荐引擎如何用17维特征评分帮你秒级锁定最优路径,
  • 对象存储迁移-组件上线
  • CANoe自动化测试新思路:像搭积木一样用XML管理你的CAPL用例(Test Module实战)
  • 内存占用3KB!极致瘦身释放MCU无限可能
  • 【Elasticsearch从入门到精通】第40篇:Elasticsearch SQL语法详解——从DDL到复杂查询
  • 强化学习优化代码生成:环境插桩与自改进策略实践
  • 基于Arduino的智能蓝调节拍器:DIY音乐练习伴侣
  • 2026年5月天津国际高中推荐:五家专业评测择校案例性价比高 - 品牌推荐
  • 紧急预警:DeepSeek-v3商用许可协议重大更新!5月31日前未完成IP尽调的企业将丧失合规豁免权
  • 基于ESP32-Pico的智能蓝牙网关:改造传统暖气阀实现远程温控
  • 2026年LLM推理加速全景:量化、投机解码与KV Cache工程实战
  • 5分钟实现音乐自由:Mac端QQ音乐加密格式转换终极指南
  • 苏州拍婚纱照去哪些园林?本地人的场地选择建议 - eee888
  • Sangfor文件夹可以删除吗?【图文讲解】深信服文件夹残留清理?如何彻底删除深信服?Sangfor文件夹是什么?
  • PlayAI实时翻译落地全图谱(金融/医疗/制造三大硬核场景深度拆解)