Очень многие из нас хотели бы быстро наполнить сайт контентом. Я покажу вам, как несколько тысяч материалов собрать всего лишь за несколько часов.
Парсер на php - раз плюнуть!
Приветствую вас, наши дорогие читатели. Сегодня решил написать сложную статью про парсеры (сбор информации со сторонних ресурсов).
Скажу сразу, что вам потребуется знание основ программирования на php. В противном случае почитайте теории. Я не буду рассказывать азы, а сразу полезу показывать всё на практике.
Шаг 1 - PHP Simple HTML DOM Parser
Для парсинга сайтов мы будем использовать простецкую библиотечку под названием PHP Simple HTML DOM Parser, которую вы сможете скачать на сайте разработчика. Данный класс поможет вам работать с DOM-моделью страницы (дерево документа). Т.е. главная идея нашей будущей программы будет состоять из следующих пунктов:
Скачиваем нужную страницу сайта
Разбираем её по элементы (div, table, img и прочее)
В соответствии с логикой получим определённые данные.
Давайте же начнём написание нашего php парсера сайтов.
Для начала подключим нашу библиотеку с помощью следующей строки кода:
1
include'simple_html_dom.php';
Шаг 2 - Скачиваем страничку
На этом этапе мы смогли подключить файл к проекту и теперь пришла пора скачать страничку для парсинга.
В нашей библе есть две функции для получения удалённой страницы сайта. Вот эти функции
str_get_htm() - получает в качестве параметров обычную строку. Это полезно, если вы стянули страничку с помощью CURL или метода file_get_contents. Пример использования:
1
$seo=str_get_html('<html>Привет, наш любимый читатель блога SEO-Love.ru!</html>')
file_get_html() - здесь же мы передаём в качестве параметра какой-то url, с которого нам потребуется скачать контент.
1
$seo=file_get_html('http://www.site.ru/');
После скачивания каждой страницы вам требуется подчищать память, дабы парсеру было легче работать и не так сильно грузился ваш сервер. Эта функция вызовется с помощью данного кода:
После получения DOM-модели мы можем приступить непосредственно к поиску нужного элемента-блока в полученном коде.
Большая часть функций поиска использует метод find(selector, [index]). Если не указывать индекс, то функция возвратит массив всех полученных элементов. В противном случае метод вернёт элемент с номером [index].
Давайте же приведу вам первый пример. Спарсим мою страничку и найдём все картинки.
Если что-то пошло не так, то прошу отписаться в комментариях. Здесь очень кстати будет мой предыдущий материал Запутываем PHP-код без зазрения совести. Полезно для тех, кто программирует как ниндзя. Больше не отвлекаюсь, идём дальше.
Шаг 4 - Параметры поиска
Надеюсь все уже поняли, что в метод find() можно писать как теги ('a'), так и id'шники ('#id'), классы ('.myclass'), комбинации из предыдущих элементов ('div #id1 span .class'). Таким образом вы сможете найти любой элемент на странице.
Если метод поиска ничего не найдёт, то он возвратит пустой массив, который приведёт к конфликту. Для этого надо указывать проверку с помощью фукнции count(), которую я использовал выше в примере.
Также вы можете производить поиск по наличию атрибутов у искомого элемента. Пример:
1
2
3
4
5
6
7
8
9
10
11
//Найдём все изображения с шириной 300$seo->find('img[width=300px]');//Найдём изображения, у которых задана ширина$seo->find('img[width]');//Поиск по наличию нескольких классов$seo->find('img[class=class1 class2]');//<img class="aclass1 class2"/>//Ищем несколько тегов вместе$seo->find('div, span, img, a');//Поиск по вложенности.//В div ищем все спаны, а в спанах ссылки$html->find('div span a');
Замечу, что у каждого вложенного тега так же есть возможность поиска!
Есть много вариантов поиска по атрибутам. Перечислять не стану, для более полного руководства прошу пройти на сайт разработчиков :)
Обычный текст, без тегов и прочего, можно искать так find('text'). Комментарии аналогично find('comment').
Шаг 5 - Поля элементов
Каждый найденный элемент имеет несколько структур:
$seo->tag Прочитает или запишет имя тега искомого элемента.
$seo->outertext Прочитает или запишет всю HTML-структуру элемента с ним включительно.
$seo->innertext Прочитает или запишет внутреннюю HTML-структуру элемента.
$seo->plaintext Прочитает или запишет обычный текст в элементе. Запись в данное поле ничего не поменяет, хоть возможность изменения как бы присутствует.
Примеры:
1
2
3
4
5
6
7
$seo=str_get_html("<div>first word <b>second word</b></div>");echo$seo;// получим <div>first word <b>second word</b></div>, т.е. всю структуру$div=$seo->find("div",0);echo$div->tag;// Вернет: "div"echo$div->outertext;// Получим <div>first word <b>second word</b></div>echo$div->innertext;// Получим first word <b>second word</b>echo$div->plaintext;// Получим first word second word
Эта возможность очень просто позволяет бегать по DOM-дереву и перебирать его в зависимости от ваших нужд.
Если вы захотите затереть какой-либо элемент из дерева, то просто обнулить значение outertext, т.е. $div->outertext = ""; Можно поэксперементировать с удалением элементов.
P.S. Я обнаружил проблему с кодировками при очистке и всяческими манипуляциями с полем innertext. Пришлось использовать outertext и затем с помощью функции strip_tags удалял ненужные теги.
Шаг 6 - Дочерние элементы
Разработчики данной библиотеки позаботились так же и о том, чтобы вам было легко перемещаться по дочерним и родительским элементам дерева. Для этого ими были любезно созданы следующие методы:
$seo->children ( [int $index] ) Возвращает N-ый дочерний элемент, иначе возвращает массив, состоящий из всех дочерних элементов.
$seo->parent() Возвращает родительский элемент искомого элемента.
$seo->first_child() Возвращает первый дочерний элемент искомого элемента, или NULL, если результат пустой
$seo->last_child() Возвращает последний дочерний элемент искомого элемента, или null, если результат пустой
$seo->next_sibling() Возвращает следующий родственный элемент искомого элемента, или null, если результат пустой
$seo->prev_sibling() Возвращает предыдущий родственный элемент искомого элемента, или null, если результат пустой
Я особо не пользовался этими возможностями, потому что они ещё ни разу не пригодились мне. Хотя один раз при разборе таблицы использовал, потому что они структурированы, что делает разбор очень простым и лёгким.
Шаг 7 - Практика
Перейдём к практике. Я решил отдать вам на растерзание одну функцию, что использовал при написании парсера текстов песен на один из своих сайтов. Пытался досконально подробно описать код. Смотрите комментарии и задавайте вопросы.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
publicfunctionparser_rock_txt(){$i=0;$new_songs=0;//номер категории, чтобы хранить в базе. У меня Рок = 1$category=1;//Скачиваем страничку с сайта Rock-Txt.ru$data=file_get_html('http://rock-txt.ru/');//нашли хотя бы одну ссылку на песни по буквам (проходим навигацию)if(count($data->find('div.a-z a'))){//пробежим по всей навигацииforeach($data->find('div.a-z a')as$a){//Выводим букву, которую парсимecho('Текущая буква - '.$a->plaintext.'<br />');//нашли список всех исполнителей$data_vocalist=file_get_html("http://rock-txt.ru".$a->href);//если есть хотя бы один исполнительif(count($data_vocalist->find('#dle-content div.full-news a'))){foreach($data_vocalist->find('#dle-content div.full-news a')as$vocalist){//приводим название исполнителя к нижнему регистру$vocalist->plaintext=mb_strtolower((mb_convert_encoding(($vocalist->plaintext),'utf-8',mb_detect_encoding(($vocalist->plaintext)))),'UTF-8');//получаем id исполнителя из моей базы$id_vocalist=$this->songs_model->check_vocalist(trim($this->db->escape($vocalist->plaintext)),trim($this->db->escape($this->translit($vocalist->plaintext))),$category);//Нашли все песни исполнителя$data_songs=file_get_html($vocalist->href);//если есть хотя бы одна песня такого исполнителя - идём дальшеif(count($data_songs->find('#dle-content div.left-news-band a'))){foreach($data_songs->find('#dle-content div.left-news-band a')as$songs){//Получим название песни. Удалим название исполнителя.$name_song=substr(preg_replace('/\s\s+/',' ',$songs->plaintext),strlen(trim($vocalist->plaintext))+1);$name_song=trim($name_song);//приводим название песни в нижний регистр$name_song=mb_strtolower((mb_convert_encoding(($name_song),'utf-8',mb_detect_encoding(($name_song)))),'UTF-8');//Транслитизируем название песни (моя самописная функция)$name_song_translit=$this->translit($name_song);//Отсекаем все пустые названияif($name_song==''||$name_song_translit=='')continue;//Проходим по всем страницам навигации (пейджер, постраничная навигация)$num_page=0;foreach($songs->find('div.navigation a')as$num){//если число - сравниваем, а не нашли ли мы ещё одну страницу навигацииif(is_int($num->plaintext)){if($num->plaintext>$num_page)$num_page=$num->plaintext;}}echo$num_page.'<br />';//загрузим текст песни$text_songs=file_get_html($songs->href);if(count($text_songs->find('div.full-news-full div[id] p'))){foreach($text_songs->find('div.full-news-full div[id] p')as$text_song){//очищаем всякие ненужны ссылки и спаныforeach($text_song->find('span')as$span){$span->outertext='';}foreach($text_song->find('a')as$a){$a->href='';$a->outertext='';}//выводим исполнителя, песню и текстecho$name_song.'<br />';echo$songs->href.'<br />';echo$text_song->outertext.'<br />';$text_song->outertext=preg_replace("/(<br[^>]*>\s*)+/i","<br />",$text_song->outertext,1);//вставляю в мою базу текст песни и исполнителя (самописная функция)$result=$this->songs_model->check_song(trim($this->db->escape($name_song_translit)),trim($this->db->escape($name_song)),trim($this->db->escape($id_vocalist)),trim($this->db->escape_str(preg_replace("#(:?<br />){2,}#i","<br />",strip_tags($text_song->outertext,'<br /><br><b><strong><p>')))));//если добавили - увеличим счётчик новых песенif($result!=-1){$new_songs++;}$i++;//выйдем, тут всякие косяки бываютbreak;}}//теперь аналогично пробегаем по остальным страницамif($num_page>0){$text_songs=file_get_html($songs->href.'page/'.$num_page);if(count($text_songs->find('div.full-news-full div[id] p'))){foreach($text_songs->find('div.full-news-full div[id] p')as$text_song){foreach($text_song->find('span')as$span){$span->outertext='';}foreach($text_song->find('a')as$a){$a->href='';$a->outertext='';}echo$name_song.'<br />';echo$songs->href.'<br />';echo$text_song->outertext.'<br />';$text_song->outertext=preg_replace("/(<br[^>]*>\s*)+/i","<br />",$text_song->outertext,1);$result=$this->songs_model->check_song(trim($this->db->escape($name_song_translit)),trim($this->db->escape($name_song)),trim($this->db->escape($id_vocalist)),trim($this->db->escape_str(preg_replace("#(:?<br />){2,}#i","<br />",strip_tags($text_song->outertext,'<br /><br><b><strong><p>')))));if($result!=-1){$new_songs++;}$i++;break;}}}}}}}}}return"<br />Парсер сайта rock-txt.ru завершён. Спарсено песен всего ".$i.", из них новых ".$new_songs." ";}
Получилась вот такая здоровая функция, которая парсит тексты песен с сайта о роке. Написал её я за час. Спарсил 10 000 текстов песен. Думаю, что руками вы бы набивали такую базу очень и очень долго :-)
Замечу, что в коде много самописных функций, которые используются для вставки в мой базу. Эти функции у каждого могут быть индивидуальными, так что в этом я вам не помощник. Либо обращайтесь за помощью в комментарии. Всегда буду рад помочь!
Пока что на этом всё. В следующих уроках расскажу, как можно быстро и просто спарсить кучу информации на несколько десятков сайтов. Этот кейс должен обогатить каждого!
Всего доброго! Ретвиты, лайки и репосты приветствуются!
Если статья была для Вас полезной - Поделитесь ссылкой!
Что то не работает парсер((( Не получается его запустить
Parse error: syntax error, unexpected T_PUBLIC in Z:\home\localhost\www\parser\index.php on line ____
Комментарии (10)