Pull to refresh

Comments 39

Сдаётся мне, что большая часть задач просто решается с использованием штатного DOMXPath и, вероятнее всего, заметно быстрее. Да, синтаксис не как у CSS селекторов, но не намного сложнее. Потому библиотека, созданная 10 лет назад, и не обрела себе новой жизни.

Согласен. Но не каждый будет париться с DOMХpath, когда есть сиюминутное решение. Хотя я полностью поддерживаю изучение встроенной библиотеки. У неё, в отличии от phpQuery, есть какое-то будущее.

phpQuery это просто обёртка для стандартной библиотеки DOM.

В том же Chrome есть возможность скопировать XPath через панель разработчика. Так что париться не придется

xpath очень простой язык. Если его лень учить, можно dev mode браузера узнать xpath у любого элемента и все.
Этот бесконечный
pq($value)
ужасно бесит, особенно, если нужно парсить вложенные элементы. DiDom гораздо удобнее для этого
господи, неужели кто-то еще пользуется этой библиотекой? не, я понимаю лет эдак 10 назад еще ладно, но мы ведь вроде в 2019ом, php развивается, сообщества перестают выкладывать говнокод и начали задумываться над качеством того, что они делают.

есть symfony/dom-crawler, который враппер для стандартной DOM библиотеки. И с помощью symfony/css-selector можно писать в без XPath, оно само конвертирует.
Только вдобавок в отличии от phpQuery оно еще и написано не на статических свойствах, которые хранят хрен пойми, включая прошлые документы и не течет как соломенная крыша.

// Получаем код
$html = file_get_contents("https://какой-то_сайт.com/");

// Получаем объект dom
$dom = phpQuery::newDocument($html);

// Ищем в объекте dom элемент с классом .product-essential, обращаясь к методу find(). Он вмещает в себя все данные о продукте.
foreach($dom->find(".product-essential") as $key => $value){

        // Преобразуем dom объект в объект phpQuery. Делаем сие действие с помощью метода pq(); который является аналогом ($) в jQuery.
    $pq = pq($value);

    // Находим в этом элементе элемент с классом .brand-link и получаем значение атрибута "href" с помощью метода attr();
    $productHref[$key]["brand-href"] = $pq->find(".brand-link")->attr("href");

    // Получаем название бренда. Оно находится в строке <span class="brand-name">Какой-то бренд</span>.
    // Мы можем получить текст, содержащийся в <span> и других тегах с помощью метода text();
    $productHref[$key]["brand-name"] = $pq->find(".brand-name")->text();

    // Далее нам необходимо получить название товара.
    // Помимо указания класса элемента, мы можем указать имя вложенного элемента.
    // В данном случае имя бренда находится в элементе <h1>, который находится в элементе <div class="brand-name">
    $productHref[$key]["product-name"] = $pq->find(".product-name h1")->text();

    // PhpQuery позволяет перечислять классы нескольких, вложенных друг в друга, элементов.
    // Только не забывайте следить за порядком!
    // Тут мы получаем цену товара.
    $productHref[$key]["product-price"] = $pq->find(".price-info .price-box .regular-price .price")->text();

    // Получаем описание товара
    $productHref[$key]["product-description"] = $pq->find(".description .product-description")->text();

    // Так же есть возоможность шагать по элементам.
    // Деется это с помощью метода next();
    // В данном случае мы получим только числовой идентификатор без лишних строк.
    $productHref[$key]["product-id"] = $pq->find(".description .sku span")->next()->text();
    
}


а можно было вот так:
$html = file_get_contents("https://какой-то_сайт.com/");

$crawler = (new Crawler($html));
$productHref = $crawler->filter('.product-essential')->each($node) {
    return [
        'brand-href'          => $node->filter('.brand-link')->first()->attr('href'),
        'brand-name'          => $node->filter('.brand-name')->first()->text(),
        'product-name'        => $node->filter('.product-name h1')->first()->text(),
        'product-price'       => $node->filter('.price-info .price-box .regular-price .price')->text(),
        'product-description' => $node->filter('.description .product-description')->text(),
        'product-id'          => $node->filter('.description .sku span')->eq(1)->text(),
    ];    
});


