2011-03-24 22 views
11

Estoy trabajando en un raspador web que a veces necesita recordar una página en particular, luego vaya a algunas otras páginas y luego regrese a esa página. Actualmente solo guardo la URL de la página, pero eso no funciona para páginas como Google Maps, donde la URL siempre es la misma.¿Cómo volver "manualmente" con un WebBrowser?

Veo que el método GoBack vuelve a la página anterior, por lo que de alguna manera el WebBrowser recuerda lo que eran las páginas anteriores. ¿Cómo puedo hacer esto manualmente? Podría contar cuántas páginas se han visitado desde la página a la que quiero volver y luego llamar al GoBack tantas veces como sea necesario, pero eso es bastante poco confiable y poco elegante. Entonces me pregunto cómo podría implementar un método GoBackToAParticularPage.

Hay una cosa que creo que me acercaría más a una solución: guardar la URL de todos los marcos y luego volver a colocarlos cuando regrese a esa página. Creo que eso solucionaría los problemas de Google Maps. No lo he probado todavía No sé exactamente cuál sería la forma correcta de hacer esto. Tendría que esperar a que los marcos existieran antes de configurar sus URL.

Respuesta

0

En caso de que alguien más pueda beneficiarse de él, aquí es cómo terminé haciéndolo. La única advertencia es que si el registro de viaje tiene demasiadas páginas en el medio, la entrada podría no existir más. Probablemente exista una manera de aumentar el tamaño del historial, pero dado que tiene que haber algún límite, utilizo el método TravelLog.GetTravelLogEntries para ver si la entrada aún existe o no, y si no, use la URL en su lugar.

La mayor parte de este código provino del PInvoke.

using System; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
using System.Collections.Generic; 

namespace TravelLogUtils 
{ 
    [ComVisible(true), ComImport()] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [GuidAttribute("7EBFDD87-AD18-11d3-A4C5-00C04F72D6B8")] 
    public interface ITravelLogEntry 
    { 
     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int GetTitle([Out] out IntPtr ppszTitle); //LPOLESTR LPWSTR 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int GetURL([Out] out IntPtr ppszURL); //LPOLESTR LPWSTR 
    } 

    [ComVisible(true), ComImport()] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [GuidAttribute("7EBFDD85-AD18-11d3-A4C5-00C04F72D6B8")] 
    public interface IEnumTravelLogEntry 
    { 
     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int Next(
      [In, MarshalAs(UnmanagedType.U4)] int celt, 
      [Out] out ITravelLogEntry rgelt, 
      [Out, MarshalAs(UnmanagedType.U4)] out int pceltFetched); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int Skip([In, MarshalAs(UnmanagedType.U4)] int celt); 

     void Reset(); 

     void Clone([Out] out ITravelLogEntry ppenum); 
    } 

    public enum TLMENUF 
    { 
     /// <summary> 
     /// Enumeration should include the current travel log entry. 
     /// </summary> 
     TLEF_RELATIVE_INCLUDE_CURRENT = 0x00000001, 
     /// <summary> 
     /// Enumeration should include entries before the current entry. 
     /// </summary> 
     TLEF_RELATIVE_BACK = 0x00000010, 
     /// <summary> 
     /// Enumeration should include entries after the current entry. 
     /// </summary> 
     TLEF_RELATIVE_FORE = 0x00000020, 
     /// <summary> 
     /// Enumeration should include entries which cannot be navigated to. 
     /// </summary> 
     TLEF_INCLUDE_UNINVOKEABLE = 0x00000040, 
     /// <summary> 
     /// Enumeration should include all invokable entries. 
     /// </summary> 
     TLEF_ABSOLUTE = 0x00000031 
    } 

    [ComVisible(true), ComImport()] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [GuidAttribute("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8")] 
    public interface ITravelLogStg 
    { 
     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int CreateEntry([In, MarshalAs(UnmanagedType.LPWStr)] string pszUrl, 
      [In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle, 
      [In] ITravelLogEntry ptleRelativeTo, 
      [In, MarshalAs(UnmanagedType.Bool)] bool fPrepend, 
      [Out] out ITravelLogEntry pptle); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int TravelTo([In] ITravelLogEntry ptle); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int EnumEntries([In] int TLENUMF_flags, [Out] out IEnumTravelLogEntry ppenum); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int FindEntries([In] int TLENUMF_flags, 
     [In, MarshalAs(UnmanagedType.LPWStr)] string pszUrl, 
     [Out] out IEnumTravelLogEntry ppenum); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int GetCount([In] int TLENUMF_flags, [Out] out int pcEntries); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int RemoveEntry([In] ITravelLogEntry ptle); 

     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int GetRelativeEntry([In] int iOffset, [Out] out ITravelLogEntry ptle); 
    } 

