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 末尾展开。