Вопрос по парсингу (с звёдочкой)

Uralbox

Client
Регистрация
04.02.2019
Сообщения
39
Благодарностей
0
Баллы
6
Есть задача, спарсить язык игры в steam. Задача вроде как не сложная, но что-то моя черепная коробка не может логически придумать как это реализовать из-за одного момента, об этом ниже.

Сама задача:
Есть страницы вида - https://store.steampowered.com/app/261550/Mount__Blade_II_Bannerlord/ (это пример)
Нужно спарсить язык игры и записать значение в переменную.

Вот с такими условиями:
интерфейс и озвучка на русском - ru
интерфейс на русском, озвучка английская - ruen
интерфейс и озвучка на английском - en
интерфейс на русском, озвучки нет (часто в инди играх) - runo
интерфейс на английском, озвучки нет - enno

тут думаю понятна логика значений.

Вопрос вот как это реализовать. В стиме язык выводиться в таблице, что и создаёт сложность. И х*** пойми как это всё выпарсить логически.
Хотя у кого голова хорошо работает, то думаю не составить труда реализовать.

Собсна, нужна помощь.
 

RoyalBank

Client
Регистрация
07.09.2015
Сообщения
557
Благодарностей
547
Баллы
93
И х*** пойми как это всё выпарсить логически.
Я бы делал на каком-нибудь JSON формате хранилища. Тот же mongoDB разбирался тут. Это позволит динамически расширять кол-во языков.
А дальше проще:
- Считаешь кол-во <tr> в таблице (-1 заголовок)
- Забираешь сам язык и проверяешь checkcol на наличие Span.

Если с базой в json нет возможности, то придумай для себя, какой-то уникальный формат записи в одну строку и накидывай языки через String.Builder.
К примеру, ru.-1;en.1.1.1;cn.1.0.1;tr.1.0.1
Дальше, когда будешь забирать из базы эту строку и читать, то сплитишь её по ";" для получения языков и по "." сплитишь для инфы по Интерфейс/Озвучка/Субтитры.
 

Uralbox

Client
Регистрация
04.02.2019
Сообщения
39
Благодарностей
0
Баллы
6
Я бы делал на каком-нибудь JSON формате хранилища. Тот же mongoDB разбирался тут. Это позволит динамически расширять кол-во языков.
А дальше проще:
- Считаешь кол-во <tr> в таблице (-1 заголовок)
- Забираешь сам язык и проверяешь checkcol на наличие Span.

Если с базой в json нет возможности, то придумай для себя, какой-то уникальный формат записи в одну строку и накидывай языки через String.Builder.
К примеру, ru.-1;en.1.1.1;cn.1.0.1;tr.1.0.1
Дальше, когда будешь забирать из базы эту строку и читать, то сплитишь её по ";" для получения языков и по "." сплитишь для инфы по Интерфейс/Озвучка/Субтитры.
Да, задача не из простых для меня. Проще наверное будет какими-то невероятно костыльными способами сделать со множеством условий и на кубиках.
За подсказку спасибо, как-то изучу, думаю пригодиться ещё.
 

RoyalBank

Client
Регистрация
07.09.2015
Сообщения
557
Благодарностей
547
Баллы
93
со множеством условий и на кубиках.
Если ты хотел на кубиках, то не знаю, как сделать. В коде делается в два цикла, первый цикл считает кол-во языков <tr> в таблице. Второй цикл проходит по каждому из span для языка. Во втором же цикле сразу собираешь большую строку.
 
  • Спасибо
Реакции: Uralbox

Uralbox

Client
Регистрация
04.02.2019
Сообщения
39
Благодарностей
0
Баллы
6
Если ты хотел на кубиках, то не знаю, как сделать. В коде делается в два цикла, первый цикл считает кол-во языков <tr> в таблице. Второй цикл проходит по каждому из span для языка. Во втором же цикле сразу собираешь большую строку.
Буду благодарен если набросаешь коды на C#. Я уже постараюсь же применить это всё дело.
 

