Работа с трафиком (Fetch/XHR) при помощи Selenium (4) WebDriver? (пример внутри)

Привет! Для реализации моей задачи необходимо разбирать данные входящего трафика, а точнее XHR данные, которые можно увидеть в браузере Chrome → Открыть DevTools (F12) → Вкладка Network → Фильтр Fetch/XHR. Я видел примеры, где для перехвата трафика используют BrowserMob Proxy, но также увидел, что Selenium 4 может напрямую работать с CDP (Chrome DevTools Protocol), которому отдал предпочтение.

У меня возникла проблема – не у всех успешно полученных XHR пакетов (статус 200) выходит успешно извлечь тело getResponseBody. Возможно, я делаю что-то не так.

Ниже я выложил рабочий пример, который заходит в Instagram и записывает XHR пакеты. По окончанию всех действий, смотрим количество неудачно извлеченных тел XHR пакетов (обычно от 1 до 3). Подчеркну, что всех XHR пакеты имеют статус 200, поэтому не понимаю в чем проблема получения данных из них.

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());

            // Подготавливаем хранилище данных
            CollectionXHR = new ConcurrentBag<Task<Response>>();

            // Включаем запись получаемых данных
            Domains.Network.ResponseReceived += ResponseReceived;

            // Какие-то действия на сайте
            Instagram("username", "password"); // ! ввести логин и пароль

            // Отключаем запись получаемых данных
            Domains.Network.ResponseReceived -= ResponseReceived;

            // Ожидаем завершение извлечения полученных данных
            Task.WaitAll(CollectionXHR.ToArray());

            // Количество неудачных извлечений полученных данных 
            var failResponseBody = CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).Count();

            // Выводим данные неудачных извлечений полученных данных 
            string failDescr = string.Empty;
            foreach (var i in CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).ToList())
            {
                failDescr += $"RequestId = {i.Result.RequestId} | ResponseStatus = {i.Result.ResponseStatus} | ResponseBodySuccess = {i.Result.ResponseBodySuccess} \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
            {
                return new Response()
                {
                    RequestId = e.RequestId,
                    ResponseUrl = e.Response.Url,
                    ResponseStatus = e.Response.Status,
                    ResponseBodySuccess = false,
                    ResponseBody = null
                };
            }
        }

        private static void Instagram(string username, string password)
        {
            // Переходим на instagram.com
            Driver.Navigate().GoToUrl("https://www.instagram.com/");

            // Вводим логин и пароль
            {
                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();
                }
            }

            // Отказываемся от сохранения данных для входа
            {
                var byNotAutoLoginButton = By.XPath("//button[contains(text(),'Не сейчас')]");

                if (Driver.FindElements(byNotAutoLoginButton).Count > 0)
                {
                    Driver.FindElement(byNotAutoLoginButton).Click();
                }
            }

            // Отказываемся от включения уведомлений
            {
                var byNotNotifyButton = By.XPath("//button[contains(text(),'Не сейчас')]");

                if (Driver.FindElements(byNotNotifyButton).Count > 0)
                {
                    Driver.FindElement(byNotNotifyButton).Click();
                }
            }

            // Переходим в директ
            {
                var byMessengerLink = By.XPath("//a[@href='/direct/inbox/']");

                if (Driver.FindElements(byMessengerLink).Count > 0)
                {
                    Driver.FindElement(byMessengerLink).Click();
                }
            }
        }
    }
}

Никто не работал с CDP в Selenim 4 (Java, Python)?

наверняка кто-то работал, но мне ваш пример на шарпе завести не удалось, постоянно null везде

Пожалуйста, напишите более подробно где null.

  1. Создать консольное приложение (проект) в VS [ConsoleApp1]
  2. Через NuGet подключить Selenium.WebDriver 4.0.0-rc2
  3. Скачать Chrome Driver v94 и закинуть его в папку с проектом [*ConsoleApp1\bin\Debug\net5.0*]
  4. Заменить код в файле Program.cs на мой, не забыв указать свой логин и пароль в Instagram
  5. Установить точку останов в конце функции Main (напр. на строке var failResponseBody)
  6. Готово к запуску.

