#c# #web-scraping #.net-core #buttonclick #simulate
#c# #очистка веб-сайта #.net-core #buttonclick #имитировать
Вопрос:
Я очищаю веб-сайт, который выполнен в классическом asp.net . В нем есть 2 поля с идентификатором. Один — это вводимый текст, а другой — кнопка. Мне нужно заполнить поле ввода и нажать кнопку. А также получите ответ. Кнопка представляет собой тип отправки.
Я использовал HTML Agility pack. Но этого недостаточно для заполнения поля ввода и нажатия кнопки.
Пример кода: ‘
<table class="MainTable">
<tbody>
<tr>
<td class="styleIndent">amp;nbsp;</td>
<td class="Labels"><span id="ctl00_MainContent_lblLastName" class="fieldHeader" for="ctl00_MainContent_txtLastName">Name:</span></td>
<td class="styleColumnBody">
<input name="ctl00$MainContent$txtLastName" type="text" value="sberbank" maxlength="250" id="ctl00_MainContent_txtLastName" tabindex="2" title="Enter name as search criteria." style="width:200px;">
</td>
<td class="Labels"><span id="ctl00_MainContent_lblCity" class="fieldHeader" for="ctl00_MainContent_txtCity">City:</span></td>
<td class="styleColumnBody">
<input name="ctl00$MainContent$txtCity" type="text" maxlength="250" id="ctl00_MainContent_txtCity" tabindex="6" title="Enter city name as search criteria." style="width:200px;">
</td>
</tr>
<tr>
<td class="Labels"></td>
<td style="text-align: left">
<input type="submit" name="ctl00$MainContent$btnSearch" value="Search" id="ctl00_MainContent_btnSearch" tabindex="9" style="font-weight:normal;height:22px;width:96px;">amp;nbsp;amp;nbsp;
<input type="submit" name="ctl00$MainContent$btnReset" value="Reset" id="ctl00_MainContent_btnReset" tabindex="10" style="font-weight:normal;height:22px;width:96px;">
</td>
</tr>
</tbody></table>
‘
Это классика.Сеть, в которой страница перезагружается при нажатии кнопки (ctl00_MainContent_btnSearch). Поэтому трудно что-либо узнать, просмотрев страницу:
Комментарии:
1. Я думаю, что jQuery мог бы решить эту проблему довольно легко. Если у вас есть идентификаторы, вы можете просто захватить элементы по ним. Что-то вроде «$ (‘#buttonId’).Click ();» и «$ (‘#inputId’).Val (‘SOMEVAL’);»
2. @Dortimer Я не использую jQuery. Вот почему это немного сложно. Я работаю над rest API с использованием dotnet core
Ответ №1:
Html Agility Pack предназначен для анализа, запросов и манипулирования HTML DOM. Для этого можно было бы использовать какие-то сканеры. Но вы хотите фактически запустить http-запрос, javascript-событие или что-то еще, что стоит за этой кнопкой. Самый простой метод с большинством функций — удаленное управление веб-браузером.
Сначала установите Selenium и драйвер браузера. Я использую Firefox здесь, поскольку он бесплатный, с открытым исходным кодом и следит за конфиденциальностью:
Install-Package Selenium.WebDriver
Install-Package Selenium.Firefox.WebDriver
Загрузите исполняемый файл драйвера вашего браузера. Драйвер Firefox gecko можно найти на github здесь:https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-win64.zip Обзор версии, если сообщение становится старше: https://github.com/mozilla/geckodriver/releases
Теперь запустите архив и скопируйте его путь в переменную:
string geckoDriverPath = @"D:Downloadsgeckodriver-v0.24.0-win64";
Мы готовы начать использовать Firefox. Простой пример, который вводит некоторый запрос в поле поиска stackoverflow и нажимает кнопку поиска справа:
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using System;
class Program {
static void Main(string[] args) {
string geckoDriverPath = @"D:Downloadsgeckodriver-v0.24.0-win64";
using (var driver = new FirefoxDriver(geckoDriverPath)) {
driver.Navigate().GoToUrl("https://stackoverflow.com");
var searchBox = driver.FindElementByCssSelector("#search .js-search-field");
searchBox.SendKeys("Selenium");
var searchButton = driver.FindElementByCssSelector("#search .js-search-submit");
searchButton.Click();
Console.Read();
}
}
}
Пожалуйста, наберитесь терпения, инициализация браузера может занять несколько секунд.
В зависимости от того, что делает ваше нажатие кнопки, могут быть другие способы. Если это какой-то http-запрос (форма или вызов ajax), вы можете отправить его вручную. Это быстрее, экономит ресурсы, и вы можете легко запускать его без головы. Но это сложнее реализовать. Особенно на сложных страницах, где вам нужно извлекать данные, такие как идентификаторы, из источника страницы. Вы можете рассмотреть это, если вас волнует производительность и ресурсы.
Комментарии:
1. Да, я это сделал. Но я не хочу использовать selenium.
2. есть более разумный способ для path: var chrome = new ChromeDriver(Path.GetDirectoryName(Assembly. GetExecutingAssembly(). Местоположение), параметры)
3. Конечно, в зависимости от развертывания есть более разумные способы. Но короткий вопрос задает только автоматическое заполнение, что делает мой POC. Также в вопросе упоминается только, что Html Agility Pack не работает. Нет информации о том, что вы уже пробовали Selenium и почему он у вас не работает.
4. @Lion12 Да, верно, моя ошибка! Html Agility pack работает, но в данном контексте этого недостаточно. Поскольку я должен установить значение поля ввода и нажать кнопку. А также обратите внимание, что это asp.net веб-сайт. Это означает, что нет вызова rest API. И страница перезагружается при нажатии кнопки.
Ответ №2:
Если форма представляет собой стандартную HTML-форму, вы можете получить URL-адрес обратной отправки, а затем опубликовать данные формы самостоятельно. По сути, вы выполняете действие, которое обычно выполняет кнопка, вместо заполнения самой формы.
Чтобы это сработало, вам нужен URL-адрес, на который публикуется, и имя элементов, которые отправляются обратно на сервер. Вы можете легко получить это с помощью любых инструментов веб-инспектора. Как только у вас это получится, вы можете сделать следующее:
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = HttpMethod.Post.ToString();
request.ContentType = "application/json";
// replace name1, name2, value1, value2 with the
// key value pairs that need to be posted.
var content = $"{name1}={value1}amp;{name2}={value2}"
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(content);
}
request.ContentLength = content.Length;
using (var response = (HttpWebResponse)request.GetResponse())
{
var encoding = Encoding.GetEncoding(response.CharacterSet);
using (var responseStream = response.GetResponseStream())
{
using (var reader = new StreamReader(responseStream, encoding))
{
return reader.ReadToEnd();
}
}
}
Если вы используете .NET 4.5 или выше, вы можете использовать класс HttpClient, который делает это намного проще:
var httpClient = new HttpClient();
response = await httpClient.PostAsync(uri, new StringContent(data));
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
Комментарии:
1. Да, я пробовал это (HttpClient). Но я не мог этого понять. Или это не сработало. Пожалуйста, обратите внимание, что я хочу установить значение элемента по идентификатору («ctl00_MainContent_txtLastName»)
2. Может быть, мне не хватает: как настроить данные для запроса post.
3. Откройте веб-инспектор, заполните форму, а затем посмотрите на отправляемый запрос. Вам необходимо убедиться, что основное содержимое находится в формате, который он ожидает — самый простой способ сделать это — скопировать тот, который генерируется самой формой. Оба приведенных выше примера взяты из моего рабочего кода, поэтому я знаю, что они работают.
4. Привет, это asp.net кнопка. Это означает, что страница перезагружается при нажатии на нее. И на стороне сервера есть функция even. Пример кода находится в вопросе сейчас. Я отредактировал свой вопрос.
Ответ №3:
Как насчет использования безголового Chrome? вы можете перейти на веб-страницу и выполнить любую операцию по своему усмотрению.
https://github.com/kblok/puppeteer-sharp
// lauch browser and save in variable
var _browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
ExecutablePath = _config.ChromePath, // get path to chrome executable
});
// go to page
var _page = await _browser.NewPageAsync();
var page.GoToAsync("http://www.example.com");
// click on form input
await _page.ClickAsync("#name");
// set data
await _page.Keyboard.SendCharacterAsync("John");
// submit form
await _page.ClickAsync("#SubmitButton");
Комментарии:
1. @ Хорошая идея. Я пробовал это. И это работало. Я использовал selenium. Я не знаю Puppeteer. Это хорошо использовать здесь ..?
2. Да, это работает хорошо. Я использовал в своих проектах, и это работает хорошо. Я также заполнял форму и получал данные со страниц.
3. Спасибо! Но есть зависимость от исполняемого файла Chrome. У нас должен быть chrome.
4. Будет ли это работать как серверное решение (например, работающее на Azure)?
5. @NorbertKardos Да, например, у меня был один, запущенный на виртуальной машине, и я подключил к нему сокет, так что все в порядке
Ответ №4:
-
Во-первых, вам необходимо установить пакет Selenium WebDriver NuGet в свой проект. Вы можете сделать это из консоли NuGet с помощью следующей команды:
Установка-Пакет Selenium.WebDriver
-
В вашем контроллере вы можете определить действие, которое получает идентификационный номер для поиска и использует Selenium WebDriver для перехода на страницу поиска, заполнения формы и получения результатов. Вот пример того, как может выглядеть это действие:
public IActionResult Index() { var userAgent = HttpContext.Request.Headers["User-Agent"]; return View(); } public IActionResult Search(string dni) { var options = new ChromeOptions(); options.AddArgument("headless"); options.AddArgument("disable-gpu"); IWebDriver driver = new ChromeDriver(options); try { // Navegar a la página de búsqueda driver.Navigate().GoToUrl("https://eldni.com/pe/buscar-por-dni"); // Llenar el formulario con el número de DNI var inputElement = driver.FindElement(By.Name("dni")); inputElement.SendKeys(dni); // Hacer clic en el botón de búsqueda var buttonElement = driver.FindElement(By.XPath("//button[contains(@class, 'btn-success')]")); buttonElement.Click(); //// Esperar a que la página de resultados cargue completamente WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); IWebElement resultsElement = wait.Until(ExpectedConditions.ElementIsVisible(By.Id("div-copy"))); // Obtener los resultados string nombre = resultsElement.FindElement(By.Id("nombres")).GetAttribute("Value"); string apellidop = resultsElement.FindElement(By.Id("apellidop")).GetAttribute("Value"); string apellidom = resultsElement.FindElement(By.Id("apellidom")).GetAttribute("Value"); // Devolver los resultados en la vista return Json(new { Nombre = nombre, ApellidoP = apellidop, ApellidoM = apellidom }); } finally { // Cerrar el navegador driver.Quit(); } }
-
На ваш взгляд, вы можете отобразить результаты, полученные на предыдущем шаге:
$(document).ready(function () {
$("#searchButton").click(function () {
var dni = $("#dni").val();
$.ajax({
type: "POST",
url: "/Controller/Search?dni=" dni,
success: function (data) {
$("#resultado").html(
"<br><br>"
"<h3>RESULTADO</h3>"
"<table class='table-bordered table-striped' style='width: 100%' >"
"<thead><tr><th>NOMBRES</th><th>A. PATERNO</th><th>A. MATERNO</th></tr></thead>"
"<tbody><tr><td>" data.Nombre "</td><td>" data.ApellidoP "</td><td>" data.ApellidoM "</td></tr></tbody>"
"</table>"
);
}
});
});
});
<div class="form-group">
<label for="dni">DNI: </label>
<input type="number" class="form-control" id="dni" name="dni" maxlength="8" value="@Model" oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);">
</div>
<button type="button" class="btn btn-primary" id="searchButton">Consultar Datos</button>
<div id="resultado"></div>
Я надеюсь быть полезным. Приветствия