RoyalBank

Client
Регистрация
07.09.2015
Сообщения
557
Благодарностей
547
Баллы
93
Буду благодарен если набросаешь коды на C#
C#:
StringBuilder sb = new StringBuilder();

Tab t = instance.ActiveTab;

string x_tb = @".//*[contains(@class,'game_language_options')]/descendant::tr";
string x_tb_l = @".//*[contains(@class,'game_language_options')]/descendant::tr[{0}]/td[1]";
string x_tb_o = @".//*[contains(@class,'game_language_options')]/descendant::tr[{0}]/td[{1}]/span";


string lng = string.Empty;
string inerface = string.Empty;
string fAudio = string.Empty;
string subt = string.Empty;

for (int l = 1; l <= t.FindElementsByXPath(x_tb).Count; l++) {
    if (!t.FindElementByXPath(String.Format(x_tb_l,l),0).IsVoid) lng = t.FindElementByXPath(String.Format(x_tb_l,l),0).InnerText.Trim();
    else {
        // Тут проверка на "Не поддерживается" зависит от языка браузера.
    }

    if (!t.FindElementByXPath(String.Format(x_tb_o,l,2),0).IsVoid) inerface = "1"; else inerface = "0";
    if (!t.FindElementByXPath(String.Format(x_tb_o,l,3),0).IsVoid) fAudio = "1"; else fAudio = "0";
    if (!t.FindElementByXPath(String.Format(x_tb_o,l,4),0).IsVoid) subt = "1"; else subt = "0";
    
    sb.Append(String.Format("{0}_{1}.{2}.{3};",lng,inerface,fAudio,subt));
}
sb.Length--;
return sb.ToString();
 

Uralbox

Client
Регистрация
04.02.2019
Сообщения
39
Благодарностей
0
Баллы
6
C#:
StringBuilder sb = new StringBuilder();

Tab t = instance.ActiveTab;

string x_tb = @".//*[contains(@class,'game_language_options')]/descendant::tr";
string x_tb_l = @".//*[contains(@class,'game_language_options')]/descendant::tr[{0}]/td[1]";
string x_tb_o = @".//*[contains(@class,'game_language_options')]/descendant::tr[{0}]/td[{1}]/span";


string lng = string.Empty;
string inerface = string.Empty;
string fAudio = string.Empty;
string subt = string.Empty;

for (int l = 1; l <= t.FindElementsByXPath(x_tb).Count; l++) {
    if (!t.FindElementByXPath(String.Format(x_tb_l,l),0).IsVoid) lng = t.FindElementByXPath(String.Format(x_tb_l,l),0).InnerText.Trim();
    else {
        // Тут проверка на "Не поддерживается" зависит от языка браузера.
    }

    if (!t.FindElementByXPath(String.Format(x_tb_o,l,2),0).IsVoid) inerface = "1"; else inerface = "0";
    if (!t.FindElementByXPath(String.Format(x_tb_o,l,3),0).IsVoid) fAudio = "1"; else fAudio = "0";
    if (!t.FindElementByXPath(String.Format(x_tb_o,l,4),0).IsVoid) subt = "1"; else subt = "0";
   
    sb.Append(String.Format("{0}_{1}.{2}.{3};",lng,inerface,fAudio,subt));
}
sb.Length--;
return sb.ToString();
Спасибо!
 

Uralbox

Client
Регистрация
04.02.2019
Сообщения
39
Благодарностей
0
Баллы
6
получилось такое значение после выполнения кода-> _0.0.0;русский_0.0.0;английский_1.1.1

проблема как его привести в тот вид, который я хотел. ru ruen и тд

кстати что означает это значение - _0.0.0; (перед русский) ?
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 766
Благодарностей
2 407
Баллы
113
получилось такое значение после выполнения кода-> _0.0.0;русский_0.0.0;английский_1.1.1

проблема как его привести в тот вид, который я хотел. ru ruen и тд

