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近年来最大的改进没有之一。环境搭建只需要两步安装Selenium库pipinstallselenium4.21.0安装Chrome浏览器Edge也可以代码几乎一样就这么简单。不需要下载任何驱动不需要配置任何环境变量。完整实战手把手教你爬取微博热搜这是我写的一个完整的微博热搜爬虫经过多次优化能稳定运行基本不会被检测到。先给大家看一下整体的流程图初始化浏览器配置反爬参数访问微博热搜页面显式等待页面加载完成定位热搜列表元素遍历提取每条热搜数据数据清洗与格式化保存数据到CSV文件关闭浏览器异常处理第一步初始化浏览器并配置反爬参数这是最关键的一步。如果你的反爬参数配置得不好一打开页面就会被检测到是自动化工具。fromseleniumimportwebdriverfromselenium.webdriver.chrome.optionsimportOptionsfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimportWebDriverWaitfromselenium.webdriver.supportimportexpected_conditionsasECimporttimeimportrandomimportcsvdefinit_browser():chrome_optionsOptions()# 基础反爬设置chrome_options.add_argument(--start-maximized)# 最大化窗口chrome_options.add_argument(--disable-blink-featuresAutomationControlled)# 禁用自动化控制特征chrome_options.add_experimental_option(excludeSwitches,[enable-automation])# 排除自动化开关chrome_options.add_experimental_option(useAutomationExtension,False)# 禁用自动化扩展# 隐藏浏览器指纹chrome_options.add_argument(--user-agentMozilla/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(--headlessnew)# 初始化浏览器driverwebdriver.Chrome(optionschrome_options)# 执行JavaScript覆盖window.navigator.webdriver属性driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument,{source: Object.defineProperty(navigator, webdriver, { get: () undefined }) })returndriver这些参数都是我踩了无数坑总结出来的。少了任何一个都可能被微博检测到。特别是disable-blink-featuresAutomationControlled和覆盖navigator.webdriver这两个设置是绕过Selenium检测的核心。第二步访问页面并等待元素加载很多人写Selenium代码喜欢用time.sleep()来等待页面加载。这是一个非常不好的习惯。time.sleep()会让程序固定等待一段时间不管页面有没有加载完成。如果设置的时间太短元素还没加载出来就会报错如果设置的时间太长又会浪费时间。正确的做法是使用显式等待。显式等待会一直等待直到指定的元素加载出来或者超时。defcrawl_weibo_hot():driverinit_browser()hot_list[]try:# 访问微博热搜页面driver.get(https://weibo.com/newlogin?tabtypeweibogid102803openLoginLayer0url)# 显式等待热搜列表加载完成最多等待10秒waitWebDriverWait(driver,10)hot_itemswait.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:# 提取热搜标题titleitem.find_element(By.CSS_SELECTOR,h3[class*HotTopic_title_]).text.strip()# 提取热搜热度heatitem.find_element(By.CSS_SELECTOR,span[class*HotTopic_num_]).text.strip()# 提取热搜标签置顶、爆、新、热等tagtry:tag_elementitem.find_element(By.CSS_SELECTOR,span[class*HotTopic_tag_])tagtag_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选择器来定位元素。相比XPathCSS选择器更简洁执行速度更快而且不容易因为页面结构的微小变化而失效。第三步保存数据到CSV文件爬取到数据后我们把它保存到CSV文件里方便后续分析。defsave_to_csv(data,filenameweibo_hot.csv):ifnotdata:print(没有数据可保存)return# 获取所有字段名fieldnamesdata[0].keys()withopen(filename,w,newline,encodingutf-8-sig)asf:writercsv.DictWriter(f,fieldnamesfieldnames)writer.writeheader()writer.writerows(data)print(f数据已保存到{filename})if__name____main__:print(开始爬取微博热搜...)hot_datacrawl_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_times3):for_inrange(scroll_times):# 随机滚动距离scroll_heightrandom.randint(300,800)driver.execute_script(fwindow.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-serverhttp://{proxy_ip}:{proxy_port})常见问题与解决方案我把大家在实际运行代码的时候最容易遇到的问题整理了一下每个问题都给出了具体的解决方案。问题1元素定位失败提示NoSuchElementException这是最常见的问题。原因主要有三个页面还没加载完成就开始查找元素元素定位器写错了微博更新了页面结构解决方案一定要使用显式等待不要用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_datacrawl_weibo_hot()save_to_csv(hot_data,fweibo_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协议不要用于非法用途。