Я уже писал о библиотеке simple_html_dom, с помощью которой можно парсить страницы сайтов. В этой заметке поделюсь некоторыми наработками, когда содержимое имеет одинаковые контейнеры.
Что делать когда сложно зацепиться за DOM элементы?
По неизвестной мне причине в библиотеке simple_html_dom не работает несколько классов в html элементе. Например, не получится найти и сделать выборку по строке:
Очевидным будет использовать конструкции вроде:
или
но на деле это не работает или почти никогда не работает.
Описание задачи
Нужно найти два отдельных блока содержимого. Оба блока имеют одинаковые свойства и потому не цепляются по отдельности.
Сначала preg_match потом simple_html_dom
Для этих целей можно было бы использовать сначала конструкции preg_match, чтобы найти определённое содержимое страницы:
Код выше схватит всё что начинается с «Первого заголовка» и до «второго заголовка».
Иногда приходится так изощрять код, потому что иначе не захватить точный кусок кода. В базовых примерах php часто даются простые и типовые ситуации, которые не работают в сложных случаях.
А вот ещё один код:
С помощью него мы уже захватим всё что после второго заголовка.
А потом уже из найденного использовать выборку
Но здесь возникает другая проблема. Содержимое в переменной matches отказывается пониматься и выводит разного рода ошибки. Даже, если содержимое конвертировать в массивы или строки, всё равно чего-то не хватает. А что именно, совсем не ясно.
Хотя в обратную сторону работает. Если сначала сделать выборку через simple_html_dom, то потом можно вполне забирать информацию через preg_match.
В ином случае остаётся придерживаться одного стиля, либо использовать только preg_match либо simple_html_dom. В данной статье будем использовать только второе.
Дополнено позже. Проблему описанную выше можно решить с помощью предварительного сохранения результат выборки $matches в отдельных файл, а затем снова использовать функцию file_get_html(). Например, так:
if ( !empty($matches[2]) ) {
file_put_contents($file_temp, $matches[2]);
}
if ( file_exists($file_temp) ) {
$html = file_get_html($file_temp); // Используем библиотеку
}
Только simple_html_dom
Если перед парсингом нужно удалить какие-то данные, воспользуйтесь этой статьёй.
В коде уже привел все пояснения. Первый блок находим так.
$i=0;
// Находим общий контейнер
foreach ( $html->find('div.class-best') as $matches ) {
$i++;
if($i==2) break; // Останавливаем поиск после первого найденного контейнера
// Создаём массив из данных
$j=0;
// Теперь в общем контейнере будем забирать нужную нам информацию
foreach ( $matches->find('div[itemtype=""]') as $matches2 ) {
$j++;
if($j==6) break; // На 6 одинаковом элементе останавливаем поиск
// Ищем 3 отдельных элемента.
// 1 - Простой текст в ссылке, 2 - простой текст внутри тега span,
// 3 - и ссылку на изображение
@$one = $matches2->find('a.one', 0)->plaintext;
@$two = $matches2->find('span.two', 0)->plaintext;
@$three = $matches2->find('img', 0)->src;
// На всякий случай избавимся от лишних пробелов с обоих сторон найденного
$one = trim($one);
$two = trim($two);
$three = trim($three);
// Дальше можно поменять содержимое одного найденного объекта
$three = preg_replace('/\/\//','https://',$three); // меняем в начале ссылку '//' на 'https://'
$our_array[] = $one .', ' .$two .', ' .$three;
}
}
В результате этих манипуляций мы получим массив $our_array, в котором будет содержаться такая информация:
И так 5 строк в массиве. Дальше с этим массив можно делать всё что угодно.
Но задача ещё не закончена. Помните, выше я говорил о втором блоке, который тоже нужно найти? Проблема в том, что он имеет одинаковые свойства с первым блоком, который нам не надо никак захватывать.
Предположим что первый блок меняется и содержит всегда разное количество информации. И не получится это обойти используя наш первый массив, начиная например с 6-ого значения: our_array[5]. Ведь в другой раз, на другой странице, нужные нам данные могут начинаться с третьей позиции или любой другой.
Мы знаем, что второй блок, к счастью начинается сразу за первым. Или любым другим, но идёт всегда в одинаковом порядке. В приведенном примере всегда после первого блока. Тогда добавляем следующую конструкцию:
// Находим общий контейнер
foreach ( $html->find('div.class-best') as $matches ) {
$i++;
if($i==1) continue; // Пропускаем первый найденный объект
if($i==3) break; // Останавливаем поиск после первого найденного объекта
Здесь всё тоже самое, что и в первом блоке.
$our_array2[] = $one .', ' .$two .', ' .$three;
}
}
Как видно, содержимое второго php блока повторяет первый, за исключением того, что мы находим первый блок и пропускаем его с помощью конструкции if($i==1) continue. И обязательно останавливаем наш поиск на втором блоке. Иначе схватится лишнее. Одинаковых контейнеров может быть очень много.