    [ComImport, ComVisible(true)] 
    [Guid("6d5140c1-7436-11ce-8034-00aa006009fa")] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    public interface IServiceProvider 
    { 
     [return: MarshalAs(UnmanagedType.I4)] 
     [PreserveSig] 
     int QueryService(
      [In] ref Guid guidService, 
      [In] ref Guid riid, 
      [Out] out IntPtr ppvObject); 
    } 

    public class TravelLog 
    { 
     public static Guid IID_ITravelLogStg = new Guid("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8"); 
     public static Guid SID_STravelLogCursor = new Guid("7EBFDD80-AD18-11d3-A4C5-00C04F72D6B8"); 

     //public static void TravelTo(WebBrowser webBrowser, int 
     public static ITravelLogEntry GetTravelLogEntry(WebBrowser webBrowser) 
     { 
      int HRESULT_OK = 0; 

      SHDocVw.IWebBrowser2 axWebBrowser = (SHDocVw.IWebBrowser2)webBrowser.ActiveXInstance; 
      IServiceProvider psp = axWebBrowser as IServiceProvider; 
      if (psp == null) throw new Exception("Could not get IServiceProvider."); 

      IntPtr oret = IntPtr.Zero;    
      int hr = psp.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out oret);    
      if ((oret == IntPtr.Zero) || (hr != HRESULT_OK)) throw new Exception("Failed to query service."); 

      ITravelLogStg tlstg = Marshal.GetObjectForIUnknown(oret) as ITravelLogStg; 
      if (null == tlstg) throw new Exception("Failed to get ITravelLogStg");    
      ITravelLogEntry ptle = null; 

      hr = tlstg.GetRelativeEntry(0, out ptle); 

      if (hr != HRESULT_OK) throw new Exception("Failed to get travel log entry with error " + hr.ToString("X")); 

      Marshal.ReleaseComObject(tlstg); 
      return ptle; 
     } 

     public static void TravelToTravelLogEntry(WebBrowser webBrowser, ITravelLogEntry travelLogEntry) 
     { 
      int HRESULT_OK = 0; 

      SHDocVw.IWebBrowser2 axWebBrowser = (SHDocVw.IWebBrowser2)webBrowser.ActiveXInstance; 
      IServiceProvider psp = axWebBrowser as IServiceProvider; 
      if (psp == null) throw new Exception("Could not get IServiceProvider."); 

      IntPtr oret = IntPtr.Zero; 
      int hr = psp.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out oret); 
      if ((oret == IntPtr.Zero) || (hr != HRESULT_OK)) throw new Exception("Failed to query service."); 

      ITravelLogStg tlstg = Marshal.GetObjectForIUnknown(oret) as ITravelLogStg; 
      if (null == tlstg) throw new Exception("Failed to get ITravelLogStg"); 

      hr = tlstg.TravelTo(travelLogEntry); 

      if (hr != HRESULT_OK) throw new Exception("Failed to travel to log entry with error " + hr.ToString("X")); 

      Marshal.ReleaseComObject(tlstg); 
     } 

     public static HashSet<ITravelLogEntry> GetTravelLogEntries(WebBrowser webBrowser) 
     { 
      int HRESULT_OK = 0; 

      SHDocVw.IWebBrowser2 axWebBrowser = (SHDocVw.IWebBrowser2)webBrowser.ActiveXInstance; 
      IServiceProvider psp = axWebBrowser as IServiceProvider; 
      if (psp == null) throw new Exception("Could not get IServiceProvider."); 

      IntPtr oret = IntPtr.Zero; 
      int hr = psp.QueryService(ref SID_STravelLogCursor, ref IID_ITravelLogStg, out oret); 
      if ((oret == IntPtr.Zero) || (hr != HRESULT_OK)) throw new Exception("Failed to query service."); 

      ITravelLogStg tlstg = Marshal.GetObjectForIUnknown(oret) as ITravelLogStg; 
      if (null == tlstg) throw new Exception("Failed to get ITravelLogStg"); 

      //Enum the travel log entries 
      IEnumTravelLogEntry penumtle = null; 
      tlstg.EnumEntries((int)TLMENUF.TLEF_ABSOLUTE, out penumtle); 
      hr = 0; 
      ITravelLogEntry ptle = null; 
      int fetched = 0; 
      const int MAX_FETCH_COUNT = 1; 

      hr = penumtle.Next(MAX_FETCH_COUNT, out ptle, out fetched); 
      Marshal.ThrowExceptionForHR(hr); 

      HashSet<ITravelLogEntry> results = new HashSet<ITravelLogEntry>(); 

      for (int i = 0; 0 == hr; i++) 
      { 
       if (ptle != null) results.Add(ptle); 
       hr = penumtle.Next(MAX_FETCH_COUNT, out ptle, out fetched); 
       Marshal.ThrowExceptionForHR(hr); 
      } 

      Marshal.ReleaseComObject(penumtle); 
      Marshal.ReleaseComObject(tlstg); 

      return results; 
     } 
    } 
} 
1

