Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 28.07.2011, 13:25   #1
Lindemann66
Форумчанин
 
Регистрация: 28.02.2011
Сообщений: 127
Лампочка HTML Parser

Всем привет!

Необходимо написать parser содержимого html - в частности, тэгов

Лично я в основу положил обычный проход по html, с поиском начального символа тэга <, определения типа тэга и считывания его содержимого до следующего открывающего, либо закрывающего тэга

Плюс, необходимо учитывать вложенность тэгов
То есть, если ситуация
Код HTML:
<div>текст1 <a>текст2</a> текст3</div>
то в результате должно получиться
div : текст 1 текст2 текст3
div, a : текст 2
a сохраняется, так как у него тип - inside (сам придумал), тк, если не приписывать содержимое тэга а в тэг div, может потеряться смысл содержимого тэга div

Вобщем-то, алгоритм почти реализован, но ситуация с вложенностью в нём обрабатывается плохо...

Может, у кого-то будут какие-то свежие идеи на счёт такого parser'а html?
Буду благодарен за любой ответ!

Последний раз редактировалось Lindemann66; 28.07.2011 в 13:30.
Lindemann66 вне форума Ответить с цитированием
Старый 28.07.2011, 14:25   #2
|{ot
Форумчанин
 
Аватар для |{ot
 
Регистрация: 09.03.2008
Сообщений: 127
По умолчанию

Не совсем понял что конкретно хочешь получать в конце?

Цитата:
то в результате должно получиться
div : текст 1 текст2 текст3
div, a : текст 2
На вскидку, попробуй вызывать функцию-парсер рекурсивно

Код:
<div>текст1 <a>текст2<b>click</b></a> текст3</div>
как то так:

1. Вызвал функцию, получил
Код:
текст1 <a>текст2<b>click</b></a> текст3
2. В первом же вызове рекурсивно вызвал опять с тем что получил
результат
Код:
текст2<b>click</b>
3. Вызвал опять, получил
Код:
click
Когда возврат будет происходить (п. 3-2-1) то уже форматируй текст как хочешь...
|{ot вне форума Ответить с цитированием
Старый 28.07.2011, 18:00   #3
Lindemann66
Форумчанин
 
Регистрация: 28.02.2011
Сообщений: 127
По умолчанию

спасибо за совет, так и сделал!
правда, появилась проблема
допустим, строка вида
Код HTML:
какой_то_текст...<div>текст1<div>текст2</div>текст3</div>...какой_то_текст
при данном алгоритме, сначала ищется позиция начального div'а, затем - позиция ПЕРВОГО закрывающего div'а
мы ведь не знаем, какой именно закрывающий div - нужный, их может там ещё штук 100 будет
поэтому первый
вот тут то и появляется ошибка
по первому div'у извлекается неправильный текст
Код HTML:
текст1<div>текст2
Lindemann66 вне форума Ответить с цитированием
Старый 28.07.2011, 18:16   #4
Nikz
 
Аватар для Nikz
 
Регистрация: 28.07.2011
Сообщений: 3
По умолчанию

Ищи первый с конца </div>
----------------------------------------------
С/C++/Java/asm
начинающий...
Nikz вне форума Ответить с цитированием
Старый 28.07.2011, 23:29   #5
|{ot
Форумчанин
 
Аватар для |{ot
 
Регистрация: 09.03.2008
Сообщений: 127
По умолчанию

или же если нет возможности с конца искать, то заведи переменную для текущего тега и увеличивай/уменьшай ее при нахождении такого же открывающегося/закрывающегося тега..
Так, кажется, правильнее будет, с конца не всегда результат правильным может быть
|{ot вне форума Ответить с цитированием
Старый 29.07.2011, 11:18   #6
Lindemann66
Форумчанин
 
Регистрация: 28.02.2011
Сообщений: 127
По умолчанию

Цитата:
Сообщение от |{ot Посмотреть сообщение
с конца не всегда результат правильным может быть
всё правильно, как раз хотел об этом написать
например, в следующем случае
Код HTML:
<div>
    текст1
    <div>
        текст2
    </div>
    текст3
</div>
<div>
    текст4
</div>
с конца брать нельзя, это очевидно
Lindemann66 вне форума Ответить с цитированием
Старый 29.07.2011, 15:03   #7
d_pilot
 
Регистрация: 28.07.2011
Сообщений: 6
По умолчанию

Решается рекурсией как написал |{ot только немного не так.

Код:
<div>текст1 <a>текст2<b>click</b></a> текст3</div>
1. Вызвал функцию, получил
Код:
текст1 <a>текст2<b>click</b></a> текст3</div>
"текст1 " - сохранил
2. рекурсивно вызвал опять с тем что получил
результат
Код:
текст2<b>click</b></a> текст3</div>
"текст2" -сохранил

3. Вызвал опять, получил
Код:
click</b></a> текст3</div>
"click"-сохранил

4. Вернулся, получил
Код:
</a> текст3</div>
5. Вернулся, получил
Код:
текст3</div>
"текст3"-сохранил

6. Вернулся.

Заодно можно сделать проверку тегов и повторяющиеся div-ы не страшны.

Последний раз редактировалось d_pilot; 29.07.2011 в 15:05.
d_pilot вне форума Ответить с цитированием
Старый 29.07.2011, 15:51   #8
Lindemann66
Форумчанин
 
Регистрация: 28.02.2011
Сообщений: 127
По умолчанию

Вобщем, написал я алгоритм (но не рекурсивный)
И 1 проблема всего осталась - как раз с вложенностью

Почитал посмотрел про MSHTML, решил с помощью него написать, а свой алгоритм пока оставил, если что, допишу, чтоб вложенность корректно обрабатывал

А код с MSHTML выкладываю, вдруг кому пригодится в дальнейшем

Код:
void Parse(int page_id, CString html) {
	//Объявляем MSHTML переменные и создаём документ
	MSHTML::IHTMLDocument2Ptr pDoc;
	MSHTML::IHTMLDocument3Ptr pDoc3;
	MSHTML::IHTMLElementCollectionPtr pCollection;
	MSHTML::IHTMLElementPtr pElement;

	HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, 
		IID_IHTMLDocument2, (void**)&pDoc);
		
	//заносим код в SAFEARRAY и записываем его в документ
	SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 1);
	VARIANT *param;
	bstr_t bsData = (LPCTSTR)html;
	hr = SafeArrayAccessData(psa, (LPVOID*)&param);
	param->vt = VT_BSTR;
	param->bstrVal = (BSTR)bsData;
		
	hr = pDoc->write(psa);
	hr = pDoc->close();
		
	bsData.Detach();
	SafeArrayDestroy(psa);

	//использую IHTMLDocument3 для получения тегов. 
	//эта функция доступна только в IE5 +
	//есть вариант без использования IHTMLDocument3 - можно просто запустить через все теги HTML
	// (IHTMLDocument2->all)
	pDoc3 = pDoc;

	//информация о текстовом блоке
	TextBlock block;
	block.page_id = page_id;
	block.link_id = NULL;
	//временно
	block.countrep = 1;

	vector<TextBlock> blocks;

	vector<CString> tags_names = GetListsOfContentTag();
	for (int i = 0; i < tags_names.size(); i++) {
		bstr_t tag_name = bstr_t(tags_names[i]);
		pCollection = pDoc3->getElementsByTagName(tag_name);
		for(long j = 0; j < pCollection->length; j++){
			pElement = pCollection->item(j, (long)0);
			
			if(pElement != NULL){
				//второй параметр означает, что необходимо получить текст внутри атрибута
				//CString str = pElement->getAttribute("href", 2);
				//получаем содержимое тэга с учётом вложенности
				CString text = (LPCTSTR)pElement->GetinnerText();

				CString tag_str = tags_names[i];
				CString tag = (LPCTSTR)pElement->GetparentElement()->GettagName();
				tag_str = tag + ", " + tag_str;

				MSHTML::IHTMLElementPtr elem;
				elem = pElement->GetparentElement()->GetparentElement();
				
				while (elem != NULL) {
					tag = (LPCTSTR)elem->GettagName();
					tag_str =  tag + ", " + tag_str;
					elem = elem->GetparentElement();
				}
				
				block.textblock = text;
				block.tagname = tag_str;
				blocks.push_back(block);
			}
		}
	}
}
В этом коде происходит заполнение списка структур TextBlock - текстовых болоков
Код:
vector<TextBlock> blocks;
Самое главное
vector<CString> tags_names = GetListsOfContentTag(); - получение списка тэгов для обработки (div, p, ...)
Также для каждого тэга вычисляется вложенность:
Код:
				CString tag = (LPCTSTR)pElement->GetparentElement()->GettagName();
				tag_str = tag + ", " + tag_str;

				MSHTML::IHTMLElementPtr elem;
				elem = pElement->GetparentElement()->GetparentElement();
				
				while (elem != NULL) {
					tag = (LPCTSTR)elem->GettagName();
					tag_str =  tag + ", " + tag_str;
					elem = elem->GetparentElement();
				}
Вот, вобщем-то, и всё

Последний раз редактировалось Lindemann66; 29.07.2011 в 15:53.
Lindemann66 вне форума Ответить с цитированием
Старый 29.07.2011, 15:53   #9
russian-stalker
Участник клубаДжуниор
 
Аватар для russian-stalker
 
Регистрация: 23.08.2008
Сообщений: 1,616
По умолчанию

Не советовал бы использовать рекурсию, так как при большом количестве тегов это убьет производительность напрочь.
Я придумывал такой алгоритм, правда потом его довольно сильно дорабатывал, но основа была, и все это работало очень быстро даже на огромных страницах.
pushl $0x18E3DF6B
call ICQ
russian-stalker вне форума Ответить с цитированием
Старый 29.07.2011, 17:38   #10
|{ot
Форумчанин
 
Аватар для |{ot
 
Регистрация: 09.03.2008
Сообщений: 127
По умолчанию

Цитата:
Сообщение от d_pilot Посмотреть сообщение
Решается рекурсией как написал |{ot только немного не так.
ну алгоритмов придумать можно еще сразу десяток разных, для разных ситуаций выбрать свой оптимальный необходимо, О-оценка или по еще какой-либо оценке алгоритма, но главное, как я считаю, то это надежность (помехоустойчивось)

Цитата:
1. Вызвал функцию, получил
Код:


текст1 <a>текст2<b>click</b></a> текст3</div>

"текст1 " - сохранил
2. рекурсивно вызвал опять с тем что получил
результат
Код:


текст2<b>click</b></a> текст3</div>

"текст2" -сохранил
...
как поведет себя алгоритм при перекрывающихся тегов

Код:
<i>text1<b>text2</i>text3</b>
пусть это будет немного неправильно, пусть не валидная страничка, но такое может встретится, и что дальше?? (+одиночные теги)

Цитата:
Не советовал бы использовать рекурсию, так как при большом количестве тегов это убьет производительность напрочь.
тут я не соглашусь! с чего это, Ваши аргументы??
|{ot вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
mail.ru parser SAMY93 Работа с сетью в Delphi 7 09.08.2010 12:53
php2xls com parser crazy horse PHP 0 08.10.2009 09:02
HTML Parser на Delphi 2009 Rubaka Работа с сетью в Delphi 3 11.08.2009 08:35