кстати что означает это значение - _0.0.0; (перед русский) ?
Если совсем кратко то алгоритм должен быть примерно такой:
1. Ищем нужную табличку.
2. Мы знаем, что количество столбцов там одинаковое, в первом столбце всегда язык, в первой строке заголовка нет тега td.
3. Удаляем мусор банальными заменами - этого достаточно должно быть, чтобы не мешало перед глазами (в дальнейшем в рабочей версии это можно убрать)
4. На сколько я понял брать нужно только русский-английский - определим допустим, что английский = 1, русский = 0
5. Получаем все теги TR нашей таблички в какой-то массив
6. В найденном массиве ищем массив тегов TD.
7. Первой ячейке присваиваем язык (по жесткому условию, содержится ли в переменной имя языка)
8. Во всех ячейках проверяем наличие галочки - галочка есть - 1, галочки нет 0, не нужную ячейку просто не принимаем во внимание
9. Добавляем найденные значения в какой-то временный список
10. Дальше соединим строчки списка - получится какая-то строка
11. Строкам с нужными для нас показателями присвоим какие-то значения через switch
12. Если ничего не найдено (из желанного нами - пустая строка).

Чисто на коленке думаю должен подойти этот код.
C#:
string url = @"https://store.steampowered.com/app/261550/Mount__Blade_II_Bannerlord/";
string get = ZennoPoster.HttpGet(url: url);
string table = Regex.Match(get, @"<table[\w\W]*?</table>").Value;

