I'll承认,这种解决方案比Firefox Profile saveToDisk替代方案要“hacky”一些,但它适用于Chrome和Firefox,并且不依赖于可能随时更改的特定于浏览器的功能。如果没有其他办法,也许这会给某人在解决未来挑战时提供一些不同的视角。
先决条件:确保您已安装selenium和pyvirtualdisplay...
Python 2:
sudo pip install selenium pyvirtualdisplay
Python 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()
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:
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()
display.popen.kill()
解释
首先,我们在目标域名上加载一个URL以从中下载文件。这样我们就可以在该域名上执行AJAX请求,而不会遇到跨站脚本问题。
接下来,我们将一些JavaScript注入到DOM中,触发一个AJAX请求。一旦AJAX请求返回响应,我们就将响应加载到一个FileReader对象中。从那里,我们可以通过调用readAsDataUrl()提取文件的base64编码内容。然后,我们将base64编码内容附加到全局可访问变量window
上。
最后,由于AJAX请求是异步的,我们进入一个Python while循环,等待内容附加到窗口。一旦附加完成,我们解码从窗口检索到的base64内容并将其保存到文件中。
这个解决方案应该适用于Selenium支持的所有现代浏览器,并且无论是文本还是二进制文件,以及所有MIME类型都可以使用。
备选方法
尽管我还没有测试过,但Selenium确实可以让您等待直到DOM中出现元素。您可以在DOM中创建一个具有特定ID的元素,并使用该元素的绑定作为检索已下载文件的触发器,而不是循环直到全局可访问变量被填充。
urllib
并使用urllib.urlretrieve(url)
来获取下载,其中url
是链接发送到的网址。 - Serial