我承认这个解决方案比 Firefox Profile saveToDisk 替代方案更“hacky”,但它适用于 Chrome 和 Firefox,并且不依赖可能随时更改的特定于浏览器的功能。如果不出意外,也许这会让人们对如何解决未来的挑战有一点不同的看法。
先决条件:确保您已安装 selenium 和 pyvirtualdisplay ...
- 蟒蛇2:
sudo pip install selenium pyvirtualdisplay
- 蟒蛇 3:
sudo pip3 install selenium pyvirtualdisplay
魔法
import pyvirtualdisplay
import selenium
import selenium.webdriver
import time
import base64
import json
root_url = 'https://www.google.com'
download_url = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'
print('Opening virtual display')
display = pyvirtualdisplay.Display(visible=0, size=(1280, 1024,))
display.start()
print('\tDone')
print('Opening web browser')
driver = selenium.webdriver.Firefox()
#driver = selenium.webdriver.Chrome() # Alternately, give Chrome a try
print('\tDone')
print('Retrieving initial web page')
driver.get(root_url)
print('\tDone')
print('Injecting retrieval code into web page')
driver.execute_script("""
window.file_contents = null;
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
window.file_contents = reader.result;
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', %(download_url)s);
xhr.send();
""".replace('\r\n', ' ').replace('\r', ' ').replace('\n', ' ') % {
'download_url': json.dumps(download_url),
})
print('Looping until file is retrieved')
downloaded_file = None
while downloaded_file is None:
# Returns the file retrieved base64 encoded (perfect for downloading binary)
downloaded_file = driver.execute_script('return (window.file_contents !== null ? window.file_contents.split(\',\')[1] : null);')
print(downloaded_file)
if not downloaded_file:
print('\tNot downloaded, waiting...')
time.sleep(0.5)
print('\tDone')
print('Writing file to disk')
fp = open('google-logo.png', 'wb')
fp.write(base64.b64decode(downloaded_file))
fp.close()
print('\tDone')
driver.close() # close web browser, or it'll persist after python exits.
display.popen.kill() # close virtual display, or it'll persist after python exits.
解释
我们首先在我们要从中下载文件的域上加载一个 URL。这允许我们在该域上执行 AJAX 请求,而不会遇到跨站点脚本问题。
接下来,我们将一些 javascript 注入到触发 AJAX 请求的 DOM 中。一旦 AJAX 请求返回响应,我们就会获取响应并将其加载到 FileReader 对象中。从那里我们可以通过调用 readAsDataUrl() 来提取文件的 base64 编码内容。然后,我们获取 base64 编码的内容并将其附加到window
全局可访问的变量中。
最后,因为 AJAX 请求是异步的,我们进入一个 Python while 循环等待内容被追加到窗口中。附加后,我们解码从窗口检索到的 base64 内容并将其保存到文件中。
这个解决方案应该适用于 Selenium 支持的所有现代浏览器,无论是文本还是二进制,以及所有 mime 类型。
替代方法
虽然我没有对此进行测试,但 Selenium 确实让您能够等到元素出现在 DOM 中。您可以在 DOM 中创建一个具有特定 ID 的元素,并使用该元素的绑定作为触发器来检索下载的文件,而不是循环直到填充了全局可访问的变量。