Python selenium 获取 javascript 添加的网页内容

Python selenium get contents of a webpage added by javascript

我用的在线音乐播放器叫“网易云音乐”,我的账户里有多个播放列表,里面有几千首曲目,组织和分类很差,有重复的条目,所以我想导出它们进入 SQL table 来组织它们。

我找到了一种不使用客户端软件查看播放列表的方法,即点击播放列表页面顶部的分享按钮,然后点击“复制link”。

但在客户端以外的任何浏览器中打开link,播放列表将被限制为 1000 首曲目。

但我找到了克服它的方法,我安装了 Tampermonkey and then installed this script

现在我可以在浏览器中查看完整的播放列表。

这是 sample playlist.

播放列表如下所示:

第一列是歌名,第二列是时长,第三列是歌手,最后一列是专辑。

第一、第三和第四列中的文本分别是歌曲、艺术家和专辑页面的超link。

我对html一无所知,但我设法获得了它的数据结构。

我们需要的是位于 xpath //table/tbody 的 table,每一行都是名为 tr(xpath //table/tbody/tr) 的 table 的子节点。

这是示例行:

<td class="left">
    <div class="hd "><span data-res-id="5221710" data-res-type="18" data-res-action="play" data-res-from="13" data-res-data="158624364" class="ply ">&nbsp;</span><span class="num">1</span></div>
</td>
<td>
    <div class="f-cb">
        <div class="tt">
            <div class="ttc">
                <span class="txt">
                    <a href="#/song?id=5221710"><b title="Axel F">Axel F</b></a>
                    
                    
                </span>
            </div>
        </div>
    </div>
</td>
<td class=" s-fc3">
    <span class="u-dur candel">03:00</span>
    <div class="opt hshow">
        <a class="u-icn u-icn-81 icn-add" href="javascript:;" title="添加到播放列表" hidefocus="true" data-res-type="18" data-res-id="5221710" data-res-action="addto" data-res-from="13" data-res-data="158624364"></a>
        <span data-res-id="5221710" data-res-type="18" data-res-action="fav" class="icn icn-fav" title="收藏"></span>
        <span data-res-id="5221710" data-res-type="18" data-res-action="share" data-res-name="Greatest Hits Of The Millennium 80's Vol.2" data-res-author="Harold Faltermeyer" data-res-pic="https://p2.music.126.net/tOa6Tizqy755OZE7ITsw_g==/775155697626111.jpg" class="icn icn-share" title="分享">分享</span>
        <span data-res-id="5221710" data-res-type="18" data-res-action="download" class="icn icn-dl" title="下载"></span>
        <span data-res-id="5221710" data-res-type="18" data-res-from="13" data-res-data="158624364" data-res-action="delete" class="icn icn-del" title="删除">删除</span>
    </div>
</td>
<td>
    <div class="text" title="Harold Faltermeyer">
        <span title="Harold Faltermeyer">
            <a href="#/artist?id=34854" hidefocus="true">Harold Faltermeyer</a>
        </span>
    </div>
</td>
<td>
    <div class="text">
        <a href="#/album?id=509819" title="Greatest Hits Of The Millennium 80's Vol.2">Greatest Hits Of The Millennium 80's Vol.2</a>
    </div>
</td>

列是元素的子节点。

我已经成功获取了对应列的 xpaths:

/td[2]/div/div/div/span/a/b -->  title
/td[2]/div/div/div/span/a -->  song link
/td[3]/span -->  duration
/td[4]/div/span/a -->  artist
/td[4]/div/span/a['href'] -->  artist link
/td[5]/div/a -->  album
/td[5]/div/a['href'] -->  album link

我们应该在 link 前面添加地址 music.163.com/ 以获得完整地址。

我正在考虑使用 selenium 获取元素,更具体地说,通过 xpath 查找行,然后遍历行并通过行内的 xpath 获取列,然后将值添加到命名元组列表中。

从这里开始,将元素添加到 SQL table。

但我无法让它工作。

我成功打开了 Firefox selenium window,安装了 tampermonkey 和访问完整播放列表的脚本(这两个安装是手动完成的),然后进入播放列表页面并尝试获取元素:

from selenium import webdriver
Firefox = webdriver.Firefox()
Firefox.get('https://music.163.com/#/playlist?id=158624364&userid=126762751')
Firefox.find_elements_by_xpath('//table/tbody/tr')

结果是一个空列表。

我不知道出了什么问题,我在开发者工具中可以查看 table 元素,然后我查看了它的源代码,发现 table 不在它的源代码。

我什至通过开发者工具获得了完整的 table,我上传了它 here

但是selenium是看不到的。显然,浏览器有办法显示原始 html 源代码中没有的内容,而 selenium 不能。那时我才意识到浏览器可以执行 javascript 并且原始源代码中没有的其他内容可能是由 javascript 在某处添加的,而我使用的代码没有涉及 javascript 并且只能得到原始源码,没有附加内容。

我试过谷歌搜索 python selenium get contents of a webpage added by javascript,但没有用。

所以我有两个问题,第一,在短期内,如何使用一些html解析库来解析本地存储在txt文件中的一段html代码?

其次,从长远来看,我如何使用 selenium 或任何其他 Python html 库来获取由 javascript 添加的附加内容的网页的完整源代码而不是只有原始源代码而没有附加内容,这样我就不需要每次都手动导出元素了?

最简单的答案是,您必须在使用 Firefox.get('https://music.163.com/#/playlist?id=158624364&userid=126762751') 打开页面后添加一些延迟,然后再使用 Firefox.find_elements_by_xpath('//table/tbody/tr') 获取元素,以便加载页面上的元素。这需要一些时间。
所以,你可以简单地在其中添加一种 time.sleep(5)
更好的方法是改用预期条件。
像这样:

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
Firefox = webdriver.Firefox()

# Wait for initialize, in seconds
wait = WebDriverWait(Firefox, 20)

Firefox.get('https://music.163.com/#/playlist?id=158624364&userid=126762751')

wait.until(EC.visibility_of_element_located((By.XPATH, '//table/tbody/tr')))

Firefox.find_elements_by_xpath('//table/tbody/tr')

UPD
那里有一个 iframe,所以你需要切换到那个 iframe,如下所示:

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
Firefox = webdriver.Firefox()

# Wait for initialize, in seconds
wait = WebDriverWait(Firefox, 20)

Firefox.get('https://music.163.com/#/playlist?id=158624364&userid=126762751')

iframe = driver.find_element_by_xpath('//iframe[@id="g_iframe"]')
driver.switch_to.frame(iframe)

wait.until(EC.visibility_of_element_located((By.XPATH, '//table/tbody/tr')))

Firefox.find_elements_by_xpath('//table/tbody/tr')