Те, кто хоть раз писал парсер, знает, что не стоит этого делать с помощью регулярных выражений. Проиллюстрировать это утверждение поможет следующий пример.
Возьмем HTML код:
<div><a href="http://xdan.ru"><div>Сайт по программированию парсеров</div><div> и многое другое</div></a></div>
К примеру, из него нам нужно получить описание и url сайта. Если брать исключительно этот кусок кода, то все решается достаточно просто:
$html = '<div><a href="http://xdan.ru"><div>Сайт по программированию парсеров</div><div> и многое другое</div></a></div>'; preg_match('#<div><a href="([^"]+)"><div>([^<]+)</div><div>([^<]+)</div></a></div>#U',$html,$list); echo 'url:'.$list[1].',title:'.$list[2].$list[3]; // выведет url:http://xdan.ru,title:Сайт по программированию парсеров и многое другое
Проблемы начинаются тогда, когда описание сайта заполняют пользователи, и оно не имеет определенного шаблона.
<div><a href=”http://xdan.ru”><div>Сайт по <b>программированию</b> парсеров</div><div> и многое <div> многое </div> другое </div></a></div>
Такой код регулярному выражению не по зубам.
Обычно, в вузах на этот случай учат писать конечный автомат. Суть его в том, что мы перебираем, посимвольно, весь html текст, находим начало тега, и строим дерево документа. Так называемое DOM (Document Object Model)
Сейчас, писать такое самому нет необходимости.
В php, начиная с версии 5, есть встроенные методы работы с деревом документа (класс DOMDocument), но основан он на XML парсере.
А HTML и XML это хоть и очень похожие, но в тоже время абсолютно разные технологии.
К примеру, непременное требование к XML это закрытые теги и отсутствие ошибок.
Отсюда вытекает условие: ошибок в html, который мы парсим с помощью нативных средств php, быть не должно.
К сожалению, на сайтах донорах, ошибки не редки, а значит этот метод отпадает.
Для корректного разбора таких сайтов, на помощь придут php библиотеки PHPQuery, Simple HTML DOM, Zend DOM Query, Nokogiri .
Некоторые из них, после небольших манипуляций скармливают html тому же DOMDocument. Мы не будем их рассматривать.
В этой статье я расскажу про SimpleHTMLDOM. Этой библиотекой я пользуюсь уже несколько лет, и она меня еще ни разу не подводила.
Скачиваем последнюю версию здесь.
Пусть Вас не смущает то, что она не обновлялась с 2008 года, то, что она умеет, полностью покроет Ваши нужды в разборе html текстов.
В архиве, который вы скачали, две папки (примеры работы и документация) и файл simple_html_dom.php.
simple_html_dom.php это и есть вся библиотека, больше ничего для работы не потребуется. Кидаем этот файл в папку с проектом и в своем скрипте просто подгружаем его.
include 'simple_html_dom.php';
Кроме документации, которую вы скачали с архивом, доступна еще online версия, ее вы найдете здесь
Файл подключен и готов к работе.
Для того, чтобы начать разбирать HTML, его сперва нужно получить. Обычно, я делаю это при помощи библиотеки CURL.
В simplehtmldom есть методы для удаленной загрузки страниц. После подключения файла библиотеки, нам доступны 2 функции для обработки HTML строк.
str_get_html(str) и file_get_html(url)
Они делают одно и тоже, преобразуют HTML текст в DOM дерево, различаются лишь источники.
str_get_htm – на вход получает обычную строку, т.е. если вы получили HTML прибегнув к curl, или file_get_contents то вы просто передаете полученный текст этой функции.
$html = str_get_html('<html><body>Привет!</body></html>');
file_get_html – сама умеет загружать данные с удаленного URL или из локального файла
$html = file_get_html('http://www.yandex.ru/');
или
$html = file_get_html('data/test.htm');
К сожалению, file_get_html загружает страницы обычным file_get_contents. Это значит если хостер, выставил в php.ini allow_url_fopen = false (т.е. запретил удаленно открывать файлы), то загрузить что-то удаленно, не получится. Да и серьезные веб сайты таким способом парсить не стоит, лучше использовать CURL с поддержкой proxy и ssl. Однако для наших опытов, вполне хватит и file_get_html.
$html = file_get_html('http://www.yandex.ru/');
в результате, в переменной $html будет объект типа simple_html_dom.
При больших объемах данных, в библиотеке происходит утечка памяти. Поэтому после окончания одного цикла надо ее чистить.
Делает это метод clear.
К примеру грузим 5 раз сайт www.yandex.ru с разными поисковыми запросами
$k = 5; while($k>0){ $html = file_get_html('http://yandex.ru/yandsearch?text=hi'.$k.'&lr=11114'); // загружаем данные // как-то их обрабатываем $html->clear(); // подчищаем за собой unset($html); $k--; }
Эти две строчки $html->clear(); и unset($html); лучше писать сразу же после того, как Вы создали объект. Иначе забудете, и скрипт отвалится, забив всю память.
После того, как html текст упакован в объект, можно приступать непосредственно к поиску нужных элементов.
Большинство поисковых функций выполняет метод find(selector, [index]). Если второй аргумент не задан, метод возвращает массив элементов. Если же задан то элемент этого массива с индексом index.
Пример: скачаем главную страницу моего блога, и выведем все ссылки, которые встретим на своем пути.
require_once 'simple_html_dom.php'; $data = file_get_html('http://xdan.ru'); if($data->innertext!='' and count($data->find('a'))){ foreach($data->find('a') as $a){ echo '<a href="http://xdan.ru/'.$a->href.'">'.$a->plaintext.'</a></br>'; } }
В примере, в качестве селектора я воспользовался названием тега <a>. Но можно использовать и другие CSS селекторы. Элемент на странице можно найти по его атрибутам. В первую очередь, это название тега, id и class. Также могут быть использованы и второстепенные атрибуты, к примеру, href ссылки или width картинки. Если и этих атрибутов нет, то не грех воспользоваться и регулярными выражениями.
Поиск по названию тега вы уже видели
$html->find('div')
поиск по id
$html->find('#preview')
поиск по классу
$html->find('.myclass')
или комбинированный вариант
$html->find('#preview div.myclass')
в данном случае, сначала найдется элемент с id= preview затем в нем найдутся все теги div, и уже среди них фильтруются те у которых class=”myclass”
Если метод find ничего не нашел и index не задан, то он возвращает пустой массив. Если же index задан, то метод возвращает null.
Поэтому верным решением будет проверить
if(count($html->find('#preview div.myclass'))) foreach($html->find('#preview div.myclass') as $div) echo $div->innertext;
Поиск по наличию атрибута
$html->find(' img [width]'); // найдет нам все изображения у которых задан атрибут ширина
или более конкретный поиск по значению атрибута
$ret = $html->find('img[width=400px]');// найдет все изображения, у которых задана ширина равная 400px
Такая нотация позволяет искать по двум и более смежным классам
$ret = $html->find('img[class=active myclass]');//<img class="active myclass"/>
Поиск нескольких тегов
$html->find('a, img, br,span');
Поиск вложенных тегов
$es = $html->find('ul.myclass li');// найдет все li который является потомком ul(возможно и не прямым) $es = $html->find('div.myclass li');// найдет все li в div.myclass
У каждого найденного элемента также есть метод find
$html->find('div.myclass li');//найдет все div.myclass а потом все li лежащие в них
если нам нужно найти все li только первого div’а то мы можем написать так
$html->find('div.myclass',0)->find('li');
Поиск по значению атрибута не ограничивается только равенством. Вот доступные условия
[атрибут] – проверяет есть ли у элемента данный атрибут
[атрибут=величина] - проверяет, есть ли у элемента данный атрибут и равно ли его значение величине.( div[class=myclass] – найдет все div’ы у которых class равен myclass)
[атрибут!=величина] - проверяет, есть ли у элемента данный атрибут и не равно ли его значение величине.( div[class!=myclassok] – найдет все div’ы у которых class не равен myclassok)
[атрибут^=величина] - проверяет, есть ли у элемента данный атрибут и начинается ли его значение с величины ( div[class^=my] – найдет все div’ы у которых class начинается с my, к примеру myclass и myclassok)
[атрибут$=величина] - проверяет, есть ли у элемента данный атрибут и заканчивается ли его значение величиной( div[class$=ok] – найдет все div’ы у которых class заканчивается на ok, к примеру myclassok, yok, okно не oki)
[атрибут*=величина] - проверяет, есть ли у элемента данный атрибут и содержит ли его значение в себе величину, в любом месте(div[class*=sok] – найдет все div’ы у которых class содержит sok, к примеру myclassok, ysoki, sok)
Обычный текст можно искать как тег text
$es = $html->find('text'); // найдет все текстовые блоки в html
Комментарии находим по тегу comment
$es = $html->find('comment');
Каждый найденный элемент и сам $html имеют 5 полей
$html = str_get_html("<div>foo <b>bar</b></div>"); echo $html; // выведет <div>foo <b>bar</b></div>; $e = $html->find("div", 0); echo $e->tag; // Вернет: "div" echo $e->outertext; // Вернет: <div>foo <b>bar</b></div> echo $e->innertext; // Вернет: foo <b>bar</b> echo $e->plaintext; // Вернет: foo bar
$e->tag Читает или записывает имя тега элемента.
$e->outertext Читает или записывает весь HTML элемента, включая его самого.
$e->innertext Читает или записывает внутренний HTML элемента
$e->plaintext Читает или записывает простой текст элемента, это эквивалентно функции strip_tags($e->innertext). Хотя поле доступно для записи, запись в него ничего не даст, и исходный html не изменит
$html = str_get_html("<div>foo <b>bar</b></div"); $div = $html->find('div',0); $div->plaintext = 'gooo'; echo $div->innertext; // вернет <div>foo <b>bar</b></div>
Как Вы могли догадаться, для удаления ненужного элемента из HTML можно затереть его поле outertext
$html = str_get_html("<div>foo <b>bar</b></div"); $b = $html->find('b',0); $b->outertext = ''; echo $html->innertext; // вернет <div>foo</div>
Тут следует помнить, что хоть элемент и не виден в html, из дерева DOM он никуда не делся
$html = str_get_html("<div>foo <b>bar</b></div"); $b = $html->find('b',0); $b->outertext = ''; echo $html->innertext; // вернет <div>foo</div>, элемент удален из HTML // но echo count($html->find('b')); // вернет 1, в дерево элемент присутствует
при желании мы даже можем вернуть элемент на место
$b->outertext = '<span>bar</span>'; echo $html->innertext;// вернет <div>foo<span>bar</span></div>
Для более эффективной навигации по дереву документа доступны методы
$e->children ( [int $index] ) Возвращает объект N-го прямого потомка, если индекс установлен, в противном случае возвращает массив всех дочерних элементов
$e->parent() Возвращает родительский элемент.
$e->first_child() Возвращает первый дочерний элемент, или null, если ничего не найдено
$e->last_child() Возвращает последний дочерний элемент, или null, если ничего не найдено
$e->next_sibling() Возвращает следующий родственный элемент, или null, если ничего не найдено
$e->prev_sibling() Возвращает предыдущий родственный элемент, или null, если ничего не найдено
пример
$html ="<div> <b>bar</b> <b>foo</b> <span>arg</span> <div> <b>tor</b> </div> </div>";
Все дочерние элементы разные, как-то подобрать к ним селектор проблематично. Поэтому воспользуемся описанными методами.
$html = str_get_html($html); $div = $html->find('div',0); $i = 0; while($item = $div->children($i++)){ echo $item->innertext; }
либо так
$item = $div->children(0); echo $item->innertext; while($item = $item -> next_sibling()){ echo $item->innertext; }
Данные методы полезны при разборе таблиц, элементы которых, как правило, структурированы, но не имеют идентифицирующих атрибутов.
Ну и последняя фишка это вызов callback функции на найденный элемент
function my_callback($element) { if ($element->tag=='span') $element->outertext = '<b>'.$element->innertext. '</b>';// заменим все span элементы на b } $html = str_get_html('<span>bar</span><span>pole</span><span>sushi</span><a>okno</a>'); // Регистрация функции обратного вызова с ее именем $html->set_callback('my_callback');// вызов функции произойдет при конвертации объекта в строку echo $html; // на самом деле, при этом вызывается магический метод __toString, он и запускает наши калбяки
На экране мы увидим
<b>bar</b><b>pole</b><b>sushi</b><a>okno</a>
Доступ к атрибутам элементов осуществляется напрямую
foreach($html->find('img') as $img) echo $img->src; //или echo $html->find('img',0)->src;
Хватит теории, перейдем к практике
Загрузим n фотографий из поисковой выдачи Yandex Картинок. http://images.yandex.ru/
require_once 'simple_html_dom.php'; // поисковый URL $url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image'; $n = 2; // загружаем данный URL $data = file_get_html($url); // очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = ''; // находим все изображения на странице if(count($data->find('div.b-image img'))){ $i = 1; foreach($data->find('div.b-image img') as $img){ // выводим на экран изображение echo '<img src="'.$img->src.'"/>'; // и скачиваем его в файл file_put_contents('data/'.($i++).'.jpg',file_get_contents($img->src)); if($i>$n)break; // выходим из цикла если скачали достаточно фотографий } } $data->clear();// подчищаем за собой unset($data);
Как быть если нам нужно больше фото, чем лежит на одной странице?
Ответ прост: Код, приведенный выше, заключается в функцию, в html помимо фото находим еще и URLвсех страниц, и рекурсивно вызываем данную функцию для этих страниц.
require_once 'simple_html_dom.php'; function getYandexImages($url,$findpages = true){ static $i = 1; $n = 200; // загружаем данный URL $data = file_get_html($url); // очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = ''; // находим URL страниц только для первого вызова функции if( $findpages and count($data->find('div.b-pager__pages a'))){ foreach($data->find('div.b-pager__pages a') as $a){ // довольно распространенный случай - локальный URL. Поэтому иногда url надо дополнять до полного if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href; // и еще дна тонкость, & надо заменять на & $a->href = str_replace('&','&',$a->href); // вызываем функцию для каждой страницы getYandexImages($a->href,false); } } // находим все изображения на странице if(count($data->find('div.b-image img'))){ foreach($data->find('div.b-image img') as $img){ // выводим на экран изображение echo '<img src="'.$img->src.'"/>'; // и скачиваем его в файл file_put_contents('data/'.($i++).'.jpg',file_get_contents($img->src)); if($i>$n)exit; // завершаем работу если скачали достаточно фотографий } } $data->clear();// подчищаем за собой unset($data); } // поисковый URL $url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image'; getYandexImages($url);
Все хорошо, 200 картинок лежат в папке data. Но их размер слишком мал.
Поэтому завершающим аккордом нашей практики будет загрузка увеличенной фотографии.
Для этого определим еще одну функцию
function getBigImage($url){ $data = @file_get_contents($url); if(trim($data)=='')return false; // бывает что сайт недоступен, его фото мы не грузим $data = str_get_html($data); // находим фото if( count($data->find('#i-main-pic')) ){ $dataimg = @file_get_contents($data->find('#i-main-pic',0)->src); // собачка нужна в если сервер нам вернул 404, это выозвет Warning:, поэтому экранируем ошибки if(trim($dataimg)=='')return false; // фото не доступно, его не грузим file_put_contents( 'data/'.md5($url).'.jpg', $dataimg ); // сохрпаняем в файл } $data->clear();// подчищаем за собой unset($data); }
и слегка поправим getYandexImages
function getYandexImages($url,$findpages = true){ global $i,$n; // загружаем данный URL $data = @file_get_contents($url); $data = str_get_html($data); // очищаем страницу от лишних данных, это не обязательно, но когда HTML сильно захламлен бывает удобно почистить его, для дальнейшего анализа foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = ''; // находим URL страниц только для первого вызова функции if( $findpages and count($data->find('div.b-pager__pages a'))){ foreach($data->find('div.b-pager__pages a') as $a){ // довольно распространенный случай - локальный URL. Поэтому иногда url надо дополнять до полного if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href; // и еще дна тонкость, & надо заменять на & $a->href = str_replace('&','&',$a->href); // вызываем функцию для каждой страницы getYandexImages($a->href,false); } } // находим все изображения на странице if(count($data->find('div.b-image img'))){ foreach($data->find('div.b-image a') as $a){ if( !preg_match('#^http://#',$a->href) )$a->href = 'http://images.yandex.ru'.$a->href; $a->href = str_replace('&','&',$a->href); getBigImage($a->href); if($i++>=$n)exit; // завершаем работу если скачали достаточно фотографий echo '<script>document.getElementById("counter").innerHTML = "Загружено: '.$i.' из '.$n.' фото";</script>'; flush(); } } $data->clear();// подчищаем за собой unset($data); } // поисковый URL $i = 1; $n = 20; // будем грабить 20 картинок $url = 'http://images.yandex.ru/yandsearch?text='.urlencode('Джессика Альба').'&rpt=image'; getYandexImages($url);
Вот и все, наслаждаемся фото великолепной Джессики Альбы. Надеюсь меня простит Яндекс, ведь по сути фото грабится не с их серверов, а с прямиком с сайтов, где они лежат.
Кроме того это всего лишь демонстрация работы. Думаю никому в здравом уме, не придет в голову парсить Яндекс с помощью file_get_content. Данную библиотеку можно применять и в мирном программировании. К примеру в качестве шаблонизатора для CMS. Почему нет, с хорошим кешированием будет очень удобная штука.
При больших объемах сайтов доноров, неплохо бы разбить все на потоки.
А используя описанный мой скрипт сортировки изображений по цвету, можно собрать неплохую отсортированную базу, фотографий знаменитостей.
И, как всегда, выкладываю все исходники
UPD: Благодаря пользователю Диме, выяснилось, что библиотека обновилась 2011-07-14 и имеет версию 1.5. Нововведений не много, однако самый главный баг, с которым сталкивались все, кто пользовался библиотекой, а именно утечка памяти, устранен. На то она и версия 1.5, а не 2.0. Радует, что проект все же поддерживается и развивается. Ждем новых релизов.
Комментарии
http://cell-phones.shop.ebay.com/Cell-Phones-Smartphones-/3312/i.html?LH_BIN=1&_trkparms=65%253A10%257C66%253A4%257C39%253A1&rt=nc
&_dmpt=Cell_Phones&_ds=1&_fcid=168&_localstpos=&_sc=1&_sticky=1&_stpos=&_trksid=p3286.c0.m14
&gbr=1&_sop=15&_sc=1
Как спарсить картинки отдельно, описание отдельно, цену отдельно?
вродь все просто, откройте сайт в каком-нибудь DOM инспекторе и смотрите его структуру.
Встроенный дом инспектор есть в Опере,Хроме и IE начиная с 8 вродь. На файрфокс есть фаербаг.
$html = file_get_html("D:\Files\сайт\detail_ajax.htm");
foreach($html->find('script,link,comment') as $tmp)$tmp->outertext = '';
if(count($html->find('span[class="titleproductname]'))){
foreach($html->find('span[class="titleproductname] ') as $a){
print $title = $a->plaintext."
";
}
}
if(count($html->find('.textproductdescription'))){
foreach($html->find('.textproductdescription') as $a){
echo $a->innertext;
//при echo в цикле выводится все строки с дивов .textproductdescription
но когда присваеваю значение переменной, выводиться с переменной только одна строка, последняя
}
}
$image = $html->find('img',0)->src;
//echo $title, $text, $image;
$html->clear();
unset($html);
//подскажите как в цикле присвоить значение переменной?
или записать результат многих дивов?
что такое div.b-pager__pages a не могу разобраться
foreach($data->find('script,link,comment') as $tmp)$tmp->outertext = '';
Скажите, а здесь разве $data изменяется? разве изменения не происходят в локальной переменной $tmp ?
1 -ссылка1
2 -ссылка2
1 -ссылка3
2 -ссылка4
что мне нужно дописать в коде, чтобы выводилось так:
1 -ссылка1
1 -ссылка2
1 -ссылка3
1 -ссылка4
у нужной мне ссылки нету атрибутов, для её поиска...
Заранее спасибо.
Ждем возвращения из армии!
Если, что надо пишите, вдруг сможем помочь!
file_put_contents('data/'.($nomer).'.jpg',file_get_contents($img->src));
сохраняет нужное количество фалов в папке, но все они пустые
$img->src - содержит правильный путь к фото на сайте .. в чем беда не пойму :(
как только написал вопрос понял в чем проблема - оказывается в $img->src хранился относительный путь в фото, банально добавил название домена и все ок.
спасибо за помощь :) !
Имеется следующий кусок кода:
Автор:А.Мецгер
ISBN:978-5-378-02142-0
Переплет:
Страниц:16
Формат:200х280
Со СПАНами все понятно, но нужно присвоить переменным значения,которые между тегами /strong и br или /strong и /span (для последнего случая)
Например:
MyAuthor = "А.Мецгер"
MyBinding = ""
Причем вся строка, например (ISBN:978-5-378-02142-0
) может отсутствовать.
Заранее благодарен.
Добрый день!
Имеется следующий кусок кода:
{span class="block"}
{strong}Автор:{/strong}А.Мецгер{br}
{strong}ISBN:{/strong}978-5-378-02142-0{br}
{strong}Переплет:{/strong}{br}
{strong}Страниц:{/strong}16{br}
{strong}Формат:{/strong}200х280
{/span}
Со СПАНами все понятно, но нужно присвоить переменным значения,которые между тегами /strong и br или /strong и /span (для последнего случая)
Например:
MyAuthor = "А.Мецгер"
MyBinding = ""
Причем вся строка, например ({strong}ISBN:{/strong}978-5-378-02142-0{br}) может отсутствовать.
Заранее благодарен.
/pages/13674/1367437.shtml
/pages/14072/1407291.shtml
/pages/14107/1410773.shtml
/download.php?id=1420841.mp3
/reklama.shtml
/rp/list.php
/spages/events.shtml
Каким образом их этого файла выбрать имено строку: /download.php?id=1420841.mp3 ??? Спасибо заранее.
Я думаю тут все понятно) а вообще это элементарная работа со строками
P.S. Возможно что-то не работает, я только пришел с армии =)
надо с этой страницы:
http://zaycev.net/pages/14208/1420841.shtml
выдрать ссылку **********.mp3 она одна там, выкладывайте вариант с Z Кошельком... СПС
....
ErrorException [ Fatal Error ]: Allowed memory size of 134217728 bytes exhausted (tried to allocate 59008046 bytes)
фреймворк kohana
Fatal error: Maximum execution time of 30 seconds exceeded in simple_html_dom.php on line 70
Кстати, оригинальные примеры с Джессикой Альбой также не дорабатывают до конца, если увеличить количество выкачиваемого с 20 до, хотя бы, 500.
парни, а совсем тугой можно помочь:)?
мне надо простейшую программульку -выдрать номера телефонов из одной категории сайта обьявлений.
никаких наворотов больше не надо - только голый текст с номерами тел. в столбик - шеф поручил составить такой список - я за день только 200 номеров собрала.
была бы очень благодарна за помощь:)
Подскажите пожалуйста как распарсить и вывести нужный текст и соответствующую ему нужную ссылку. Всю голову сломал.
Я ведь написал, что серьезные парсеры не пишутся через file_get_contents, нужно использовать curl
все отлично работает, почитайте эти статьи Авторизация на сайте при помощи curl php и Многопоточные парсеры
1. Обязательно нужна авторизация на сайте, что парсить данные?
2. Не понял тот пример что вы написали выше. Как он применим к моей ситуации.
Попробовал сделать следующим образом:
Можно как то по подробнее рассмотреть мою ситуацию?
cURL support - enabled
cURL Information -libcurl/7.20.0 OpenSSL/0.9.8o zlib/1.2.3
пропала ошибка 500, с но все равно все пусто. Я если это проделать с yandex то парситься на ура! Подскажите что нужно сделать?:)
авторизовыватся на сайте не нужно, дал линк на статью чтобы почитали про курл и общие принципы
Значит нужно произвести замену в на Правильно понял вас?
Я создал cookie.txt в той же директории где и лежит index.php, соответственно подправил путь:
т.е. получается:
Но у меня все равно появляется сообщение с кодом 500. Честно говоря уже перечитал все не на раз. И хочу разобраться, но как то еще запутаннее получается.
создавать cookie.txt не надо, он автоматически создастся, если сервере вернет куки
$ch = curl_init(); - адресс можно не прописывать, если он пишется ниже
curl_setopt($ch, CURLOPT_URL, $url );
в вашем коде в 15 строчке ошибка, нет слеша после dirname(__FILE__)
ошибка 500 возникает скорее всего потому, что в скрипте есть ошибка, а ваш сервер настроен на то чтобы не выдавать ошибок php, почитайте статейку как включить показ ошибок в php
Очень благодарен!
А не подскажете дальше что изучать, чтобы вывести нужный мне контент, а не все в подряд?
Не могу понять что именно нужно вставить в $html = str_get_html('__________');
Пробовал и имя функции - request и $url, толку нет.
Вставлял после : return data;
str_get_html - на вход принимает строку
file_get_html - на вход принимает url либо адрес файла
Подскажите как дальше поправить адреса ссылок. Сейчас ссылки ссылаются на не существующие адреса, как их заменить на свой адрес? Не могу понять как сделать связку со следующим:
Пробовал сделать следующее, но видимо не то: Прошу очередной раз вашей помощи:)
Сделал так, потому что ваш вариант работает только с конкретной ссылкой:)Теперь надо понять, как это дело украшать)
Когда задавал вопрос, подписался на уведомления о комментариях, но как теперь отписаться?
Кажется помогло
Вот такая ситуация:
У меня CMS на win1251, а сайт донер на UTF8, парсинговый скрипт соответственно пишется в кодировке UTF-8. Как только этот скрипт подгружаю на сайт в нужное место, весь спарсенный контент появляется ввиде символов. Как сделать так чтобы Данные нормально отображались и при кодеровке win1251&
работает не всегда, т.к. многих символов в win, которые есть в utf, попросту нет.
еще есть mb_convert, загуглите
еще на php.net под описанием iconv куча велосипедных реализаций, если iconv не справляется то попробуйте их, по опыту скажу, что 100% рабочего решения вы не найдете, у каждого сайта свои тонкости. Это та еще проблемка
не один из вариантов не проверял, и думаю кусок кода вы привели для примера, но думаю ход мыслей понятен
Столкнулся с проблемой кодировки :(
Есть страница: http://beward.ru/goods/?dir1=1&dir2=14 в кодировке "windows-1251"
Есть рабочая ф-ция
Читаем:
$url = 'http://beward.ru/goods/?dir1=1&dir2=14';
$content = curl_gets_NEW($url);
Все нормально!
Далее, читаем: $HTMLtext = str_get_html($content,true,true,'windows-1251');
и получаем вместо русских букв пустоту :(
Если не указывать кодировку при "str_get_html", то появляется белеберда.
mb_detect_encoding тоже не помогает. Вообще вошел в ступор :(
А вообще я бы советовал работать с чистым DOMDocument через xpath без использования дополнительных библиотек.
Про приведение к единой кодировке я в курсе, но ни iconv(), ни mb_detect_encoding() не помогали.
Все оказалось банально просто, сайт говорит что кодировка win-1251, а реально контент на нем в win-1252.
В тэгах есть еще вложенные , это я скопировал не раскрыв их.
Мне нужно занести в массив, делаю так:
И в $cat добавляются все тэги и вложенные все, а мне надо только которые первые, не глубже. Как можно это сделать?
Может из-за того, что страница перекодирована из win1251 в UTF-8
iconv не работает
Автору спасибо. Как раз необходим был хороший пример помимо мануала.
Я в парсинге новичок, но появилась такая необходимость.
Подскажите, как правильно организовать парсер новостных сайтов. Я так понимаю, делать это с сервера, на котором расположен сайт агригатор не стоит?
Всех с Новым годом! Ура!
Помогите, запутался, не пойму что не так (новичёк-самоучка, что с меня взять))).
Вопрос в следующем.
Извлек страницу, распарсил этой чудной библиотекой, на экран все выводится в лучшем виде, но при записи в файл (стоит задача сохранить в один файл все ссылки) получается не 1 файл со списком N-ссылок, а N-файлов с ОДНОЙ каждой последующей ссылкой. Уже всякие варианты перебрал, менял циклы всякие, получается либо последняя ссылка в одном файле, либо как уже описал. Вот кусок кода где происходят итерации (подскажите ошибку):
$html= str_get_html($str);
$n = count($html->find('a, href'));
foreach($html->find('script,comment,image,img, png, jpg') as $tmp)$tmp->outertext = '';
if(count($html->find('a'))){
$i=0;
foreach($html->find('a') as $links){
echo $links->href .' ('. $links->innertext. ')
';
$result = file_put_contents(($i++).'Links.txt', $links->href);
if($i==$n)break;
if ($result) echo 'Данные в файл успешно занесены.';
else echo 'Ошибка при записи в файл.';
какой код тут надо сделать что бы взять цифры с 246.62 и 4.87 по отдельности в массив
Coğrafi Kuzeyden Kıble Açısı : 246.62 ° dir
2013 Senesi Ortası İçin Sapma Açısı : 4.87 ° dir
Pusula Kuzeyine Göre Kıble Açısı : (246.62) - (4.87) = 241.75 ° dir.
Учите регулярные выражения, хоть я и написал что только с помощью них парсить нельзя, но и совсем без них парсить сложновато
пишет!
2 дня перечитал все не получается! тестирую на денвере!
http://www.namazvakti.com/QAbout.php?cityID=8408 сайт откуда нужно взять 246.62 и 4.87
помогите пожалуйста! в долгу не останусь!
include('simple_html_dom.php');
$html = file_get_html('http://www.namazvakti.com/QAbout.php?cityID=8408');
if(preg_match_all('#[\d]+\.[\d]+#',$text,$list))
foreach($list as $dig)
echo $dig;
Например:
include_once('C:/Users/Public/Apache/htdocs/Parser/simple_html_dom.php');
echo file_get_html('http://www.yandex.ru/')->plaintext;
Ругется:
PHP Fatal error: Call to undefined function mb_detect_encoding() in C:\Users\Public\Apache\htdocs\Parser\simple_html_d om.php on line 1234
Ну а сам php_mbstring.dll у меня лежит в C:\Program Files\PHP\ext, я так думаю это верно.
Подскажите а почему выводятся кракозябры типа:
Секс знакомства ru -
после парсинга
http://www.php.net/manual/ru/book.tidy.php
include_once('C:/Users/Public/Apache/htdocs/Parser/simple_html_dom.php');
echo file_get_html('http://www.yandex.ru/')->plaintext;
пишет стандартное сообщение:
Этот веб-узел не может отобразить эту страницу
HTTP 500
Возможные причины:
Веб-узел находится в состоянии обслуживания.
На этом веб-узле обнаружена программная ошибка.
Попробуйте сделать следующее:
Повторите ввод адреса.
Перейдите на предыдущую страницу.
Подробнее
Эта ошибка (HTTP 500 - внутренняя ошибка сервера) означает, что на веб-узле, который вы хотите посетить, имеется проблема, препятствующая отображению нужной веб-страницы.
Тое есть у этих сайтов стоит защита от парсера?
Отлично написали статью, с нее начал знакомиться с парсингом.
Есть задачка:
Лажа в том, что атрибут href для ссылки получить не удастся... По идее все портит json, но как это обойти?
Есть идеи?
...
...
как получить с помощью Simple HTML DOM значения атрибутов Code, HighPic, Title?
Заранее благодарен :)
...
Product Code="1" HighPic="namepic.jpg" Title="titleproduct"
...
Подскажите, взял такой скрипт:
Хочу в htef="" подставить адрес картинки, но у меня не выходит.
Не могу правильно составить поиск и вытянуть в href.
Как правильно это сделать?
Спасибо.
нo в href мне надо поместить не src img, а адрес ссылки в которую заключен сам тег img.
тоесть парсю вот это:
просто примеров не нашол, либо не внимательно смотрел, как высунуть в одном цыкле вывести две переменные и подставиь их.
Получаеться img есть, а как второй парсинг подставить и правильно вытянуть, не могу.
$b->parent() Возвращает родительский элемент.
$b->parent() ->href
значения span с классами today, wday нужно найти и поместить в:
значения td с классом time нужно найти и поместить в:
Можно искать по одному из классов которые прописаны в атрибуте class.
К примеру
можно найти
Но тем не менее столкнулся с проблемой:
Пробуем парсить html:
Для этого использую:
Два момента интересны:
1. Если не заменять(закоментить prepareForDOM) то выдает ошибку
arning: mb_strpos() [function.mb-strpos]: Offset not contained in string ... on line 1570
2. Даже после предварительной обработки div не находится. $lets - пустая на выходе.
Очень надеюсь на вашу помощь. Сам уже и не знаю что делать - на другую библиотеку что-ли переходить...
2. если убрать элемент русский текст из title, то работает нормально, даже если русский текст есть в теле страницы.
Форматирование теряется, нет перехода на новые строки. Скажите, пожалуйста, как исправить?
к любому конкретному примеру можно подобрать регулярку, но стоит слегка отойти от это примера и все сломается. simple_html_dom такие перепады не страшны
Допустим есть сайт site.com
на нем 100 ссылок. И на каждой из тех ссылок еще по 20 ссылок. Получается мне нужно спарсить 2000 страниц в 1 базу. Допустим картинка, описание, телефон.
Второй вопрос: Как парсить только новую информацию на сайте. Например на сайте СМИ обновляются статьи. Я хочу заходить туда раз в сутки(через cron) и забирать только новые статьи в свою базу(а так же если какая то статья из старых обновилась на сайте то ее тоже обновить у себя).
Третий вопрос: Как работать через proxy?
Вообщем хочу для себя создать мощный парсер(с PHP, mysql, HTML работаю давно). Статья лучшая что пока находил по парсерам. НО у меня даже тот пример с Джесикой альбой не получился. Я просто скопировал код и всеравно пустая страница.
Чтобы знать какие статьи у вас новые, надо запоминать старые. Я обычно если парсил статью, в отдельное поле складывал md5(ее урл) потом сравнивал. Если в базе уже есть такой хеш, то не грузил статью.
Через прокси умеет работать curl
код:
а как на счет h2[class=description]?
"Beats Audio Original phone HTC ONE V T320e 4G ROM WIFI GPS 5MP 3G GSM smartphone free shipping
Product description:
General 2G Network GSM 850 / 900 / 1800 / 1900
3G Network HSDPA 850 / 900 / 2100
SIM Mini-SIM
Announced 2012, February
Status Available. Released 2012, April
Body Dimensions 120.3 x 59.7 x 9.2 mm (4.74 x 2.35 x 0.36 in)
Weight 115 g (4.06 oz)
Display Type Super LCD2 capacitive touchscreen, 16M colors
Size 480 x 800 pixels, 3.7 inches (~252 ppi pixel density)
Multitouch Yes " и т.д.
как вывести именно это описание до картинок? после этого описания выходят картинки.
"Fatal error: Call to a member function next_sibling() on a non-object in Z:\home\test1.ru\www\index.php on line 32"
32 линия и есть эта линия что вы написали.
когда достаточно
во сторых в html похоже ошибка, и найти он его не может. h2.desc... нет в дом. Парсите этот кусок регуляркой. только не спрашивайте какой.
и мне нужно сделать выборку именно по нему, так как нужный текст на странице встречается только после него.
Например я делаю так:
Но ничего не выводится
А если делать так:
то выводится ненужный мне текст, так как тег встречается на странице несколько раз.
Вариант с указанием позиции тега так:
тоже не подходит, так как нужный текс может встречаться на страницах под разной позицией.
Подскажите, как сделать выборку, если на нужной мне странице стоит тег:
и мне нужно сделать выборку именно по нему, так как нужный текст на странице встречается только после него.
Например я делаю так:
foreach($html->find('td[class="alt1" colspan="2"] ') as $element1)
echo $element1;
Но ничего не выводится
А если делать так:
foreach($html->find('td[class="alt1"] ') as $element1)
echo $element1;
то выводится ненужный мне текст, так как тег встречается на странице несколько раз.
Вариант с указанием позиции тега так:
foreach($html->find('td[class="alt1"] ', 2) as $element1)
echo $element1;
тоже не подходит, так как нужный текст может встречаться на страницах под разной позицией.
Вот пример html кода http://i-css.ru.s3.hhos.ru/example.txt
Там только один раз встречается тэг
И нужно спарсить сразу то что следует за ним:
Но ничего не выходит :(
Я хочу парсит сайт ism.uz но столькнуля проблемой не ругайтес ёще я новичок в этом деле ))
Например я хочу парсит страницу http://ism.uz/letter/f/ ну там много ссылок,как парсит эти ссылки? ато не хочу войти каждуйу ссылку и парсит ))
Большинство сайтов можно спарсить с помощью curl однако в более сложных случая приходится эмулировать браузер (IE COM PHP) или других хитрых методов
Я новичек в php и вообще в программировании в целом, меня всегда интересовал этот вид ТВОРЧЕСТВА и сейчас мне необходимо сделать парсер который удовлетворял бы моим требованиям и спомощью данной статьи я боюсь что это у меня получится.
Собственно вопрос заключается в следующем.
мне необходим парсер каторый мог бы забирать теги из например с xvideos[dot]соm с помощью метода find. Хотелось бы узнать правельный ли у меня ход мысли или это лучше реализовать с посощью других методов. Еще раз Большое спасибо за статью и заранее благодарю Вас за ответ.
Я новичок, поэтому не серчайте, пожалуйста! :)
Не могли бы Вы подсказать, как получить данные в следующем примере:
Есть таблица:
<table width=820 cellspacing=1 cellpadding=3 border=0>
Есть код, которые не работает к сожалению, а по идее должен был бы работать, но, видимо, я ошибаюсь в чем-то, а в чем понять пока не могу:
в массив ничего не заносится к сожалению.
Не могли бы подсказать, что не так делаю?
Благодарю!
Вам нужна file_get_html как минимум
foreach($html->find('a[href*=http]') as $link)
echo $link.'
';
возвращает объект? Я же нигде не указывал индексы, т.е должен быть массив. Мне нужно результат работы парсера записать в csv файл, а функция fputcsv, что очевидно, не может принимать объект. Как быть? Спасибо
я пробую
$rated = array();
$main_cats = $html->find('.big_menu a');
$rated['main_cats'] = $main_cats;
print_r($rated);
у меня какая-то жесть выводится а не мой массив
http://imagizer.imageshack.com/img538/5411/R6gpci.png
foreach($data->find('div.top_menu div.inner ul') as $ul)
{
foreach($ul->find('li') as $li)
{
echo $li;
}
}
но ничего не находит
Спасибо за ваш сайт, нашел много полезного.
В парсинг окунулся пару дней назад, знаний мало.
Я спарсил таблицу:
В таблице присутствуют ссылки. От них мне нужно избавится.
Когда ставлю вместо innertext - plaintext, то ссылки уходят, но так же уходят и картинки, которые присутствуют в таблице, поэтому такой вариант не подходит.
Прошу помощи, заранее спасибо.
Подскажите, пожалуйста!
Подскажите как выбрать текст после закрывающего тега</h2>?
Пытаюсь так, но не получается. Пните в нужном направлении! Заранее спасибо!
Только вот интересно когда такой случай
Когда есть к примеру " <b>Название: </b>Название1<br />"
Как вытянуть "Название1"?
<div id="bar"><ul>
<li><a href="/zakon/2014/">не нужная ссылка 2014</a></li>
<li><a href="/">не нужная ссылка</a></li>
<li><a href="/zakon/">не нужная ссылка</a></li>
<li><a href="/catalog/">не нужная ссылка</a></li>
<li><a href="/forum/">не нужная ссылка</a></li>
</ul></div>
<div><a href="/info/423/">нужная ссылка</a></div>
<div>нужный текст</div>
<div>нужный текст</div>
<div style="margin-top: 10px"></div>
<div><a href="/info/472/">нужная ссылка</a></div>
<div>нужный текст</div>
<div>нужный текст</div>
<div style="margin-top: 10px"></div>
<div><a href="/info/732/">нужная ссылка</a></div>
<div>нужный текст</div>
<div>нужный текст</div>
<div style="margin-top: 10px"></div>
в пхп прописываю так:
но что то не выходит, помогите плиз!
Вы сначала получаете список тегов <a>, а потом в них ищете <li> хотя в HTML которые вы привели в качестве примера во всех тегах <a> только текст.
Смысл самого кода написанный вами непонятный.
Всё в роди нормально, но хочу чтобы можно было указывать определённое количество новостей, кто знает подскажите пожалуйста.
В переменной $ret ноль. Я ставила другие теги. Так же всё по нулям. Точнее ищет только html тег
Вообще блок с картинками отдается AJAX запрсом, поэтому тут таким простым подходом не ограничишься.
потому что из-за них память тоже засоряется
К примеру, на загружаемой странице есть скрипт ниже:
var flashvars = {
uid: 'videoplayer',
file: 'http://',
poster: 'http://...jpg',}
var params = {
bgcolor: '#000', ...}
$html = file_get_html('http://пример.php'); //Загружаю страницу
foreach($html->find('script') as $element) //парсю все скрипты
echo $element->plaintext . ''; //а дальше не знаю как вывести код скрипта, либо вывести значение переменной flashvars:file
Можно ли такое проделать с этим парсером?
if(preg_match('#file:'(.*)',#Uus', $element->plaintext, $list)) {
echo $list[1];
}
У меня вопрос:
Можно ли в выборке попросить чтобы название класса начинолось на x и кончалось на y?
Что-то вроде этого:
div[class^=x && $=y]
Есть сайт с авторизацией,авторизированому пользователю доступен контент на определенной странице этого сайта,часть этого контента автоматически обновляется 1 раз в час,необходимо разместить часть этого контента на странице другого сайта с теми же функциями автоматического обнавления.
Хожу вокруг да около,вроде рядом,но не могу понять как реализовать.
Пробовал так:
$bgPos = $digit->getAttribute('style');
echo $bgPos. '';
И так:
echo $digit->style . '';
Почему то не вышло(
Код:
При обработке выдает ошибку:
Fatal error: Call to a member function find() on a non-object in C:\OpenServer\domains\localhost\index.php on line 5
Как я понимаю в следствии пустого обьекта.
Прошу помочь составить запрос.
foreach($data->find('h2.zagolovki') as $txt)
{
$htmls = $txt->innertext;
$a++;
if($a>$n)break;
}
foreach($data->find('div.shortimg a img') as $img)
{
$arr = explode("/", $img->src);
echo $htmls.'';
$i++;
file_put_contents('uploads/'.($arr[5]).'.jpg', file_get_contents($img->src));
if($i>$n)break;
}
результат работы, для каждой картинки берется одинаковый заголовок. Что не так?
например Игры онлайн как заменить http://igroflot.ru/ на http://site.ru?
К сожалению php не знаю, а в комментариях похожего случая не было.
Самое верхнее название исполнителя и трэка, вот часть из кода:
Цитата: Отсюда нужно забрать "KEIFER, Tom - Blue Christmas"
Спасибо!
KEIFER, Tom - Blue Christmas
из этого нужно забрать "KEIFER, Tom - Blue Christmas"
Было бы интересно увидеть настолько же подробный мануал по парсингу сайтов с защитой.
Может быть есть готовые библиотеки с эмуляцией браузера? Наподобие зеннопостера только на PHP
Пробовал
$html->find([text()='Employment type'])
$html->find('Employment type')
не получается(
Часто надо выдернуть какой-то контент из текста, хотя по ДОМу лазиить лучше конечно селекторами.
С другой стороны.. оба этих метода легко крошатся при модификациях страниц.
А так да, я совмещаю и регулярки и дом. Если нужно выдернуть одно единственное значение то конечно лучше регулярки
Код:
foreach($html->find('#allEntries') as $a){
$jj = $a->plaintext;
$zz = $a->find('.vnewtop')->plaintext;
find('.vnewtop') ищет все элементы с классом vnewtop, и добавляет в массив, а plaintext выводит чистый текст уже для конкретного элемента.
Решение:
$zz = $a->find('.vnewtop');
foreach($zz as $zzz){
echo "Тест zzz: " . $zzz->plaintext;
}
примерно так, но могу и ошибаться)
$img_src = $img->src;
echo $img_src->outertext;
Почему не работает? Пишет: Trying to get property of non-object in
Хочу заменить урл в src на свой, как это сделать?
$link = $_POST['page'];
include('simplehtmldom/simple_html_dom.php');
$html = file_get_html('http://weplay.tv/csgo/tournaments');
$rated = array();
foreach($html->find('td') as $t1)
echo $t1->plaintext;
#print_r($rated);
$html->clear();
unset($html);
хочу спарсить таблицу. Но класс есть только у первого и последнего столбца. Как сделать так, чтобы парсились только те строки, у которых в 5 стобце класс status complete ?
Есть сайт http://game-tournaments.com/dota-2/prodota-cup-sea-8 Заходя на сайт через curl он видит уже нарисованную страницу, на сайте можно переключать букмекера представляющего коэффициенты тогда идет POST запрос на ajax и потом в php обрабатывается запрос и отрисовываются новые коэффициенты без перезагрузки страницы. К сожалению curl видит только начальную страницу, возможно ли как то сделать так что бы curl заходил на страницу менял переключатель и только потом забирал страницу? пробовал генерировать curl с POST запросом без успешно, видимо форма его не отлавливает там. Подскажите направление к решению если оно конечно есть.
Написал мини-парсер по данной статье, но очень много времени занимает обработка одной ссылки (3-4 секунды). Это нормально или я что-то делаю не так?
для https://zanimayonlayn.ru/mikrokredity/chestnoe-slovo.php
$data = file_get_html($url);
результат при попытке искать в $data Call to a member function find()
не создало объект
почему?
кривой html код ?
Есть вопрос: есть сайт, просит логин и пароль для входа и только потом дает нужную мне страницу.
Как это автоматизировать разобрался, curl помог, получаю следующую страницу с нужным мне контекстом.
На этой второй странице есть список, при изменении которого меняются данные страницы.
Страницу с этим списком в переменную получаю. Поиском foreach($html->find("select")as $s) успешно этот список нахожу. Но не могу понять как заставить его изменить значение.....
В отладчике chrome все просто:
1) var mlist = $$("select")
2) mlist[0].value='нужное мне значение'
3) mlist[0].onchange()
и я попадаю на нужную мне статистику, но это в консоли chrome, а как сделать пукнт 2 и 3 через php ?
Если я кладу файл "simple_html_dom.php" в корень, то все работает, если кладу его к примеру в common/parser то получаю "пустую страницу" без контента
Т.е. я так понимаю что файлы плагина между собой как то связаны "путями" или как то "завязаны" на корне сайта
Пример boogiwoo.ru - внизу парсятся отзывы с вк
Код:
<iframe width="100%" height="350" src="https://www.youtube.com/embed/BnR1MfWbKhM" frameborder="0" allowfullscreen=""></iframe>
Мне надо вырезать из этого кода Код:
width="100%" height="350"
к записи на сайт.Можно ли вырезать Код:
width="100%" height="350"
Если это возможно, напишите код скрипт с помощью которого я это могу сделать.
Код:
<iframe width="100%" height="350" src="https://www.youtube.com/embed/BnR1MfWbKhM" frameborder="0" allowfullscreen=""></iframe>
Мне надо вырезать из этого кода Код:
width="100%" height="350"
к записи на сайт.Можно ли вырезать Код:
width="100%" height="350"
Если это возможно, напишите код скрипт с помощью которого я это могу сделать.