如何使用Selenium(任何版本)下载图像?

如何使用Selenium(任何版本)下载图像?,selenium,webdriver,Selenium,Webdriver,我想知道,如何使用selenium/webdriver下载页面的图像。假设需要用户会话来下载图像,因此使用纯URL是没有帮助的。非常感谢任何示例代码。我更喜欢这样做: 1. Get the SRC attribute of the image. 2. Use ImageIO.read to read the image onto a BufferedImage 3. Save the BufferedImage using ImageIO.write function 例如 String sr

我想知道,如何使用selenium/webdriver下载页面的图像。假设需要用户会话来下载图像,因此使用纯URL是没有帮助的。非常感谢任何示例代码。

我更喜欢这样做:

1. Get the SRC attribute of the image.
2. Use ImageIO.read to read the image onto a BufferedImage
3. Save the BufferedImage using ImageIO.write function
例如

String src=imgElement.getAttribute('src');
buffereImage buffereImage=ImageIO.read(新URL(src));
File outputfile=新文件(“saved.png”);
write(bufferedImage,“png”,outputfile);

如果需要测试该映像是否可用并存在,可以执行以下操作:

protected boolean isResourceAvailableByUrl(String resourceUrl) {
    // backup current url, to come back to it in future
    String currentUrl = webDriver.getCurrentUrl();
    try {
        // try to get image by url
        webDriver.get(resourceUrl);
        // if "resource not found" message was not appeared - image exists
        return webDriver.findElements(RESOURCE_NOT_FOUND).isEmpty();
    } finally {
        // back to page
        webDriver.get(currentUrl);
    }
}
 WebElement logo = driver.findElement(By.cssSelector(".image-logo"));
 String logoSRC = logo.getAttribute("src");

 URL imageURL = new URL(logoSRC);
 BufferedImage saveImage = ImageIO.read(imageURL);

 ImageIO.write(saveImage, "png", new File("logo-image.png"));
但您需要确定的是,在执行此方法之前,通过currentUrl确实会使您返回页面。我的情况就是这样。如果没有-您可以尝试使用:

webDriver.navigate().back()
而且,不幸的是,似乎没有机会分析响应状态代码。这就是为什么您需要在未找到的页面上找到任何特定的web元素,并检查它是否出现,然后确定该图像不存在

这只是权宜之计,因为我没有找到任何正式的方法来解决它

注:
当您使用授权会话获取资源时,此解决方案非常有用,并且不能仅通过ImageIO或严格地通过HttpClient下载资源。

另一个最正确的解决方案是通过简单的HTTP请求直接下载资源。
您可以使用webDriver的用户会话,因为它存储cookies。
在我的示例中,我只是分析它返回的状态代码。若为200,则图像存在,可供显示或下载。若您真的需要下载文件本身,那个么您可以从httpResponse实体(将其用作简单的输入流)获取所有图像数据


这里的其他解决方案不适用于所有浏览器,也不适用于所有网站,或者两者都适用

这个解决方案应该更加稳健。它使用浏览器查看图像,调整浏览器大小以适应图像大小,拍摄屏幕截图,最后将浏览器大小调整回原始大小

蟒蛇: 此解决方案的一个缺点是,如果图像非常小,浏览器将不会调整到那么小的大小,并且可能会在其周围出现黑色边框。

我更喜欢这样:

protected boolean isResourceAvailableByUrl(String resourceUrl) {
    // backup current url, to come back to it in future
    String currentUrl = webDriver.getCurrentUrl();
    try {
        // try to get image by url
        webDriver.get(resourceUrl);
        // if "resource not found" message was not appeared - image exists
        return webDriver.findElements(RESOURCE_NOT_FOUND).isEmpty();
    } finally {
        // back to page
        webDriver.get(currentUrl);
    }
}
 WebElement logo = driver.findElement(By.cssSelector(".image-logo"));
 String logoSRC = logo.getAttribute("src");

 URL imageURL = new URL(logoSRC);
 BufferedImage saveImage = ImageIO.read(imageURL);

 ImageIO.write(saveImage, "png", new File("logo-image.png"));

