跳转至

14.2 爬虫:网易云音乐(动态页面 + Selenium)

内容梗概

本课是爬虫实战的第二例,处理与 B 站不同的另一种情况:动态渲染页面。以网易云音乐的歌单列表为例,老师先解释为什么 requests 直接抓不到内容——页面里嵌套了由 JavaScript 延迟加载的 iframe,真实歌单数据在 iframe 内部。随后演示用 Selenium(webdriver)控制浏览器打开页面,通过 page_source 验证渲染完整,再经 switch_to.frame 切入 iframe,用 WebDriverWait 显式等待元素加载,最后用 CSS 选择器和 get_attribute 提取所有歌单的名字与链接。全程边查官方文档边写代码,演示"看文档写代码"的工作方式。

知识点详解

动态页面的问题(约 00:00 - 04:30)

  • 静态页面(B 站)之外还有动态页面:直接用 requests 访问拿不到真实 HTML。
  • 网易云音乐的歌单页:F12 检查可发现页面嵌套了 iframe,requests 直接获取时 iframe 内部是空的——内容由 JavaScript 在页面加载后再填充。

iframe 概念(约 04:30 - 07:00)

  • iframe 是一个框架,可以在一个网页里完整加载另一个链接的内容(如在自己网站里嵌一个百度搜索框)。
  • 早期网站布局大量依赖 iframe(左导航右内容),现代仍有网站保留这种架构,网易云音乐即是。
  • 网易云的 iframe 不是随页面实时加载的,而是页面加载完后由 JavaScript 再加载内容。

Selenium 环境准备(约 00:30 - 03:30、07:30 - 09:00)

  • 新建目录 music163,pipenv install 初始化虚拟环境(可用 PowerShell 的 Ctrl+R 搜索历史命令),pipenv shell 进入,安装 Selenium(那个"s 开头、能自动打开浏览器的包")。
  • code ./ 用 VSCode 打开目录,trl+\ 打开内置终端。
  • 因 B 站演示被封 IP,本课改为"写好代码逐行讲解、最后只运行一次",降低被封风险。

Selenium 基本用法(约 08:50 - 11:00)

  • 查官方文档 Getting Started:导入 webdriver,在已下载对应浏览器驱动(如 ChromeDriver)的前提下,webdriver.Chrome() 创建浏览器对象,用 .get(url) 控制浏览器打开链接。
  • 验证渲染完整性:通过 webdriver API 中的 page_source(网页源码)属性打印 HTML,与浏览器肉眼所见核对——确认 JavaScript 已正常渲染。

定位 iframe 内元素(约 11:00 - 16:40)

  • 歌单封面项在 li 中,其父元素 id 为 m-pl-container,理想选择器是 #m-pl-container > li。
  • 陷阱:直接 find_element_by_css_selector 找不到——不能直接在主 HTML 里查找属于其 iframe 内部的元素,结果是空。
  • 解决:文档 Navigating 章节的 "Moving between windows and frames"——用 driver.switch_to.frame(...) 先进入 iframe 再查找;网易云这个 iframe 的 name 是 contentFrame。
  • 版本差异:旧文档写 switch_to_frame,新版已废弃,应写 switch_to.frame(...);写错时包会报错提示改用新写法(约 16:00)。

显式等待 WebDriverWait(约 17:00 - 24:00)

  • 代码中用了 try/finally 异常处理(Python 内置机制,报错也会执行 finally 部分,用于让浏览器正常退出),不展开。
  • 问题:switch 进 iframe 后立刻 find_element... 看似合理,实际会失败——网络原因导致 iframe 内容还没加载完,元素不存在;会让人误以为 CSS 选择器写错。
  • 解决:WebDriverWait(driver, 15) —— 第一个参数是浏览器对象,第二个是最长等待秒数;链式调用 .until(...) 指定期望条件。
  • 期望条件(expected_conditions)有很多种,这里用 presence_of_element_located(某元素已加载出现);配合 By.ID 指定元素 id。
  • 整串调用会**返回**等到后选中的那个元素(此处为 ul),其类型是 WebElement。
  • 老师强调:他翻文档的过程是在重放自己真实的工作流程——不会就查文档,边看边写很正常,"这才叫专业"。

提取数据(约 24:30 - 27:30)

  • 在拿到的 ul(WebElement)上继续用 find_elements_by_css_selector 找其下的 li 子元素,返回列表,每个元素仍是 WebElement 类型。
  • 用 WebElement 的 get_attribute(...) 方法取属性:取链接的 title(歌单名)和 href(歌单链接),存入结果列表。

运行结果与后续(约 27:30 - 29:30)

  • 实际运行成功:浏览器自动打开页面,抓到全部歌单名与 URL 后退出,未被封。
  • 可以歌单 URL 为新目标继续深入抓歌曲信息,层层抓下去可爬全站——但仅作技术演示,网易云同样不允许爬取,注意法律风险。
  • 预告:下半节课讲封 IP 及绕过方法(代理等),但不做演示。

示例与演示

  • 演示目标:抓取网易云音乐歌单列表页所有歌单的名称与链接。
  • 核心代码骨架:
  • webdriver.Chrome() + get(url) 打开页面;
  • driver.page_source 验证渲染;
  • driver.switch_to.frame("contentFrame") 切入 iframe;
  • WebDriverWait(driver, 15).until(expected_conditions.presence_of_element_located((By.ID, "m-pl-container"))) 等待列表容器加载;
  • find_elements_by_css_selector 取所有 li,循环 get_attribute("title") / get_attribute("href") 收集结果;
  • try/finally 保证浏览器退出。
  • 结果:成功抓到全部歌单名与链接。

重点与难点

  • iframe 是动态页常见坑:元素明明在页面上看得见却选不中,要先 switch_to.frame。
  • 等待加载:直接 find 会因内容未加载完而失败;用 WebDriverWait + expected_conditions 显式等待,而不是怀疑选择器。
  • 区分两种"找不到元素":选择器错 vs 元素尚未加载 vs 元素在 iframe 里。
  • 文档版本差异:旧 API(switch_to_frame、find_element_by_*)在新版 Selenium 中已废弃,要按报错提示改用新写法。
  • 动态渲染方案(Selenium)比 requests 慢且重,确认页面是动态渲染后才用它。

关联内容

  • 与 14.1 对比:B 站是静态页面(requests + BeautifulSoup),本课是动态页面(Selenium);两者都要"先验证拿到的 HTML 是否完整"这一步。
  • 前置知识:pipenv 虚拟环境流程(10.x)、requests 抓取流程(14.1)、CSS 选择器。
  • 后续:14.3 讲第三种情况——Ajax 数据接口;本课末尾预告的"封 IP 与代理"在 14.4 末尾展开。