'How to capture network (XHR)? (Selenium 4)

How to capture network (XHR)? You can track the desired packs (XHR) in the Chrome (Open DevTools (F12) → Open tab “Network” → Select filter “Fetch/XHR”).

I found solutions where BrowserMob Proxy is used to capture traffic. But I wanted to use Selenium 4 which can work with CDP (Chrome DevTools Protocol).

I can successfully receive XHR packs (status 200). But I cannot get the body (getResponseBody) of some XHR packs.

namespace SeleniumCDP
{
    using System;
    using System.Collections.Concurrent;
    using System.Linq;
    using System.Threading.Tasks;
    using OpenQA.Selenium;
    using OpenQA.Selenium.Chrome;
    using OpenQA.Selenium.DevTools;
    using DevToolsVer = OpenQA.Selenium.DevTools.V93;
 
 
    class Program
    {
        private static IWebDriver Driver = null;
        private static IDevTools Tools = null;
        private static IDevToolsSession Session = null;
        private static DevToolsVer.DevToolsSessionDomains Domains = null;
 
        private static ConcurrentBag<Task<Response>> CollectionXHR = null;
 
        public struct Response
        {
            public string RequestId { get; set; }
            public string ResponseUrl { get; set; }
            public long ResponseStatus { get; set; }
            public bool ResponseBodySuccess { get; set; }
            public string ResponseBody { get; set; }
        }
 
        private static async Task Main()
        {
            Driver = new ChromeDriver();
            Driver.Manage().Timeouts().ImplicitWait = new TimeSpan(0, 0, 10);
            Driver.Manage().Timeouts().AsynchronousJavaScript = new TimeSpan(0, 0, 30);
            Driver.Manage().Timeouts().PageLoad = new TimeSpan(0, 0, 30);
            Driver.Manage().Window.Maximize();
 
            Tools = Driver as IDevTools;
 
            Session = Tools.GetDevToolsSession();
 
            Domains = Session.GetVersionSpecificDomains<DevToolsVer.DevToolsSessionDomains>();
            await Domains.Network.Enable(new DevToolsVer.Network.EnableCommandSettings());
 
            // Create storage
            CollectionXHR = new ConcurrentBag<Task<Response>>();
 
            // Enable receiving packs
            Domains.Network.ResponseReceived += ResponseReceived;
 
            // Some actions
            Instagram("username", "password"); // !!!
 
            // Disable receiving packs
            Domains.Network.ResponseReceived -= ResponseReceived;
 
            // Waiting for completion
            Task.WaitAll(CollectionXHR.ToArray());
 
            // Number of failures
            var failResponseBody = CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).Count();
 
            // Info
            string log = string.Empty;
            foreach (var i in CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).ToList())
            {
                log += $"RequestId = {i.Result.RequestId} | "; ;
                log += $"ResponseStatus = {i.Result.ResponseStatus} | ";
                log += $"ResponseBodySuccess = {i.Result.ResponseBodySuccess} | ";
                log += $"ResponseBody = {i.Result.ResponseBody} \n";
            }
            
            
        }
 
        private static void ResponseReceived(object sender, DevToolsVer.Network.ResponseReceivedEventArgs e)
        {
            if (e.Type == DevToolsVer.Network.ResourceType.XHR)
            {
                CollectionXHR.Add(GetResponseBodyAsync(e));
            }
        }
 
        private static async Task<Response> GetResponseBodyAsync(DevToolsVer.Network.ResponseReceivedEventArgs e)
        {
            try
            {
                var cmd = new DevToolsVer.Network.GetResponseBodyCommandSettings();
                cmd.RequestId = e.RequestId;
 
                var data = await Domains.Network.GetResponseBody(cmd);
 
                return new Response()
                {
                    RequestId = e.RequestId,
                    ResponseUrl = e.Response.Url,
                    ResponseStatus = e.Response.Status,
                    ResponseBodySuccess = true,
                    ResponseBody = data.Body
                };
            }
            catch (Exception ex)
            {
                return new Response()
                {
                    RequestId = e.RequestId,
                    ResponseUrl = e.Response.Url,
                    ResponseStatus = e.Response.Status,
                    ResponseBodySuccess = false,
                    ResponseBody = $"{ex.GetType()}: {ex.Message}"
                };
            }
        }
 
        private static void Instagram(string username, string password)
        {
            // Go to Instagram
            Driver.Navigate().GoToUrl("https://www.instagram.com/");
 
            // Login
            {
                var byUsernameInput = By.XPath("//form[@id='loginForm']//input[@name='username']");
                var byPasswordInput = By.XPath("//form[@id='loginForm']//input[@name='password']");
                var byLoginButton = By.XPath("//form[@id='loginForm']//button[@type='submit']");
 
                if (Driver.FindElements(byLoginButton).Count > 0)
                {
                    Driver.FindElement(byUsernameInput).SendKeys(username);
                    Driver.FindElement(byPasswordInput).SendKeys(password);
                    Driver.FindElement(byLoginButton).Click();
                }
            }
 
            // Go to direct
            {
                var byLink = By.XPath("//a[@href='/direct/inbox/']");
 
                if (Driver.FindElements(byLink).Count > 0)
                {
                    Driver.FindElement(byLink).Click();
                }
            }
        }
    }
}

If you run my code several times, you will find that the failResponseBody is greater than 0. Exceptions contain one of the following messages:

  • Network.getResponseBody: No resource with given identifier found
  • Network.getResponseBody: No data found for resource with given identifier

What am I doing wrong?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source