Por objeto javascript Location puede realizar su tarea.

<FORM><INPUT TYPE="BUTTON" VALUE="Go Back" 
ONCLICK="history.go(-1)"></FORM> 

también comprobar

JavaScript History Object

para la información de la historia

+0

Eso funciona pero implicaría contar navegaciones, que es lo que quiero evitar. – Juan

+0

@jsoldi - creo que debes verificar el objeto del doc. De la historia ... pero creo que no te permite volver sin contar la navegación –

4

probar esto!

javascript: history.go (-1)"

+1

Lo intenté. No es confiable. – Juan

+0

Lo que hice fue obtener 'history.length' antes y después y luego invocar' history.go (before - after) '. Un gran desastre. Todo esto con JavaScript inyectado por cierto. – Juan

+0

@jsoldi: No tiene que inyectar JS para hacer esto. La propiedad 'Document.Window.History.Length' en C# debería funcionar mejor. – Gabe

5

Usted puede utilizar

webBrowser1.Document.Window.History.Go(x); 

donde x es un entero que significa la posición relativa en la historia del navegador

x = -2. sería navegar dos últimas páginas

actualización:. Más información sobre HtmlHistory.Go()

+0

Por curiosidad, ¿en qué situación esta respuesta no funcionaría correctamente o cómo no produce el comportamiento deseado? Funciona para el ejemplo de Google Maps, hasta donde yo sé. ¿Realmente está esperando acceder a los datos en el mismo objeto de Historial? – Yetti

+0

No. No quiero contar navegaciones porque, ¿cómo se sabe realmente cuándo un 'DocumentComplete' cuenta como un elemento en el historial? No tengo una manera confiable de saber qué 'x 'debe ser. Sé a qué página quiero volver cuando estoy en esa página, pero una vez que navegué, no sé cuántas páginas había en el medio. – Juan

+0

¿Qué sucede cuando el navegador está configurado para no configurar el historial? – Marshal

1

El historial del navegador, por diseño, es opaco; de lo contrario, abre un agujero de seguridad: ¿realmente desea que cada página que visita tenga visibilidad sobre qué páginas/sitios ha estado visitando? Probablemente no.

Para hacer lo que quiera, deberá implementar su propia pila de URI, haciendo un seguimiento de lo que debe revisarse.

+0

Eso es lo que hago, pero se equivoca en Google Maps y algunos otros porque la URL es la misma en cada página. Pero como dije, al volver a llamar a GoBack de WebBrowser, funciona, por lo que IE hace algo más que simplemente navegar a la URL anterior.Abra lo que quiero hacer manualmente. – Juan

+0

Funciona cuando regresas a través del objeto de historial porque el navegador también está almacenando en caché los resultados. Para las páginas donde la URL permanece igual mientras que el contenido devuelto cambia (debido a llamadas AJAX o publicaciones de formulario), además del URI, es probable que deba implementar su propio caché del contenido devuelto para esa solicitud de URI particular. –

+0

¿Alguna idea de cómo hacer eso? De hecho, traté de guardar el texto de documento de la página a la que quería volver y luego navegar a esa página y luego configurar el texto de documento con el texto guardado, pero eso cambiaría la URL a un espacio en blanco y arruinaría los enlaces relativos. Creo que su solución funcionaría, pero no tenía idea de cómo implementar ese sistema de almacenamiento en caché. – Juan

0

Si no necesita ver visualmente lo que está sucediendo, es probable que haya formas más elegantes de navegar y analizar URL con las clases de WebClient, quizás la elaboración de su programa particular arrojará resultados más claros.

0

Suponiendo que tiene un control de navegador web en un formulario e intenta implementarlo, regrese.

La siguiente es la solución. (Si la suposición es incorrecta. Por favor, corríjanme)

Añadir un navegador web, cuadro de texto, como el botón btnBack

variable de historia también tiene los datos de URL para la natación (pero no se utiliza en la actualidad).

C# solución de solución

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowsFormsApplication1 
{ 
public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     WebBrowser1.Url = new Uri("http://maps.google.com"); 
    } 
    Stack< String> History = new Stack<String>(); 

    private void WebBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) 
    { 
      TextBox1.Text = e.Url.ToString(); 
      History.Push(e.Url.ToString()); 
    } 

    private void btnBack_Click(object sender, EventArgs e) 
    { 
     if(WebBrowser1.CanGoBack) 
     { 
      WebBrowser1.GoBack(); 
     } 

    } 

} 
} 

