2011-03-24 25 views
11

我正在使用網絡抓取工具,有時需要記住特定頁面,然後轉到其他頁面,然後返回到該頁面。目前,我只保存網頁的網址,但對於Google地圖等網頁無效,網址始終相同。如何「手動」返回WebBrowser?

我可以看到,GoBack方法確實會回到上一頁,因此WebBrowser不知怎的記得以前的頁面是什麼。我如何手動執行此操作?我可以統計自從我想返回的頁面以來訪問了多少頁面,然後根據需要多次撥打GoBack,但這非常不可靠,不夠優雅。所以我想知道我怎麼能實現一個GoBackToAParticularPage方法。

有一點我認爲會讓我更接近解決方案:保存所有框架的URL,然後在返回該頁面時將其放回。我認爲這將解決Google Maps問題。我還沒有測試過。我不確切知道這是否是正確的做法。在設置網址之前,我需要等待幀的存在。

回答

0

如果其他人可以從中受益,這裏是我如何最終做到這一點。唯一需要注意的是,如果旅行日誌中間有太多頁面,則條目可能不再存在。可能有一種方法可以增加歷史記錄大小,但由於必須有一些限制,因此我使用TravelLog.GetTravelLogEntries方法來查看條目是否仍存在,如果不存在,請使用URL。

此代碼大部分來自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

通過javascript Location對象,你可能會達到你的任務。

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

還要檢查

JavaScript History Object

的歷史信息

+0

這有效,但它意味着計數導航,這是我想要避免的。 – Juan 2011-03-24 06:22:13

+0

@jsoldi - 我認爲你需要檢查歷史對象的文檔..但我認爲它不允許你回去而不計算導航 – 2011-03-24 06:28:23

4

試試這個!

的javascript:history.go(-1)」

+1

我試過了。不可靠。 – Juan 2011-03-28 05:26:57

+0

我所做的是在獲取'history.length'前後調用'history.go(before - after)'。一團糟。所有這些與注入JavaScript btw。 – Juan 2011-03-28 05:40:45

+0

@jsoldi:你不必注入JS來做到這一點。 C#中的Document.Window.History.Length屬性應該更好。 – Gabe 2011-04-03 07:18:54

5

您可以使用

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

其中x是一個int標誌着在瀏覽器歷史上的相對位置

X = -2。將導航回兩頁

更新:更多信息在HtmlHistory.Go()

+0

出於好奇,在什麼情況下這個答案不能正常工作,或者它不會產生所需的行爲?據我所知,它適用於Google地圖示例。你真的希望訪問歷史對象本身的數據嗎? – Yetti 2011-03-30 12:36:26

+0

不可以。我不想計算導航,因爲您如何知道「Do​​cumentComplete」何時被視爲歷史記錄中的項目?我沒有一個可靠的方法來知道'x'應該是什麼。當我在那個頁面時,我知道我想回到哪個頁面,但是一旦它離開了,我不知道它們之間有多少頁面。 – Juan 2011-04-01 05:44:01

+0

當瀏覽器設置爲不設置歷史記錄時會發生什麼? – Marshal 2011-04-02 08:43:18

1

按設計瀏覽器歷史記錄是不透明的;否則會打開一個安全漏洞:你真的想讓你訪問的每一個頁面都能看到你訪問過的頁面/網站嗎?可能不會。

要做你想做的事情,你需要實現自己的URI堆棧,跟蹤需要重新訪問的內容。

+0

這就是我所做的,但它在Google地圖和其他一些地方混淆了,因爲每個頁面上的URL都是一樣的。但正如我所說,當通過調用WebBrowser的GoBack返回時,它確實可以工作,所以IE除了導航到前一個URL之外還有其他一些功能。 – Juan 2011-03-29 23:01:27

+0

當您通過歷史記錄對象返回時,它會起作用,因爲瀏覽器也在緩存結果。對於在返回的內容發生更改(由於AJAX調用或表單帖子)而URL保持不變的頁面中,除了URI之外,您可能需要實現自己的針對該URI請求返回的內容緩存。 – 2011-03-29 23:05:54