使用selenium获取图像src

elemImg.get_attribute('src')
为此使用编程语言,对于python; 检查以下答案:
这里是一个javascript解决方案。 这有点傻——而且我厌倦了用太多的请求来攻击源映像的服务器。有人能告诉我fetch()是否访问浏览器的缓存吗?我不想向源服务器发送垃圾邮件

它在窗口中附加一个FileReader(),获取图像并将其转换为base64,并将字符串标记到窗口中

然后,驱动程序可以返回该窗口变量

export async function scrapePic(driver) {
try {
console.log("waiting for that profile piccah")
console.log(driver)

let rootEl = await driver.findElement(By.css('.your-root-element'));
let imgEl = await rootEl.findElement(By.css('img'))
await driver.wait(until.elementIsVisible(imgEl, 10000));
console.log('profile piccah found')
let img = await imgEl.getAttribute('src')
//attach reader to driver window
await driver.executeScript(`window.myFileReader = new FileReader();`)
await driver.executeScript(`
  window.myFileReader.onloadend = function() {
    window['profileImage'] = this.result
  }
  fetch( arguments[0] ).then( res => res.blob() ).then( blob => window.electronFileReader.readAsDataURL(blob) )
  `, img)
await driver.sleep(5000)
let img64 = await driver.executeScript(`return window.profileImage`)
console.log(img64)


} catch (e) {
console.log(e)
} finally {
return img64
  }
}
为我工作:

# open the image in a new tab
driver.execute_script('''window.open("''' + wanted_url + '''","_blank");''')
sleep(2)
driver.switch_to.window(driver.window_handles[1])
sleep(2)

# make screenshot
driver.save_screenshot("C://Folder/" + photo_name + ".jpeg")
sleep(2)

# close the new tab
driver.execute_script('''window.close();''')
sleep(2)

#back to original tab
driver.switch_to.window(driver.window_handles[0])

对于我的用例,存在cookies和其他问题,使得这里的其他方法不适合

最后,我使用XMLHttpRequest填充了一个文件读取器(来自,然后使用Selenium的
ExecuteAsyncScript
(如中所示)调用该读取器)。这使我能够得到一个直接解析的文件

下面是我获取数据URL的C#代码:

