我正在使用 Selenium 和 Google Chrome 驱动程序以编程方式打开页面。在每个页面上都有一个动态生成的图像,我想下载它。目前,我正在等待页面完成加载,然后我获取图像 URL 并使用 System.Net.WebClient 下载它。
这很好用,除了我下载图像两次——一次在浏览器中,一次在 WebClient 中。问题是每个图像大约有 15MB 并且下载两次加起来很快。
那么 - 是否可以直接从谷歌浏览器获取图像?
我正在使用 Selenium 和 Google Chrome 驱动程序以编程方式打开页面。在每个页面上都有一个动态生成的图像,我想下载它。目前,我正在等待页面完成加载,然后我获取图像 URL 并使用 System.Net.WebClient 下载它。
这很好用,除了我下载图像两次——一次在浏览器中,一次在 WebClient 中。问题是每个图像大约有 15MB 并且下载两次加起来很快。
那么 - 是否可以直接从谷歌浏览器获取图像?
一种方法是使用由 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.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))
{
var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ImageName.png");
bitmap.Save(filepath, ImageFormat.Png);
}
}
是的,您可以通过几个步骤执行此操作:
示例代码 - 请添加您的代码以捕获异常
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;
}
以上所有答案都有效。但是,它们都有局限性。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 效果)的唯一方法,尽管限制也很明显。
您可以使用此技术阻止在 Google Chrome 中下载图像。它运行一个名为“Block Image”的 Google Chrome 扩展程序。这样就不会使用 chrome 下载图像,只需使用其 URL 和 System.Net.WebClient 正常下载图像即可。
根据 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();
您是否尝试使用 ImageIO 下载图像?
String imageUrl = "image.png";
BufferedImage bufferedImage = ImageIO.read(imageUrl);
ImageIO.write(bufferedImage, "png", new File("savedImage.png"));
根据Mehmet Mecek的回答,我制作了自己的小方法,根据类名获取图像(因为没有可用的“id”),并且由于我想要获取的每个图像都有相同的类名,所以我已经使用 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);
}
}
}
在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));
只是想分享一个经验:我想将验证码图像放在单独的表单中,以便用户可以将文本放在字段中作为主网页的替代品。我结合了 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;
}
I'm using Selenium & Google Chrome Driver
谈论硒。
once in the browser, once with WebClient
html单元?
无论如何,你为什么不使用 webclient (htmlunit-driver) 或纯 htmlunit ( http://htmlunit.sourceforge.net/ )。Htmlunit 默认不下载图片。
您可以根据需要自行下载。