+0

任何想法如何做到這一點?實際上我嘗試保存我想返回的頁面的DocumentText,然後導航到該頁面,然後使用保存的文本設置DocumentText,但這會將URL更改爲大約空白並弄亂相關鏈接。我認爲你的解決方案可行,但不知道如何實現該緩存系統。 – Juan 2011-03-29 23:47:55

0

如果您不需要直觀地看到發生了什麼事情,那麼使用WebClient類瀏覽和解析URL可能會有更優雅的方式,也許詳細闡述您的特定程序會產生更清晰的結果。

0

假設您在窗體上有一個webbrowser控件,並且您正試圖執行回去。

以下是解決方案。 (如果假設是錯誤的。請指正)

添加一個網頁瀏覽器,文本框,按鈕btnBack

歷史變量也有導航的URL數據(但當前未使用)。

C#解決方案

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

你不想使用history.go(-1),因爲它是不可靠的。但是,您無法使用網址,因爲有像GoogleMaps這樣的網址,網址始終相同。

如果URL相同但內容不同,則表示確定頁面內容的值正從URL以外的地方拉出。

這可能是哪裏?

您最有可能的嫌疑是張貼的表單集合,但數據也可能來自cookie。

我認爲索引絕對位置比相對位置更有意義,因爲如您所述,相對位置可能不可靠。問題是你需要獲取所有發送到Web服務器的數據,以瞭解它的實際絕對位置(因爲URI不夠)。

要做到這一點的方法是創建頁面的本地副本,並將您的服務器上的URL替換爲提交url(這可能在鏈接,表單或javascript中)。然後,當您點擊GoogleMaps頁面上的某個內容來觸發更改(似乎不會影響網址)時,您將在服務器上收到該數據,並且可以確定實際位置。

想想它就像查詢字符串。

如果我有

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

,我點擊提交按鈕,我得到帶到網址

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

但是,如果我有

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

,我點擊提交按鈕,然後我將轉到網址

http://myhost.com/page.html 

服務器仍然收到secret_location_parameter=mrbigglesworth,但它將它作爲表單值而不是查詢字符串值,因此它從URL中不可見。服務器可能會根據secret_location_parameter值呈現不同的頁面,但不會更改網址,並且如果使用post方法,則會顯示多個網頁位於同一個網址。

我的觀點是,你可能從錯誤的角度來解決問題,因爲你不明白髮生了什麼。我當然在做出假設,但根據您提出問題的方式,我認爲這可能對您有所幫助

0

以編程方式將標記元素添加到您稍後要返回到的那些頁面的DOM中。當通過瀏覽器歷史回溯時,請在每個history.go(-1)之後檢查該標記,並在遇到它時停止。在某些情況下,這可能證明不可靠,在這種情況下,記住深度級別可以作爲備用方法。

您可能需要嘗試插入元素的正確時間,以確保它正確記錄在歷史記錄中。

2

我知道一些事情已經說了,所以我也不會,重新寫,但是如果你真的想用一個JavaScript方法(即:如果你想使用JavaScript的歷史對象,而不是的webbrowser控制歷史對象),並想知道如何,有辦法做到這一點。您可以在.NET WB控件中使用.InvokeScript,或者如果您想兼容.NET,您可以使用以下代碼:

您可以在.net控件和current/.NET版本的WB控制。您還可以選擇要執行的腳本的語言,即:「JScript」或「VBScript」。這裏是一個班輪:

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

有關使用JavaScript的歷史對象的好處是,如果你通過發送數字「2」到.navigate方法,去殺死頁WebBrowser控件中的歷史信息在WB控制中取消歷史記錄將不起作用,但它將在JavaScript的歷史對象中起作用,這是一個優勢。

再次,這只是一個向後兼容的補充,已經在這篇文章討論的想法,包括其他一些沒有提到的花絮。

讓我知道如果我可以爲你提供進一步的幫助,因爲答案已經被接受。