Так как обновился пакет Selenium.WebDriver до 4.0.0-rc2, я обновил пример кода:

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.V94;


    class Program
    {
        private static string Username = "Username123";
        private static string Password = "Password123";

        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());

            // Подготавливаем хранилище данных
            CollectionXHR = new ConcurrentBag<Task<Response>>();

            // Включаем запись получаемых данных
            Domains.Network.ResponseReceived += ResponseReceived;

            // Какие-то действия на сайте
            Instagram(Username, Password);

            // Отключаем запись получаемых данных
            Domains.Network.ResponseReceived -= ResponseReceived;

            // Ожидаем завершение извлечения полученных данных
            Task.WaitAll(CollectionXHR.ToArray());

            // Количество неудачных извлечений полученных данных 
            var failResponseBody = CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).Count();

            // Выводим данные неудачных извлечений полученных данных 
            string failDescr = string.Empty;
            foreach (var i in CollectionXHR.Where(w => w.Result.ResponseBodySuccess == false).ToList())
            {
                failDescr += $"RequestId = {i.Result.RequestId} | ResponseStatus = {i.Result.ResponseStatus} | ResponseBodySuccess = {i.Result.ResponseBodySuccess} \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
            {
                return new Response()
                {
                    RequestId = e.RequestId,
                    ResponseUrl = e.Response.Url,
                    ResponseStatus = e.Response.Status,
                    ResponseBodySuccess = false,
                    ResponseBody = null
                };
            }
        }

        private static void Instagram(string username, string password)
        {
            // Переходим на instagram.com
            Driver.Navigate().GoToUrl("https://www.instagram.com/");

            // Вводим логин и пароль
            {
                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();
                }
            }

            // Отказываемся от сохранения данных для входа
            {
                var byNotAutoLoginButton = By.XPath("//button[contains(text(),'Не сейчас')]");

                if (Driver.FindElements(byNotAutoLoginButton).Count > 0)
                {
                    Driver.FindElement(byNotAutoLoginButton).Click();
                }
            }

            // Отказываемся от включения уведомлений
            {
                var byNotNotifyButton = By.XPath("//button[contains(text(),'Не сейчас')]");

                if (Driver.FindElements(byNotNotifyButton).Count > 0)
                {
                    Driver.FindElement(byNotNotifyButton).Click();
                }
            }

            // Переходим в директ
            {
                var byMessengerLink = By.XPath("//a[@href='/direct/inbox/']");

                if (Driver.FindElements(byMessengerLink).Count > 0)
                {
                    Driver.FindElement(byMessengerLink).Click();
                }
            }
        }
    }
}

@MrKamaBullet05 можно два вопроса -

  1. чего именно хотите добиться ?
  2. почему именно на c# ? а не (например) на джава
    взял wikipedia страницу стал мышью водить получил много XHR вызовов
    попробовал всякие методы и события Fetch и после того как получил responseBody он
    в общеи похож на base64 строку но на самом деле нет…
    точнее некоторые дают осмысленный текст но зваершаются invalid input
    код сырой (написан из спортивного интереса) могу показать если кому интересно
    на Java 8

кстати ваш код это случайно не реплика вот этого поста ?

mrkamab...@gmail.com

unread,

Sep 28, 2021, 5:39:20 AM

to Selenium Users

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.
https://groups.google.com/g/selenium-users/c/OueDjaEqp2U

Здравствуйте!
Есть какое то другое решение для логирования все входящих/исходящих запросов через selenium c# ? Т.к. через текущее решение большинство запросов попросту не логируется(они там отсутствуют, хотя в devtools они есть).
Может есть какое то решение выгружать весь HAR после проведения манипуляций с Driver.Navigate() ?

было и до Selenium 4.x через джаваскрипт