table = Regex.Replace(table, @"(?<=<table)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<tr)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<td)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<th)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"\s+", " ", RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"> <", ">\r\n<", RegexOptions.IgnoreCase);

string[] trs = Regex.Matches(table, @"(?<=<tr>)[\w\W]*?(?=</tr>)").Cast<Match>().Select(s => s.Value).ToArray();

List<string> list = new List<string>();
foreach(string tr in trs) {
    string[] tds = Regex.Matches(tr, @"(?<=<td>)[\w\W]*?(?=</td>)").Cast<Match>().Select(s => s.Value).ToArray();
    if(tds.Length < 1) {
        project.SendInfoToLog("Вероятнее всего - шапка", true);
        continue;
    }
    else {
        string lang = tds[0].Trim();
        if(lang == "English" || lang == "Английский") lang = "1"; // en
        if(lang == "Русский") lang = "0"; // ru
        if(lang == "0" || lang == "1") {
            project.SendInfoToLog(lang);
            int I = tds[1].Contains("10004") ? 1 : 0;
            project.SendInfoToLog(I.ToString());
            int O = tds[2].Contains("10004")? 1 : 0;
            project.SendInfoToLog(O.ToString());
            int S = tds[3].Contains("10004") ? 1 : 0;
            project.SendInfoToLog(S.ToString());
            list.Add(string.Format(@"{0}{1}{2}",lang,I,O)); // S пропускаем...
        }
        else {
            project.SendInfoToLog("Язык не нужен - пропускаем");
            continue;
        }
    }   
}
project.SendInfoToLog(list.Count.ToString());
string line = string.Join("", list);
string out_line = string.Empty;
project.SendInfoToLog(line.Length.ToString());
if(line.Length == 3){
    // найден один язык либо русский либо английский
    switch(line){
        case "111":out_line = "en";
        break;
        case "011": out_line = "ru";
        break;
        case "010": out_line = "runo";
        break;   
        case "110": out_line = "enno";
        break;
        default:out_line = string.Empty;
        break;   
    }
}
else if(line.Length == 6){
    // Найдено два языка русский и английский
    switch(line){
        case "010101": out_line = "ruen";
        break;
        case "101010": out_line = "ruen";
        break;
        default:out_line = string.Empty;
        break;   
    }
}
else return out_line;
return out_line;
 
Последнее редактирование:
  • Спасибо
Реакции: Uralbox

Edk Drs

Новичок
Регистрация
10.07.2020
Сообщения
5
Благодарностей
1
Баллы
3
Если совсем кратко то алгоритм должен быть примерно такой:
1. Ищем нужную табличку.
2. Мы знаем, что количество столбцов там одинаковое, в первом столбце всегда язык, в первой строке заголовка нет тега td.
3. Удаляем мусор банальными заменами - этого достаточно должно быть, чтобы не мешало перед глазами (в дальнейшем в рабочей версии это можно убрать)
4. На сколько я понял брать нужно только русский-английский - определим допустим, что английский = 1, русский = 0
5. Получаем все теги TR нашей таблички в какой-то массив
6. В найденном массиве ищем массив тегов TD.
7. Первой ячейке присваиваем язык (по жесткому условию, содержится ли в переменной имя языка)
8. Во всех ячейках проверяем наличие галочки - галочка есть - 1, галочки нет 0, не нужную ячейку просто не принимаем во внимание
9. Добавляем найденные значения в какой-то временный список
10. Дальше соединим строчки списка - получится какая-то строка
11. Строкам с нужными для нас показателями присвоим какие-то значения через switch
12. Если ничего не найдено (из желанного нами - пустая строка).

Чисто на коленке думаю должен подойти этот код.
C#:
string url = @"https://store.steampowered.com/app/261550/Mount__Blade_II_Bannerlord/";
string get = ZennoPoster.HttpGet(url: url);
string table = Regex.Match(get, @"<table[\w\W]*?</table>").Value;

table = Regex.Replace(table, @"(?<=<table)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<tr)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<td)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"(?<=<th)[\w\W]*?(?=>)", string.Empty, RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"\s+", " ", RegexOptions.IgnoreCase);
table = Regex.Replace(table, @"> <", ">\r\n<", RegexOptions.IgnoreCase);

string[] trs = Regex.Matches(table, @"(?<=<tr>)[\w\W]*?(?=</tr>)").Cast<Match>().Select(s => s.Value).ToArray();

List<string> list = new List<string>();
foreach(string tr in trs) {
    string[] tds = Regex.Matches(tr, @"(?<=<td>)[\w\W]*?(?=</td>)").Cast<Match>().Select(s => s.Value).ToArray();
    if(tds.Length < 1) {
        project.SendInfoToLog("Вероятнее всего - шапка", true);
        continue;
    }
    else {
        string lang = tds[0].Trim();
        if(lang == "English" || lang == "Английский") lang = "1"; // en
        if(lang == "Русский") lang = "0"; // ru
        if(lang == "0" || lang == "1") {
            project.SendInfoToLog(lang);
            int I = tds[1].Contains("10004") ? 1 : 0;
            project.SendInfoToLog(I.ToString());
            int O = tds[2].Contains("10004")? 1 : 0;
            project.SendInfoToLog(O.ToString());
            int S = tds[3].Contains("10004") ? 1 : 0;
            project.SendInfoToLog(S.ToString());
            list.Add(string.Format(@"{0}{1}{2}",lang,I,O)); // S пропускаем...
        }
        else {
            project.SendInfoToLog("Язык не нужен - пропускаем");
            continue;
        }
    }  
}
project.SendInfoToLog(list.Count.ToString());
string line = string.Join("", list);
string out_line = string.Empty;
project.SendInfoToLog(line.Length.ToString());
if(line.Length == 3){
    // найден один язык либо русский либо английский
    switch(line){
        case "111":out_line = "en";
        break;
        case "011": out_line = "ru";
        break;
        case "010": out_line = "runo";
        break;  
        case "110": out_line = "enno";
        break;
        default:out_line = string.Empty;
        break;  
    }
}
else if(line.Length == 6){
    // Найдено два языка русский и английский
    switch(line){
        case "010101": out_line = "ruen";
        break;
        case "101010": out_line = "ruen";
        break;
        default:out_line = string.Empty;
        break;  
    }
}
else return out_line;
return out_line;
можно ли с вами как-то связаться для консультации?
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)