Un modo rapido e sporco sarebbe utilizzare il controllo WinForms WebBrowser e disegnarlo in una bitmap. Fare questo in un'app console standalone è un po 'complicato perché devi essere consapevole delle implicazioni dell'hosting di un controllo STAThread mentre usi un modello di programmazione fondamentalmente asincrono. Ma qui è una prova di lavoro di concetto che cattura una pagina web in un file BMP 800x600:
namespace WebBrowserScreenshotSample
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;
class Program
{
[STAThread]
static void Main()
{
int width = 800;
int height = 600;
using (WebBrowser browser = new WebBrowser())
{
browser.Width = width;
browser.Height = height;
browser.ScrollBarsEnabled = true;
// This will be called when the page finishes loading
browser.DocumentCompleted += Program.OnDocumentCompleted;
browser.Navigate("https://stackoverflow.com/");
// This prevents the application from exiting until
// Application.Exit is called
Application.Run();
}
}
static void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Now that the page is loaded, save it to a bitmap
WebBrowser browser = (WebBrowser)sender;
using (Graphics graphics = browser.CreateGraphics())
using (Bitmap bitmap = new Bitmap(browser.Width, browser.Height, graphics))
{
Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
browser.DrawToBitmap(bitmap, bounds);
bitmap.Save("screenshot.bmp", ImageFormat.Bmp);
}
// Instruct the application to exit
Application.Exit();
}
}
}
per compilare questo, creare una nuova applicazione console e assicurarsi di aggiungere i riferimenti di assemblaggio per System.Drawing
e System.Windows.Forms
.
UPDATE: Ho riscritto il codice per evitare di dover utilizzare il pattern WaitOne/DoEvents di polling degli hacky. Questo codice dovrebbe essere più vicino alle seguenti best practice.
UPDATE 2: Si indica che si desidera utilizzare questo in un'applicazione Windows Form. In tal caso, dimenticarsi di creare dinamicamente il controllo WebBrowser
. Quello che vuoi è creare un'istanza nascosta (Visible = false) di uno WebBrowser
nel tuo modulo e usarla nello stesso modo in cui mostro sopra. Ecco un altro esempio che mostra la parte del codice utente di un modulo con una casella di testo (webAddressTextBox
), un pulsante (generateScreenshotButton
) e un browser nascosto (webBrowser
). Mentre stavo lavorando su questo, ho scoperto una particolarità che non ho gestito prima: l'evento DocumentCompleted può effettivamente essere generato più volte a seconda della natura della pagina. Questo esempio dovrebbe funzionare in generale, e si può allungare a fare quello che vuoi:
namespace WebBrowserScreenshotFormsSample
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
public partial class MainForm : Form
{
public MainForm()
{
this.InitializeComponent();
// Register for this event; we'll save the screenshot when it fires
this.webBrowser.DocumentCompleted +=
new WebBrowserDocumentCompletedEventHandler(this.OnDocumentCompleted);
}
private void OnClickGenerateScreenshot(object sender, EventArgs e)
{
// Disable button to prevent multiple concurrent operations
this.generateScreenshotButton.Enabled = false;
string webAddressString = this.webAddressTextBox.Text;
Uri webAddress;
if (Uri.TryCreate(webAddressString, UriKind.Absolute, out webAddress))
{
this.webBrowser.Navigate(webAddress);
}
else
{
MessageBox.Show(
"Please enter a valid URI.",
"WebBrowser Screenshot Forms Sample",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
// Re-enable button on error before returning
this.generateScreenshotButton.Enabled = true;
}
}
private void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// This event can be raised multiple times depending on how much of the
// document has loaded, if there are multiple frames, etc.
// We only want the final page result, so we do the following check:
if (this.webBrowser.ReadyState == WebBrowserReadyState.Complete &&
e.Url == this.webBrowser.Url)
{
// Generate the file name here
string screenshotFileName = Path.GetFullPath(
"screenshot_" + DateTime.Now.Ticks + ".png");
this.SaveScreenshot(screenshotFileName);
MessageBox.Show(
"Screenshot saved to '" + screenshotFileName + "'.",
"WebBrowser Screenshot Forms Sample",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
// Re-enable button before returning
this.generateScreenshotButton.Enabled = true;
}
}
private void SaveScreenshot(string fileName)
{
int width = this.webBrowser.Width;
int height = this.webBrowser.Height;
using (Graphics graphics = this.webBrowser.CreateGraphics())
using (Bitmap bitmap = new Bitmap(width, height, graphics))
{
Rectangle bounds = new Rectangle(0, 0, width, height);
this.webBrowser.DrawToBitmap(bitmap, bounds);
bitmap.Save(fileName, ImageFormat.Png);
}
}
}
}
non l'ho provato (che è il motivo per cui questo è un commento, non una risposta), ma (http://www.dreamincode.net/code/snippet2539.htm) sembra essere una soluzione C# per salvare una pagina Web come una bitmap. –
Quante pagine esegui la scansione al mese? – jjxtra
Non molti, sto solo usando le immagini come mezzo per estrarre i dati, quindi se uno o due falliscono, non è un grosso problema. Finora non ho avuto problemi con questo, a parte il fatto che ha bisogno di usare Application.Run() per andare avanti. –