公共字符串ImageUrlToDataUrl(IWebDriver驱动程序,字符串imageUrl)
{
var js=新的StringBuilder();
js.AppendLine(“var done=arguments[0];”;//来自ExecuteAsyncScript的回调
js.AppendLine(@)
函数toDataURL(url,回调){
var xhr=new XMLHttpRequest();
xhr.onload=函数(){
var reader=new FileReader();
reader.onloadend=函数(){
回调(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET',url);
xhr.responseType='blob';
xhr.send();
}“”;//XMLHttpRequest->FileReader->DataURL转换
js.AppendLine(“toDataURL(“+imageUrl+”,done);”;//调用函数
var executor=(IJavaScriptExecutor)驱动程序;
var dataUrl=executor.ExecuteAsyncScript(js.ToString())作为字符串;
返回dataUrl;
}
尝试以下操作

JavascriptExecutor js = (JavascriptExecutor) driver;                              
String base64string = (String) js.executeScript("var c = document.createElement('canvas');"
                       + " var ctx = c.getContext('2d');"
                       + "var img = document.getElementsByTagName('img')[0];"
                       + "c.height=img.naturalHeight;"
                       + "c.width=img.naturalWidth;"
                       + "ctx.drawImage(img, 0, 0,img.naturalWidth, img.naturalHeight);"
                       + "var base64String = c.toDataURL();"
                       + "return base64String;");
String[] base64Array = base64string.split(",");

String base64 = base64Array[base64Array.length - 1];

byte[] data = Base64.decode(base64);

ByteArrayInputStream memstream = new ByteArrayInputStream(data);
BufferedImage saveImage = ImageIO.read(memstream);

ImageIO.write(saveImage, "png", new File("path"));

我发现避免两次下载图像的唯一方法是使用ChromeDevTools协议查看器

在Python中,这将提供:

import base64
import pychrome
def save_image(file_content, file_name):
    try:
       file_content=base64.b64decode(file_content)
       with open("C:\\Crawler\\temp\\" + file_name,"wb") as f:
            f.write(file_content)
    except Exception as e:
       print(str(e))

def response_received(requestId, loaderId, timestamp, type, response, frameId):
    if type == 'Image':
        url = response.get('url')
        print(f"Image loaded: {url}")
        response_body = tab.Network.getResponseBody(requestId=requestId)
        file_name = url.split('/')[-1].split('?')[0]
        if file_name:
            save_image(response_body['body'], file_name)


tab.Network.responseReceived = response_received

# start the tab 
tab.start()

# call method
tab.Network.enable()

# get request to target the site selenium 
driver.get("https://www.realtor.com/ads/forsale/TMAI112283AAAA")

# wait for loading
tab.wait(50)

虽然@aboy021 JS代码语法正确,但我无法运行该代码。(使用ChromeV83.xx)

但是,这段代码有效(Java):


如何下载到文件,从元素文本或属性获取URL

using OpenQA.Selenium.Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");
using Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");
完整的扩展代码可在此处找到:

如果您想在不编写代码的情况下使用此方法,请使用NuGet

用法

using OpenQA.Selenium.Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");
using Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");
建议使用NuGet,因为它包含更多的Selenium工具和扩展

无需NuGet即可使用(自行实现)

扩展类

using System.IO;
using System.Net.Http;
using System.Text.RegularExpressions;
 
namespace Extensions
{
    public static class WebElementExtensions
    {
        public static IWebElement DownloadResource(this IWebElement element, string path)
        {
            return DoDownloadResource(element, path, "");
        }
 
        public static IWebElement DownloadResource(this IWebElement element, string path, string attribute)
        {
            return DoDownloadResource(element, path, attribute);
        }
 
        private static IWebElement DoDownloadResource(this IWebElement element, string path, string attribute)
        {
            // get resource address
            var resource = (string.IsNullOrEmpty(attribute))
                ? element.Text
                : element.GetAttribute(attribute);
 
            // download resource
            using (var client = new HttpClient())
            {
                // get response for the current resource
                var httpResponseMessage = client.GetAsync(resource).GetAwaiter().GetResult();
 
                // exit condition
                if (!httpResponseMessage.IsSuccessStatusCode) return element;
 
                // create directories path
                Directory.CreateDirectory(path);
 
                // get absolute file name
                var fileName = Regex.Match(resource, @"[^/\\&\?]+\.\w{3,4}(?=([\?&].*$|$))").Value;
                path = (path.LastIndexOf(@"\") == path.Length - 1)
                    ? path + fileName
                    : path + $@"\{fileName}";
 
                // write the file
                File.WriteAllBytes(path, httpResponseMessage.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult());
            }
 
            // keep the fluent
            return element;
        }
    }
}
用法

using OpenQA.Selenium.Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");
using Extensions;
 
...
 
var driver = new ChromeDriver();
 
// from element attribute
var element = driver.FindElement(By.XPath("//img[@id='my_img']")).DownloadResource(path: @"C:\images\cap_image_01.png", attribute: "src");
 
// from element text
var element = driver.FindElement(By.XPath("//div[1]")).DownloadResource(path: @"C:\images\cap_image_01.png");

页面上显示的实际图像。而不是页面的整个屏幕截图。请看,窗口已经在页面上加载了配置文件pic,我希望在它执行fetch()时,它只是进入缓存…我不完全确定如何确认是否是这种情况。新选项卡是否会向源代码发出新请求?我想是的,但没有与Snifferi检查。如果不想双重下载图像,请禁用chrome中的图像:prefs={“profile.managed_default_content_settings.images”:2}chrome_options.add_option(“prefs”,prefs)有趣的想法,但这不会提供原始图像这是一个宝石!伟大的发现!旁注:当然你会以这种方式释放原始照片的所有EXIF/元数据。最好的解决方案。花了我很多时间。希望有一天能好好利用它。:)也许有人能告诉我为什么aboy021版本不适合我。