AngleSharp с нуля (C#)
Имею необходимость научиться пасрить сайт без API (на самом деле API я находил, но на питоне). С# я начал изучать около 2 недель назад, так что я еще практически ничего не знаю. Я планирую что моя программа будет уметь вытаскивать определенный текст с конкретной страницы которую я укажу, помещать конкретный текст в конкретные тексБоксы, на тот случай если текст нужно подкорректировать, и затем "собирать" весь текст из разных текстБоксов в одну "многострочную строчку". Не спрашивайте зачем.
И... В общем то почти все из запланированного я сделал. Не до конца но наполовину. Я скачиваю весь сайт, регулярными выражениями вытягиваю текст, и делаю все остальные грязные дела. Но везде, во всех уроках, комментариях и советах пишут, что нельзя для этого использовать регулярные выражения, и нельзя качать весь сайт. Или API, или AngleSharp советуют. Но по AngleSharp ничего адекватного не удается найти. Или какие то абстракции, только теория, или практика на узких примерах, не подходящих мне.
И вот я забрел сюда, на это сообщение. Там много чего написано, наверняка полезного, но мне не помогло.
Во допустим у меня такой код.
async Task DownloadStringAsync()
{
WebClient source = new WebClient();
string reply = source.DownloadString(@"какая-то_страница");
Match match1 = Regex.Match(reply, "<h1 itemprop=\"name\" id=\"work_name\">(.*?)</h1>");
EngNameTextBox.Text = match1.Groups[1].Value;
}
Он парсит нужный мне текст
<h1 itemprop="name" id="work_name">Нужный мне текст</h1>
Как мне тоже самое сделать с помощью AngleSharp? С нуля, у меня только открытый проект и подключенный AngleSharp (через консоль диспетчера пакетов подключал). h1 - это получается тег? Так вроде в уроке писали. А itemprop и work_name атрибуты вроде? И как это использовать теперь? В документации я встречал такой код
async Task DownloadStringAsync()
{
WebClient url = new WebClient();
string source = url.DownloadString(@"какая-то_страница");
IConfiguration config = Configuration.Default;
IBrowsingContext context = BrowsingContext.New(config);
IDocument document = await context.OpenAsync(req => req.Content(source));
textBox2.Text = document.DocumentElement.OuterHtml;
}
Но что то мне подсказывает что это совсем не то. В шапке прошлой темы похожий пример. Сработает если я просто его скопирую?
void DownloadString()
{
var parser = new HtmlParser();
var doc = parser.ParseDocument(@"https://какая-то_страница.html");
var seller = doc.QuerySelector("[itemprop='seller']");
var name = seller.QuerySelector("[itemprop='name']").Text();
textBox2.Text = name;
}
Не сработало, ошибка на строчке var seller = doc.QuerySelector("[itemprop='seller']");
Иной код
var parser = new HtmlParser();
var doc = parser.ParseDocument(@"какая-то_страница");
string name = doc.QuerySelector("h1").Text();
EngGameNameTextBox.Text = name;
Тоже ошибка. Ошибка именно при выполнении кода. Сама студия никаких ошибок не видит.
Заметил наконец ошибку, должно быть не parser.ParseDocument, а просто parser.Parse
Но тогда студия ругается что Html.Parser не содержит определение Parse.
Помимо того что я уже указал, у меня еще и такие регулярные выражения в наличии.
Match match2 = Regex.Match(reply, "<a href=\"https://www.какой-то_сайт.com/profile/=/maker_id/[0-9]*.html\">(.*?)</a>");
Match match3 = Regex.Match(reply, "<td><a href=\"https://www.какой-то_сайт.com/new/=/year/[0-9]*/mon/[0-9]*/day/[0-9]*/cyear/[0-9]*/cmon/[0-9]*\">(.*?)</a></td>");
MatchCollection match6 = Regex.Matches(reply, "<a href=\"https://www.какой-то_сайт.com/genre/[0-9][0-9][0-9]/from/work.genre\">(.*?)</a>");
for (int i = 0; i < match6.Count; i++)
{
if (GenreTextBox2.Text == "")
GenreTextBox2.Text = match6[i].Groups[1].Value;
else
GenreTextBox2.Text = GenreTextBox2.Text + ", " + match6[i].Groups[1].Value;
}
<a href="https://www.какой-то_сайт.com/profile/=/maker_id/23822.html">Нужный текст 2</a>
<td><a href="https://www.какой-то_сайт.com/new/=/year/2022/mon/02/day/20/cyear/2022/cmon/02">Нужный текст 3</a></td> </tr>
<a href="https://www.какой-то_сайт.com/genre/068/from/work.genre">Нужный текст 4</a>
<a href="https://www.какой-то_сайт.com/genre/233/from/work.genre">Нужный текст 5</a>
<a href="https://www.какой-то_сайт.com/genre/134/from/work.genre">Нужный текст 6</a>
<a href="https://www.какой-то_сайт.com/genre/142/from/work.genre">Нужный текст 7</a>
<a href="https://www.какой-то_сайт.com/genre/182/from/work.genre">Нужный текст 8</a>
Как должны выглядеть аналоги такого же кода с AngleSharp?
Или лучше оставить все как есть?
Ответы (1 шт):
...Уговорил друга, шарящего в теме, накинуть мне пример рабочего кода. Вот собственно он.
async void button1_Click(object sender, EventArgs e)
{
await DownloadFromURL("тут_полная_ссылка_на_страницу");
}
введите сюда код
async Task DownloadFromURL(string url = "https://...")
{
var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
// Дожидаемся окончания загрузки с помощью await.
var document = await context.OpenAsync(url);
// Обрабатываем полученный документ.
ProceedSingleSelector(document);
ProceedMultipleSelector(document);
}
/// <summary>
/// Пример с единичным Селектором (выбор одного элемента).
/// </summary>
/// <param name="html_obj"></param>
private void ProceedSingleSelector(AngleSharp.Dom.IDocument html_obj)
{
var h1 = html_obj.QuerySelector("h1");
//Console.WriteLine("Главный заголовок страницы: '" + h1.TextContent + "'");
textBox2.Text = "Главный заголовок страницы: '" + h1.TextContent + "'";
// Также ты можешь продолжать поиск внутри уже найденных элементов, как и через document до этого.
// Т.е. h1.QuerySelector(".elem_class") будет работать, если бы h1 включал другие элементы внутри себя.
}
/// <summary>
/// Пример с мульти Селектором (выбор более 1 элемента).
/// </summary>
/// <param name="html_obj"></param>
private void ProceedMultipleSelector(AngleSharp.Dom.IDocument html_obj)
{
// Все жанры
var all_tags = html_obj.QuerySelectorAll(".main_genre a");
foreach (AngleSharp.Dom.IElement elem in all_tags)
{
//Console.WriteLine("Игра имеет тег: '" + elem.TextContent + "'");
textBox2.Text = textBox2.Text + "\r\n" + "Игра имеет тег: '" + elem.TextContent + "'";
}
// Но если нужно...
// Получить нормальный массив именно самих элементов.
AngleSharp.Dom.IElement[] tags_arr = all_tags.ToArray();
// Получить нормальный массив именно текстов этих элементов.
string[] tags_texts_arr = all_tags.Select(elem => elem.TextContent).ToArray();
// Объединить текстовые значения в 1 строку через запятую:
string all_tags_str = string.Join(", ", tags_texts_arr);
//Console.WriteLine("Всё теги в 1 строку: '" + all_tags_str + "'");
textBox2.Text = textBox2.Text + "\r\n" + "Всё теги в 1 строку: '" + all_tags_str + "'";
}
Код рабочий, проверил. Единственное несостыковка, предупреждение на метод DownloadFromURL("тут_полная_ссылка_на_страницу") "Поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Попробуйте применить оператор await к результату вызова". Но выполнению кода это не мешает...