使用selenium从页面保存图像

时间:2013-08-25 00:52:25

标签: c# selenium download selenium-chromedriver

我正在使用Selenium& Google Chrome驱动程序以编程方式打开页面。在每个页面上都有一个动态生成的图像,我想下载它。目前,我正在等待页面完成加载,然后我抓取图像URL并使用System.Net.WebClient下载它。

除了我下载图像两次外 - 其他工作正常 - 一次是在浏览器中,一次是使用WebClient。问题是每个图像大约是15MB,下载两次很快就会加起来。

那么 - 是否可以直接从谷歌浏览器中获取图像?

10 个答案:

答案 0 :(得分:9)

一种方法是使用webdriver执行的javascript获取图像的base64字符串。然后,您可以将图像的base64string保存到文件。

基本上,如果您的图片是

<img id='Img1' src='someurl'>

然后你可以转换它像

var base64string = driver.ExecuteScript(@"
    var c = document.createElement('canvas');
    var ctx = c.getContext('2d');
    var img = document.getElementById('Img1');
    c.height=img.height;
    c.width=img.width;
    ctx.drawImage(img, 0, 0,img.width, img.height);
    var base64String = c.toDataURL();
    return base64String;
    ") as string;

var base64 = base64string.Split(',').Last();
using (var stream = new MemoryStream(Convert.FromBase64String(base64)))
{
    using (var bitmap = new Bitmap(stream))
    {
        var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ImageName.png");
        bitmap.Save(filepath, ImageFormat.Png);
    }
}

答案 1 :(得分:5)

是的,您可以通过以下几个步骤完成此操作:

  1. 截取网页截图并将其保存到磁盘
  2. 找到图像元素
  3. 查找图像元素的位置,宽度和高度
  4. 从您在步骤1中截取的屏幕截图中裁剪所需的图像
  5. 将图像保存到磁盘(或使用它执行其他操作)
  6. 示例代码 - 请添加代码以捕获异常

            IWebDriver driver = new ChromeDriver();
    
            //replace with the page you want to navigate to
            string your_page = "https://www.google.com"; 
            driver.Navigate().GoToUrl(your_page);
    
            ITakesScreenshot ssdriver = driver as ITakesScreenshot;
            Screenshot screenshot = ssdriver.GetScreenshot();
    
            Screenshot tempImage = screenshot;
    
            tempImage.SaveAsFile(@"C:\full.png", ImageFormat.Png);
    
            //replace with the XPath of the image element
            IWebElement my_image = driver.FindElement(By.XPath("//*[@id=\"hplogo\"]/canvas[1]"));
    
            Point point = my_image.Location;
            int width = my_image.Size.Width;
            int height = my_image.Size.Height;
    
            Rectangle section = new Rectangle(point, new Size(width, height));
            Bitmap source = new Bitmap(@"C:\full.png");
            Bitmap final_image = CropImage(source, section);
    
            final_image.Save(@"C:\image.jpg");
    

    CropImage方法由James Hill发布, How to cut a part of image in C#

    但我会在这里添加它以便清晰

        public Bitmap CropImage(Bitmap source, Rectangle section)
        {
            Bitmap bmp = new Bitmap(section.Width, section.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
            return bmp;
        }
    

答案 2 :(得分:2)

基于meceks的答案,我使用以下版本的结果很好来捕获webdriver图像。

它以90%的质量创建一个base64 jpeg字符串。为了避免像素化问题,我将图像绘制到画布上,该画布大于稍后将要呈现的图像。因此,在保留宽高比的同时,将图像放大以最适合600像素的框。 由于jpeg不支持透明度,因此我用白色背景清除上下文。

var base64string = (driver as IJavaScriptExecutor).ExecuteScript(@"
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

function getMaxSize(srcWidth, srcHeight, maxWidth, maxHeight) {
    var widthScale = null;
    var heightScale = null;

    if (maxWidth != null)
    {
        widthScale = maxWidth / srcWidth;
    }
    if (maxHeight != null)
    {
        heightScale = maxHeight / srcHeight;
    }

    var ratio = Math.min(widthScale || heightScale, heightScale || widthScale);
    return {
        width: Math.round(srcWidth * ratio),
        height: Math.round(srcHeight * ratio)
    };
}

function getBase64FromImage(img, width, height) {
    var size = getMaxSize(width, height, 600, 600)
    canvas.width = size.width;
    canvas.height = size.height;
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, size.width, size.height);
    ctx.drawImage(img, 0, 0, size.width, size.height);
    return canvas.toDataURL('image/jpeg', 0.9);
}

var img = document.querySelector('#foo');
    return getBase64FromImage(img, img.width, img.height);
") as string;

var base64 = base64string.Split(',').Last();

答案 3 :(得分:1)

您可以使用this技术阻止图像在Google Chrome中下载。它运行名为“Block Image”的Google Chrome扩展程序。这样就不会使用chrome下载图像,而只需使用其URL和&amp ;;正常下载图像即可。 System.Net.WebClient。

答案 4 :(得分:1)

以上所有答案都有效。但是,它们都有局限性。 mecek的方法很酷,但它只适用于支持html 5的浏览器(尽管现在大多数浏览器都这样做),它会降低图像质量。屏幕截图方法也会降低图像质量。使用System.Net.WebClient可以避免这个问题,但在下载验证码图像的情况下不会工作。实际上,下载验证码图像时唯一适用于我的方法是使用Actions类(如果你使用的是Selenium的java版本,则使用Robot),如下所示:

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Interactions;
using System.Windows.Automation;//you need to add UIAutomationTypes and UIAutomationClient to references
using System.Runtime.InteropServices;

[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr point);

private IntPtr getIntPtrHandle(IWebDriver driver, int timeoutSeconds = 30)
{
        var end = DateTime.Now.AddSeconds(timeoutSeconds);
        while (DateTime.Now < end)
        {
            var ele = AutomationElement.RootElement;
            foreach (AutomationElement child in ele.FindAll(TreeScope.Children, Condition.TrueCondition))
            {
                if (!child.Current.Name.Contains(driver.Title)) continue;
                return new IntPtr(child.Current.NativeWindowHandle);
            }
        }
        return IntPtr.Zero;
}

private void downloadCaptcha(IWebDriver chromeDriver)
{
    OpenQA.Selenium.IWebElement captchaImage = chromeDriver.FindElement(By.Id("secimg0"));
    var handle = getIntPtrHandle(chromeDriver);
    SetForegroundWindow(handle);//you need a p/invoke 
    Thread.Sleep(1500);//setting foreground window takes time
    Actions action = new Actions(chromeDriver);
    action.ContextClick(captchaImage).Build().Perform();
    Thread.Sleep(300);
    SendKeys.Send("V");
    var start = Environment.TickCount;
    while (Environment.TickCount - start < 2000)
    {//can't use Thread.Sleep here, alternatively you can use a Timer
          Application.DoEvents();
    }
    SendKeys.SendWait(@"C:\temp\vImage.jpg");
    SendKeys.SendWait("{ENTER}");
}

这是我发现使用Selenium Chrome驱动程序下载验证码图像而不会失去其质量(以获得更好的OCR效果)的唯一方法,尽管限制也很明显。

答案 5 :(得分:1)

基于 Mehmet Mecek 的答案,我制定了自己的小方法, 提取基于className的图像(因为没有可用的“ id”),并且由于我要获取的每个图像都具有相同的className,因此我使用了src属性内容(图像的URL)将javascript结果过滤到得到我想要的特定图像...

src也只能是部分字符串(例如文件名),但必须与HTML中的原始字符区分大小写。

注意:不包括错误检查。

像魅力一样工作。


 public static void LoadImageFromClassAndSrcInfo(IWebDriver webDriver, string className, string partialSrc, string localFile)
        {
            IJavaScriptExecutor js = (IJavaScriptExecutor) webDriver;
            string base64string = js.ExecuteScript(@"
    var c = document.createElement('canvas');
    var ctx = c.getContext('2d');
    var img = Array.prototype.filter.call(document.getElementsByClassName('"+className+@"'), ({ src }) => src.includes('"+ partialSrc +@"') )[0];
    c.height=img.naturalHeight;
    c.width=img.naturalWidth;
    ctx.drawImage(img, 0, 0,img.naturalWidth, img.naturalHeight);
    var base64String = c.toDataURL();
    return base64String;
    ") as string;

            var base64 = base64string.Split(',').Last();

            using (var stream = new MemoryStream(Convert.FromBase64String(base64)))
            {
                using (var bitmap = new Bitmap(stream))
                {
                    bitmap.Save(localFile, ImageFormat.Jpeg);
                }
            }
        }

答案 6 :(得分:1)

只想分享经验: 我想将验证码图像以单独的形式放置,以便用户可以将文本放置在字段中,以代替主网页。 我结合了TH Todorov在https://stackoverflow.com/a/30025029(上面回答)和James Hill,在How to cut a part of image in C#(上面提到)提出的方法,并提出了以下代码。在某些情况下,它可以完美运行,您无需将图片保存在驱动器上。我也为网站上的任何图像工作。希望对您有所帮助。

private Bitmap GetCaptchaImage()
        {
            ITakesScreenshot ssdriver = driver as ITakesScreenshot;
            Screenshot screenshot = ssdriver.GetScreenshot();
            IWebElement captchaImage = driver.FindElement(By.XPath("put the captcha image path here"));

            Point point = captchaImage.Location;
            int width = captchaImage.Size.Width;
            int height = captchaImage.Size.Height;

            Rectangle section = new Rectangle(point, new Size(width, height));
            Bitmap source = new Bitmap(new MemoryStream(screenshot.AsByteArray));

            Bitmap finalCaptchImage = CropImage(source, section);
            return finalCaptchImage;
        }
        private Bitmap CropImage(Bitmap source, Rectangle section)
        {
            Bitmap bmp = new Bitmap(section.Width, section.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
            return bmp;
        }

答案 7 :(得分:0)

I'm using Selenium & Google Chrome Driver

硒会谈。

once in the browser, once with WebClient

Htmlunit?

无论如何,为什么不使用webclient(htmlunit-driver)或纯htmlunit(http://htmlunit.sourceforge.net/)。 Htmlunit默认不下载图像。

您可以根据自己的要求随意下载。

答案 8 :(得分:0)

您是否尝试使用ImageIO下载图像?

String imageUrl = "image.png";
BufferedImage bufferedImage = ImageIO.read(imageUrl);
ImageIO.write(bufferedImage, "png", new File("savedImage.png"));

答案 9 :(得分:0)

在Java中尝试以下操作:

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("C:\\ClaimsData\\downloadspdfs\\" + originalName));