Сильно сложно?

Начнем с того, что пользоваться Symfony — уже огромная проблема. Сомневаюсь, что кто-то будет собирать маленький проект на Symfony. Да, согласен, есть новые библиотеки. Одну из них я в статье указал. Но, как мне кажется, phpQuery одна из самых легких для понимания.

Не обязательно использовать Symfony целиком, можно надёргать нужных библиотек и подключить к своему проекту через композер.

Может быть я легко отношусь к маленьким проектам, но разворачивать композер, подтягивать через него библиотеки. Немного наворочено. Хотя идея, соглашусь, здравая.

Не совсем понимаю в чём проблема. Композер ставится глобально, библиотеки подключаются одной командой в консоли, которая копипастится из ридми репозитория, чаще всего. Дальше подключаете сформированный автолоадер в свой проект и пользуетесь. Не надо лазить по разным репозиториям, качать архивы, распаковывать, подключать. Банальная экономия времени.

Замечательно. Давайте упакуем все это в докер, и, для полноты картины, будем следить за актуальностью БД с помощью доктрины. Тогда проект будет максимально удобным и максимально усложненным.
Повторюсь, я за использование актуальных библиотек. Но если уж кто-то услышал про phpQuery (а слышат о нем сейчас только новички, либо вспоминают старички), то почему бы не выдать нормальное объяснение с примером?
А потом эти люди спустятся в комментарии, увидят ваше объяснение про Crawler, заинтересуются им. Будут разворачивать замечательные проекты. Сплошные плюсы же.

вот вы любите все усложнять:)
тут к чему ведут, так это что установка пакета композером нисколько не тяжелей скачивания библиотеки руками.
три строки а консоли (хоть в linux, хоть в каком-нибудь OpenServer под windows) и можно начинать накидывать код. даже если представить что у кого-то еще остался шаред хост, без консоли и вот этого вот всего, то вам же все равно закидывать туда проект по какому-нибудь ftp, так какая разница это папка vendor или phpQuery? и какая разница, будете ли вы писать require 'vendor/autoload.php' в своём скрипте или require 'phpQuery/phpQuery.php'?

Но если уж кто-то услышал про phpQuery (а слышат о нем сейчас только новички, либо вспоминают старички), то почему бы не выдать нормальное объяснение с примером?

Потому что не надо уже. Есть более актуальные и изящные решения для ровно тех же задач.


Зачем новичкам открывать мир старого? Куда лучше учить их сразу хорошим практикам

mkdir myproject
cd myproject
wget https://getcomposer.org/composer.phar
php composer.phar require symfony/dom-crawler
php composer.phar require symfony/css-selector


если использовать phpquery его же тоже надо скачать, разархивировать, подключить.
Композер подключить и пакет вытянуть (либу) это две строчки в командной строке. Во время работы скрипта Композер ни чего лишнего не грузит, кроме автозагрузчика.
То есть что я хочу сказать что Композер это не утяжеляет проект ни разу.

Я же не против. Ни разу, правда, так не делал.

myproject$ find . | wc -l
120

Всего лишь 120 файлов ради dom-crawler.
если уж файлов, то
$ find . -type f | wc -l

и 98
Да, точно. Хотя, папки тоже не стоит упускать из внимания. В любой из этих папок можно поселить бэкдор. Может быть, несущественно, но каждый файл и папка тратят inode.
Для крупного проекта dom-crawler выглядит очень даже привлекательно. Но для какого-нибудь однофайлового инструмента я бы не рискнул использовать.
Честно говоря, очень надеялся что пара строк
php composer.phar require symfony/dom-crawler
php composer.phar require symfony/css-selector

притянет 3-5 файлов, но получилось сильно больше, чем я ожидал.
Ну там же композер тащит весь пакет, LICENSE-файлы, ридми, иногда даже документацию. сам по себе пакет symfony/dom-crawler — 15 php файлов, еще 5-10 внутренние нужды композера. symfony/css-selector и вправду раздут, но никто не запрещает его не использовать и писать XPath :)

