146 lines
5.2 KiB
C#
146 lines
5.2 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using Newtonsoft.Json;
|
|||
|
using OpenQA.Selenium;
|
|||
|
using OpenQA.Selenium.Firefox;
|
|||
|
using OpenQA.Selenium.Support.UI;
|
|||
|
|
|||
|
namespace GtaVUsersInfo.Helpers
|
|||
|
{
|
|||
|
public class Car
|
|||
|
{
|
|||
|
public string Name { get; set; }
|
|||
|
public int Price { get; set; }
|
|||
|
public string Photo { get; set; }
|
|||
|
public string Manufacturer { get; set; }
|
|||
|
public string Class { get; set; }
|
|||
|
public string Model { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class CarParser : IDisposable
|
|||
|
{
|
|||
|
private IWebDriver driver;
|
|||
|
|
|||
|
public CarParser()
|
|||
|
{
|
|||
|
var chromeOptions = new FirefoxOptions();
|
|||
|
chromeOptions.AddArgument("--no-sandbox");
|
|||
|
driver = new FirefoxDriver(chromeOptions);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<List<Car>> ParseCarsFromPages(int maxPages)
|
|||
|
{
|
|||
|
var cars = new List<Car>();
|
|||
|
for (int page = 1; page <= maxPages; page++)
|
|||
|
{
|
|||
|
var url = $"https://gtacars.net/?page={page}";
|
|||
|
var pageCars = await ParseCarsFromPage(url);
|
|||
|
cars.AddRange(pageCars);
|
|||
|
}
|
|||
|
return cars;
|
|||
|
}
|
|||
|
|
|||
|
private async Task<List<Car>> ParseCarsFromPage(string url)
|
|||
|
{
|
|||
|
var cars = new List<Car>();
|
|||
|
|
|||
|
// Открываем страницу в браузере
|
|||
|
driver.Navigate().GoToUrl(url);
|
|||
|
|
|||
|
// Ожидание, пока динамические элементы не подгрузятся
|
|||
|
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
|
|||
|
// Ждём, пока появится хотя бы один элемент с ценой
|
|||
|
wait.Until(drv => drv.FindElements(By.CssSelector("span.v-popper--has-tooltip")).Any(e => e.Text.Contains("$")));
|
|||
|
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
|
|||
|
Thread.Sleep(100);
|
|||
|
js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight / 4);");
|
|||
|
Thread.Sleep(100);
|
|||
|
js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight / 3);");
|
|||
|
Thread.Sleep(100);
|
|||
|
js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight / 2);");
|
|||
|
Thread.Sleep(100);
|
|||
|
js.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
|
|||
|
Thread.Sleep(500);
|
|||
|
|
|||
|
wait.Until(drv => drv.FindElements(By.CssSelector("img[src*='data:image/gif']")).Count == 0);
|
|||
|
|
|||
|
// После этого можно парсить элементы
|
|||
|
var carElements = driver.FindElements(By.CssSelector("div.relative.flex.flex-col"));
|
|||
|
foreach (var carElement in carElements)
|
|||
|
{
|
|||
|
var car = new Car();
|
|||
|
|
|||
|
// Имя автомобиля
|
|||
|
var nameElement = carElement.FindElement(By.CssSelector("span[title]"));
|
|||
|
car.Name = nameElement.Text;
|
|||
|
|
|||
|
// Цена автомобиля
|
|||
|
var priceElement = carElement.FindElement(By.CssSelector("span.v-popper--has-tooltip"));
|
|||
|
var priceText = priceElement.Text;
|
|||
|
car.Price = ParsePrice(priceText);
|
|||
|
|
|||
|
// Фото
|
|||
|
var photoElement = carElement.FindElement(By.CssSelector("img.w-full"));
|
|||
|
car.Photo = photoElement.GetAttribute("src");
|
|||
|
|
|||
|
// Производитель
|
|||
|
var manufacturerElement = carElement.FindElement(By.CssSelector("a[href*='filter_manufacturer']"));
|
|||
|
car.Manufacturer = manufacturerElement.Text;
|
|||
|
|
|||
|
// Класс автомобиля
|
|||
|
var classElement = carElement.FindElement(By.CssSelector("a[href*='filter_class']"));
|
|||
|
car.Class = classElement.Text;
|
|||
|
|
|||
|
// Модель (часть URL)
|
|||
|
var modelElement = carElement.FindElement(By.CssSelector("a[href]"));
|
|||
|
car.Model = modelElement.GetAttribute("href").Split('/').Last();
|
|||
|
|
|||
|
cars.Add(car);
|
|||
|
}
|
|||
|
|
|||
|
return cars;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
private int ParsePrice(string priceText)
|
|||
|
{
|
|||
|
if (string.IsNullOrWhiteSpace(priceText)) return 0;
|
|||
|
priceText = priceText.Replace("$", "").Replace(",", "").Replace(" ", "");
|
|||
|
return int.TryParse(priceText, out int price) ? price : 0;
|
|||
|
}
|
|||
|
|
|||
|
public void SaveToJson(List<Car> cars, string filePath)
|
|||
|
{
|
|||
|
var json = JsonConvert.SerializeObject(cars, Formatting.Indented);
|
|||
|
File.WriteAllText(filePath, json);
|
|||
|
}
|
|||
|
|
|||
|
// Реализация метода Dispose
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
if (driver != null)
|
|||
|
{
|
|||
|
driver.Quit();
|
|||
|
driver.Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class Program
|
|||
|
{
|
|||
|
static async Task Main(string[] args)
|
|||
|
{
|
|||
|
using (var parser = new CarParser())
|
|||
|
{
|
|||
|
var cars = await parser.ParseCarsFromPages(25);
|
|||
|
parser.SaveToJson(cars, "cars.json");
|
|||
|
Console.WriteLine("Data saved to cars.json");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|