Vb

Public Class Form1 
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
    WebBrowser1.Url = New Uri("http://maps.google.com") 
End Sub 

Private Sub WebBrowser1_Navigating(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserNavigatingEventArgs) Handles WebBrowser1.Navigating 
    TextBox1.Text = e.Url.ToString 
    History.Push(e.Url.ToString) 
End Sub 
Dim History As New Stack(Of String) 
Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBack.Click 
    If WebBrowser1.CanGoBack Then 
     WebBrowser1.GoBack() 
    End If 
End Sub 

End Class 
1

Usted no desea utilizar history.go(-1) porque es poco fiable. Pero no puede usar la URL porque hay páginas como GoogleMaps donde la URL siempre es la misma.

Si la URL es la misma pero el contenido es diferente, significa que los valores para determinar el contenido de la página se extraen de otro lugar que no sea la URL.

¿Dónde podría ser esto?

Su sospechoso más probable es la colección de formularios publicada, pero los datos también podrían provenir de la cookie.

Creo que tiene más sentido indexar la ubicación absoluta que una ubicación relativa, porque, como ha notado, las ubicaciones relativas no son confiables. El problema es que necesita obtener todos los datos que se envían al servidor web, para comprender cuál es su ubicación absoluta real (porque el URI no es suficiente).

La forma de hacerlo es crear una copia local de la página y reemplazar la URL de envío (esto podría estar en un enlace, una forma o en el javascript), con una URL en su servidor. Luego, cuando haga clic en algo en la página de GoogleMaps para activar un cambio (que parece no afectar la URL), recibirá los datos en su servidor y podrá determinar la ubicación real.

Piénsalo como una cadena de consulta.

Si tengo

<form action="http://myhost.com/page.html" method="get"> 
    <input type="hidden" name="secret_location_parameter" value="mrbigglesworth" /> 
    <input type="submit" /> 
</form> 

y hacer clic en el botón de enviar, que se ocupaban de la url

http://myhost.com/page.html?secret_location_parameter=mrbigglesworth 

Sin embargo, si he

<form action="http://myhost.com/page.html" method="post"> 
    <input type="hidden" name="secret_location_parameter" value="mrbigglesworth" /> 
    <input type="submit" /> 
</form> 

y haga clic en el botón de enviar, luego me llevo a la url

http://myhost.com/page.html 

El servidor todavía recibe secret_location_parameter=mrbigglesworth, pero lo obtiene como un valor de formulario en lugar de un valor de consulta, por lo que no es visible desde la url. El servidor puede representar una página diferente según el valor secret_location_parameter, pero no cambia la URL, y si se utiliza un método de publicación, aparecerá que varias páginas residen en la misma URL.

Mi punto es que puede estar abordando el problema desde el ángulo equivocado, porque no entendía lo que sucedía debajo del capó. Ciertamente, estoy haciendo suposiciones, pero en función de la forma en que hizo su pregunta, creo que esto puede ser útil para usted

0

Agregue un marcador de forma automática al DOM para aquellas páginas a las que luego quiera volver. Al retroceder a través del historial del navegador, busque ese marcador después de cada history.go(-1) y deténgalo cuando lo encuentre. Esto podría no ser confiable en algunos casos, en cuyo caso recordar el nivel de profundidad puede servir como un enfoque de respaldo.

Es posible que deba experimentar con el momento adecuado para insertar el elemento, para asegurarse de que esté registrado correctamente en el historial.

2

sé algunas cosas que se han dicho, así que no habrá que volver a escribir, sin embargo, si realmente desea utilizar un método JavaScript (es decir: si desea utilizar el objeto de la historia Javascript en su lugar del webbrowser controla el objeto de historial) y se preguntan cómo, hay formas de hacerlo. Puede usar .InvokeScript en controles .NET WB, o si desea pre.NET & .NET compatible, puede usar esto:

Puede usar .execScript en versiones de pre.NET de control de WB y actual/Versiones .NET del control WB. También puede elegir el idioma del script que desea ejecutar, es decir: "JScript" o "VBScript". Aquí está el chiste:

WebBrowser1.Document.parentWindow.execScript "alert('hello world');", "JScript" 

Lo bueno de usar el objeto JavaScript historia es que si matas a la información del historial en el control WebBrowser enviando el número "2" en el método .navigate, ir a la página donde el historial se canceló en WB, el control no funcionará, pero funcionará en el objeto de historial de JavaScript, esto es una ventaja.

Una vez más, esto es solo un suplemento compatible con versiones anteriores de las ideas discutidas en esta publicación, incluidas algunas otras cositas no mencionadas.

Avíseme si puedo brindarle más ayuda ya que la respuesta ya fue aceptada.

Cuestiones relacionadas