В любой из этих папок можно поселить бэкдор.

Приличные люди папкой вендора в интернет не светят.

Может быть, несущественно, но каждый файл и папка тратят inode.

у меня на сервере лежит более 3 миллионов картинок и пока инод хватает.
довольно плохо представляю себе какое количество проектов нужно держать, чтоб они закончились, но на такой случай есть phar и готовые инструменты, которые сами соберут composer проект в исполняемый файл. например, github.com/clue/phar-composer
symfony/dom-crawler и symfony/css-selector — это два самобытных компонента, для жизни которых не нужна симфони. и вообще ничего не нужно, кроме стандартной DOM-библиотеки, на которой же и ваш phpQuery и основан.

phpQuery одна из самых легких для понимания.

очень субъективно.
на насколько, по-вашему, мой код вышел сложнее того, что вы привели в статье?
а еще она одна из самых тяжелых для исполнения. она тормозит и течет из всех щелей. попробуйте обойти несколько тысяч страниц.

Обходил ~ 3 тысячи страниц за ~ 4000-4500 секунд. Одна страница загружалась чуть больше, чем за секунду.
Не защищаю phpQuery. О её моральной старости написал в статье. Но она по-прежнему жива.

Symfony это в первую очередь набор компонентов, а уж потом фреймворк. Вы всегда можете установить symfony/dom-crawler не устанавливая все остальное

Мы и так отмыться от стереотипов никак не можем :( Так еще и динозавров выкапывают
интересное определение функции ) надеюсь это описка
да, в спешке набрасывал)
Я вообще регулярками все делаю, правда я не гуру в РНР.
Но, какая разница, если работает?
неужели городить регулярку проще, чем написать что-то в духе
(new Crawler($html))
    ->filter('.classname')
    ->first()
    ->attr('id')

Не проще, конечно, но как-то привычней. Возможно, просто дело в том, что большинство парсеров я писал на Perl… Как то пробовал разные РНР библиотеки, так ни на чем и не остановился. Тем более, современный РНР это уже совсем не то, что лет 10 назад. Композер и прочее, ради 2 строчек кода. Впрочем, это уже оффтоп, извините.
не ну само собой, что если нужно просто выдрать один атрибут или контент из тега, то регулярка сойдет.
но посмотрите на пример из поста — там же регексп будет длинней, чем оставшийся код на php:)
плюс DOM-парсеры более универсальное решение все же.
Тот случай когда комментарий к статье полезнее самой статьи.
Так как «пишу» и «писал» ранее много парсеров, то в своей работе использовал и simplehtmdom, затем по каким-то причинам перешёл на phpQuery (перешёл наверное из за того что сначала просто попробовал, а потом заметил кратное увеличение скорости работы парсреа) и она мне понравилась больше (она это библиотека). Совсем недавно попробовал для парсинга DomCrawler от Symfony и мне она по удобству показалась такой же как phpQuery. Даже сказал бы так что «phpQuery»==«DomCrawler» для разбора страниц.
PS: Про удобство DomCrawler конечно же имею ввиду вкупе с css-selector пакетом
Сравнение с другими парсерами (1.6.3)
Потребления памяти (в байтах)
Максимальное
Nokogiri — 763568
DiDom — 793096
Zend Dom — 954712
DomCrawler — 1534512
Simple HTML DOM — 16839400
В конце теста
Nokogiri — 157168
DiDom — 158896
Zend Dom — 329232
DomCrawler — 567440
Simple HTML DOM — 14113456
Затраченное время (в секундах)
DiDom — 27.0787
Nokogiri — 27.1009
DomCrawler — 36.0982
Zend Dom — 48.3222
Simple HTML DOM — 188.0247

1 в поисковиках вылазит в большом количестве Simple. Но он протекает и медленный.
И все было бы чудесно, если бы не
Function create_function() is deprecated in /phpquery/phpQuery/phpQuery/phpQueryObject.php


В общем нет, спасибо, не надо.
Sign up to leave a comment.

Articles