var performance = window.performance;
var timings = performance.timing;
return timings;

для XHR специально не смотрел

Selenium 4 XHR Java работает нормально помоему

Здравствуйте Сергей, прошу прощения, не могли бы вы привести пример как это все воплотить на C# ?
А то я не силен в этом, только учусь работать с selenium и никак не могу разобратся(
Цель : допустим мы сделали запрос Driver.Navigate().GoToUrl(“https://localhost”);
Вопрос: как сохранить весь HAR из devtools (включая все данные, запросы/ответы/хидеры и тд)?

Я делал так:

ChromeOptions options = new ChromeOptions();
options.SetLoggingPreference("performance", LogLevel.All);
***запросы***
var logs = Driver.Manage().Logs.GetLog("performance");
            for (int i = 0; i < logs.Count; i++)
            {
                Write_Succeeded(logs[i].Message);
            }

Да я получаю запросы, НО я не вижу ответы от сервера Response.body
Как все это можно реализовать ?

много несколько лет назад работал с page perfomance.
это не весь хар только время:
для хрома

кстати но это только мое мнение просьба дать все и сразу кажется неуместной практически в 100% случаев

пример на джава 8
активизируем Fetch

открываем википедию напр XMLHttpRequest - Wikipedia
мышкой проводим над ссыоками

слушаем все XHR на которые пришел ответ
печатаем заголовки запроса и ответа и тело:
лог

in Fetch.requestPaused 
listener. id:interception-job-29.0	
URL: none	
request headers:
content-type: application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
cache-control: s-maxage=1209600
max-age=300, content-language: en
vary: Accept-Encoding
content-location: https://en.wikipedia.org/api/rest_v1/page/summary/XHR-FM
access-control-allow-origin: *
access-control-allow-methods: GET,HEAD
access-control-allow-headers: accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding
access-control-expose-headers: etag
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
referrer-policy: origin-when-cross-origin
x-xss-protection: 1; mode=block
content-security-policy: default-src 'none'; frame-ancestors 'none'
x-content-security-policy: default-src 'none'; frame-ancestors 'none'
x-webkit-csp: default-src 'none'; frame-ancestors 'none'
server: restbase1028, date: Tue, 12 Oct 2021 15:59:30 GMT
etag: W/"1040156904/4cb79470-2b6a-11ec-8ab9-25ccec7ad2ca"
content-encoding: gzip
age: 37981
x-cache: cp1089 hit, cp1089 hit/11
x-cache-status: hit-front
server-timing: cache;desc="hit-front", host;desc="cp1089"
strict-transport-security: max-age=106384710; includeSubDomains; preload
report-to: { "group": "wm_nel", "max_age": 86400, "endpoints": [{ "url": "https://intake-logging.wikimedia.org/v1/events?stream=w3c.reportingapi.network_error&schema_uri=/w3c/reportingapi/network_error/1.0.0" }] }, nel: { "report_to": "wm_nel", "max_age": 86400, "failure_fraction": 0.05, "success_fraction": 0.0}
permissions-policy: interest-cohort=()
x-client-ip: 69.138.102.136, 
accept-ranges: bytes
content-length: 614
response status: 200
response headers:
date: Wed, 13 Oct 2021 01:48:31 GMT
content-location: https://en.wikipedia.org/api/rest_v1/page/summary/Hypertext_Transfer_Protocol
cache-control: s-maxage=1209600
max-age=300
vary: Accept-Encoding
server: ATS/8.0.8, etag: W/"1049481372/8c828e80-2b05-11ec-86dd-8f0f28473208"
content-type: application/json; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/Summary/1.5.0"
content-language: en
access-control-allow-origin: *
access-control-allow-methods: GET,HEAD
access-control-allow-headers: accept, content-type, content-length, cache-control, accept-language, api-user-agent, if-match, if-modified-since, if-none-match, dnt, accept-encoding
access-control-expose-headers: etag
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN, referrer-policy: origin-when-cross-origin
x-xss-protection: 1; mode=block, content-security-policy: default-src 'none'; frame-ancestors 'none'
x-content-security-policy: default-src 'none'; frame-ancestors 'none'
x-webkit-csp: default-src 'none'; frame-ancestors 'none'
content-encoding: gzip, age: 2642
x-cache: cp1079 hit, cp1089 hit/13
x-cache-status: hit-front, server-timing: cache;desc="hit-front", host;desc="cp1089"
strict-transport-security: max-age=106384710; includeSubDomains; preload
report-to: { "group": "wm_nel", "max_age": 86400, "endpoints": [{ "url": "https://intake-logging.wikimedia.org/v1/events?stream=w3c.reportingapi.network_error&schema_uri=/w3c/reportingapi/network_error/1.0.0" }] }
nel: { "report_to": "wm_nel", "max_age": 86400, "failure_fraction": 0.05, "success_fraction": 0.0}
permissions-policy: interest-cohort=()
x-client-ip: 69.138.102.136
accept-ranges: bytes 
content-length: 773
response body:
{
  "type": "standard",
  "title": "Hypertext Transfer Protocol",
  "displaytitle": "Hypertext Transfer Protocol",
  "namespace": {
    "id": 0,
    "text": ""
  },
  "wikibase_item": "Q8777",
  "titles": {
    "canonical": "Hypertext_Transfer_Protocol",
    "normalized": "Hypertext Transfer Protocol",
    "display": "Hypertext Transfer Protocol"
  },
  "pageid": 13443,
  "thumbnail": {
    "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/HTTP_logo.svg/320px-HTTP_logo.svg.png",
    "width": 320,
    "height": 171
  },
  "originalimage": {
    "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/HTTP_logo.svg/512px-HTTP_logo.svg.png",
    "width": 512,
    "height": 274
  },
  "lang": "en",
  "dir": "ltr",
  "revision": "1049481372",
  "tid": "8c22e250-2b05-11ec-8e0e-5b88d058b70b",
  "timestamp": "2021-10-12T02:38:47Z",
  "description": "Application protocol for distributed, collaborative, hypermedia information systems",
  "description_source": "local",
  "content_urls": {
    "desktop": {
      "page": "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol",
      "revisions": "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol?action=history",
      "edit": "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol?action=edit",
      "talk": "https://en.wikipedia.org/wiki/Talk:Hypertext_Transfer_Protocol"
    },
    "mobile": {
      "page": "https://en.m.wikipedia.org/wiki/Hypertext_Transfer_Protocol",
      "revisions": "https://en.m.wikipedia.org/wiki/Special:History/Hypertext_Transfer_Protocol",
      "edit": "https://en.m.wikipedia.org/wiki/Hypertext_Transfer_Protocol?action=edit",
      "talk": "https://en.m.wikipedia.org/wiki/Talk:Hypertext_Transfer_Protocol"
    }
  },
  "extract": "The Hypertext Transfer Protocol (HTTP) is an application layer protocol in the Internet protocol suite model for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web, where hypertext documents include hyperlinks to other resources that the user can easily access, for example by a mouse click or by tapping the screen in a web browser.",
  "extract_html": "<p>The <b>Hypertext Transfer Protocol</b> (<b>HTTP</b>) is an application layer protocol in the Internet protocol suite model for distributed, collaborative, hypermedia information systems. HTTP is the foundation of data communication for the World Wide Web, where hypertext documents include hyperlinks to other resources that the user can easily access, for example by a mouse click or by tapping the screen in a web browser.</p>"
}


код

	@Before
	public void beforeTest() throws Exception {

		List<RequestPattern> reqPattern = new ArrayList<>();
		RequestPattern xhrReqPattern = new RequestPattern(Optional.of("*"),
				Optional.of(ResourceType.XHR), Optional.of(RequestStage.RESPONSE));

		reqPattern.add(xhrReqPattern);
		chromeDevTools
				.send(Fetch.enable(Optional.of(reqPattern), Optional.of(false)));
	}

	@After
	public void afterTest() throws Exception {
		chromeDevTools.send(Fetch.disable());
	}

	@Test
	public void test() {
		// Arrange
		try {
			chromeDevTools.addListener(Fetch.requestPaused(), event -> {
				try {

					List<HeaderEntry> headerEntries = event.getResponseHeaders()
							.isPresent() ? event.getResponseHeaders().get()
									: new ArrayList<>();

					List<String> headers = headerEntries.stream().map(entry -> String
							.format("%s: %s", entry.getName(), entry.getValue()))
							.collect(Collectors.toList());

					System.err.println("in Fetch.requestPaused listener. id:"
							+ event.getRequestId().toString() + "\tURL: "
							+ (event.getRequest().getUrlFragment().isPresent()
									? event.getRequest().getUrlFragment().get() : "none")
							+ "\trequest headers: " + event.getRequest().getHeaders()
							+ "\response status: " + event.getResponseStatusCode().get()
							+ "\tresponse headers: "
							+ (event.getResponseHeaders().isPresent() ? headers : "none")
							+ "\tresource type: " + event.getResourceType());
					// always empty
					event.getRequest().getPostData().ifPresent((data) -> {
						System.err.println("Post Data:\n" + data + "\n");
					});

					Fetch.GetResponseBodyResponse response = chromeDevTools
							.send(Fetch.getResponseBody(event.getRequestId()));
					try {
						String decodedBody = new String(
								Base64.decodeBase64(response.getBody().getBytes("UTF8")));
						System.err.println("response body:\n" + decodedBody + "\n");
					} catch (Exception e) {
						System.err.println("Exception (ignored): " + e.toString());
					}
					chromeDevTools.send(
							Fetch.continueRequest(event.getRequestId(), Optional.empty(),
									Optional.empty(), Optional.empty(), Optional.empty()));
				} catch (DevToolsException e) {
					System.err.println("Web Driver exception (ignored): "
							+ Utils.processExceptionMessage(e.getMessage()));

					// org.openqa.selenium.devtools.DevToolsException:
					// {"id":6,"error":{"code":-32602,"message":"Invalid
					// InterceptionId."},"sessionId":"4515310FC6FFDECA0705C54441EFD84B"}
				}
			});
			// Act
			// hover the links in the main wikipedia document
			driver.get(url);
			Utils.sleep(1000);
			List<WebElement> elements = driver.findElement(By.id("mw-content-text"))
					.findElements(By.tagName("a"));
			actions = new Actions(driver);
			elements.stream().limit(count).forEach(element -> {
				actions.moveToElement(element).build().perform();
				Utils.sleep(1000);
			});
		} catch (WebDriverException e) {
			System.err.println("Web Driver exception (ignored): "
					+ Utils.processExceptionMessage(e.getMessage()));
		} catch (Exception e) {
			System.err.println("Exception: " + e.toString());
			throw (new RuntimeException(e));
		}
	}


это один из тестов опубликован на

  1. чего именно хотите добиться ?

Получить все входящие сообщения XHR, так как в них больше информации чем отображается на странице.

почему именно на c# ? а не (например) на джава

Против Java ничего не имею, но мой основной язык разработки это C#.

кстати ваш код это случайно не реплика вот этого поста ? (https://groups.google.com/g/selenium-users/c/OueDjaEqp2U)

Да, это мой пост, уже начал спрашивать на иностранных ресурсах, но ответа пока нет.

активизируем Fetch

Возможно дело в этом - я в своем примере не касаюсь/использую Fetch. Вы, получаете данные подписавшись на событие Fetch.requestPaused, а я на событие Network.responseReceived

Сейчас рассмотрю ваш код на Java подробнее :+1:

у org.openqa.selenium.devtools.v93.network.model.DataReceived
нету нужной информации

# Fired when data chunk was received over the network.
  event dataReceived
    parameters
      # Request identifier.
      RequestId requestId
      # Timestamp.
      MonotonicTime timestamp
      # Data chunk length.
      integer dataLength
      # Actual bytes received (might be less than dataLength for compressed encodings).
      integer encodedDataLength

у org.openqa.selenium.devtools.v93.network.model.DataReceived

Не совсем понял причем здесь DataReceived, я использовал для получения XHR событие Network.responseReceived, брал из него ид requestId и получал тело этих данных (см. мой метод GetResponseBodyAsync). Пакеты XHR я получал успешно (код 200), но вот тела этих пакетов не всегда выходило извлекать, в этом и кроется проблема, возможно нужно дожидаться еще какого-то события или же возможно все и так верно.

Сейчас “перекладываю” ваш код на C# (ипользую Fetch.requestPaused). Как будут какие-то результаты, то отпишусь здесь.

З.Ы.
На днях вышел Selenium 4, поэтому уже не V93, а V95.

у import org.openqa.selenium.devtools.v94.network.model.ResponseReceived

https://github.com/SeleniumHQ/selenium/blob/trunk/common/devtools/chromium/v93/browser_protocol.pdl#L5763

можно получать

			Network.GetResponseBodyResponse response = chromeDevTools
					.send(Network.getResponseBody(event.getRequestId()));
			String body = response.getBody();
			if (response.getBase64Encoded()) {
				try {
					body = new String(Base64.decodeBase64(body.getBytes("UTF8")));
				} catch (UnsupportedEncodingException e) {
					System.err.println("Exception (ignored): " + e.toString());
				}
			}
			System.err.println("response body:\n" + body);

можно получать

Да, с помощью Network.responseReceived можно получать XHR пакеты. Если посмотрите мой код, то я подписался на событие Network.responseReceived, в котором пытаюсь получить данные (см. мой метод GetResponseBodyAsync):

var cmd = new DevToolsVer.Network.GetResponseBodyCommandSettings();
cmd.RequestId = e.RequestId;
data = await Domains.Network.GetResponseBody(cmd);

Подчеркну тот факт, что извлечение “тел” (Network.getResponseBody), полученных сообщений, происходит асинхронно! Иначе, большое количество пакетов оказывается пропущено. Поэтому я подписываю свой метод ResponseReceived на событие Network.responseReceived, внутри которого вызываю асинхронный метод GetResponseBodyAsync, содержащий Network.getResponseBody, который извлекает “тело”.

И нюанс в том, что ответы Network.Response успешные (Network.Response.Status = 200), но извлечение (Network.getResponseBody)некоторых “тел” ответов вызывает ошибку. Именно поэтому, в моем методе GetResponseBodyAsync все обернуто в try-catch. Возникающие ошибки при извлечение:

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

Возможно это нормальное явление?

в джава варианте то что со стороны javascript описано как метод
https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-getResponseBody

то вызватся через прокси ответа

Network.GetResponseBodyResponse response = chromeDevTools
						.send(Network.getResponseBody(event.getRequestId()));
				String body = response.getBody();

а то что описано как событие

https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-dataReceived
вызывается через колбек для орбаботчика

	chromeDevTools.addListener(Network.dataReceived(), event -> {
		System.err
			.println(String.format("Network request %s data received at %s",
			event.getRequestId(), event.getTimestamp()));
		});
	

как написать второе напрямую через веб сокеты хотел бы сам узнать
наверное нужен poll

Попробуйте использовать domains.Network.RequestWillBeSent

1 лайк

У меня была аналогичная с автором задача и у меня не логировались нужные мне запросы. Решилось простым способом - перед отключением записи получаемых данных (Domains.Network.ResponseReceived -= ResponseReceived;) я добавил простую задержку Thread.Sleep и все мои запросы стали добавляться в коллекцию. Вдруг кому-то пригодится)
Автору спасибо!

1 лайк