Hootan Alghaspour
Hootan Alghaspour
خواندن ۳ دقیقه·۳ سال پیش

DomCrawling با PHP

من روش خیلی سریع و ساده را اینجا خدمتتون عرض می کنم و یک مثال می زنم.

از روش های پردازش متنی هم میتوان استفاده کرد اما Document Object Model (DOM) در حقیقت یک روش و api برای کار کردن با تگ دارها مثل HTML و XML است که بتوان در بین نودها و آیتم های آن ها گشت زد و عملیات انجام داد.

The Document Object Model (DOM) is a programming API for HTML and XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated. In the DOM specification, the term "document" is used in the broad sense - increasingly, XML is being used as a way of representing many different kinds of information that may be stored in diverse systems, and much of this would traditionally be seen as data rather than as documents. Nevertheless, XML presents this data as documents, and the DOM may be used to manage this data.

With the Document Object Model, programmers can create and build documents, navigate their structure, and add, modify, or delete elements and content. Anything found in an HTML or XML document can be accessed, changed, deleted, or added using the Document Object Model, with a few exceptions - in particular, the DOM interfaces for the internal subset and external subset have not yet been specified.


ما میخواهیم محتوی صفحات وب را بخوانیم و ویرایش کنیم و استفاده کنیم.مثلاً می خواهیم صفحه سمت چپ تصویر را به آرایه PHP سمت راست تبدیل کنیم.

یا می خواهیم یک سرویس تجیمع محتوی یا اخبار یا قیمت بنویسیم که خودش از چند سایت مطالب را خوانده و بخش های مدنظر ما را سوا و پالایش کند و اطلاعات مدنظر ما را ذخیره نماید.

فرض در این مقاله این است که شما برنامه نویس PHP هستید، به همین دلیل دیگر مبانی اولیه را شرح نمی دهم. محیط استفاده من در این مقاله PHP هفت و خورده ای است و composer هم روی سرور نصب شده.

مرحله 1 : ساخت پروژه و ادخال کلاس ها و کتابخانه های مورد نیاز

دلیلی که همینطوری از DOM و برنامه نویسی functional استفاده نمی کنم این است که از این کارهای crawl زیاد دارم و تمایل دارم همه را بصورت OOP داشته باشم که در استفاده ها و توسعه های بعدی راحت باشم و همچنین در symfony و لاراول راحت می توان از این کامپوننت ها استفاده کرد.

پروژه را composer init کنید که از 2 کامپوننت DomCrawler و CssSelector در Symfony استفاده کنیم.

یعنی دستور composer init را در پوشه مدنظر بزنید و اطلاعات درخواستی را وارد کنید یا نکنید که برامون ساختار پوشه بندی و composer.json و autoload.php و ... را بسازد که بعد دیگه برای استفاده از بسته هایی که با composer نصب می شوند راحت باشیم.

الان یک چنین ساختار و پوشه بندی در اختیار شماست :

حالا طبق راهنمای DomCrawler و CssSelector برای نصب دستورات ذیل را در همان پوشه ای که composer.json ما آنجاست وارد می کنیم :

#composer require symfony/dom-crawler #composer require symfony/css-selector #composer dump-autoload -o

می گیره و نصب می کنه و تنظیمات composer.json و autoload.php را خودش انجام می دهد و ساختار پوشه های ما بشکل ذیل می شود.

در ادامه در همین پوشه اصلی یک فایل index.php با محتوی ذیل می سازم :

<?php require_once __DIR__.'/vendor/autoload.php'; use Hootan\Crw\Classes\Iranjib; $iranjibPrices=new Iranjib('https://www.iranjib.ir/showgroup/45/%D9%82%DB%8C%D9%85%D8%AA-%D8%AE%D9%88%D8%AF%D8%B1%D9%88-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-%D8%AF%D8%A7%D8%AE%D9%84/'); var_dump($iranjibPrices->getPrices());

مرحله 2 : گرفتن و پالایش محتوی HTML

در اینجا مثلاً صفحه قیمت خودرو داخلی ایران جیب را مثال می زنم. برای استخراج جدول قیمت ها از این صفحه و تبدیل آن به یک آرایه قابل استفاده در برنامه نویسی و ذخیره در دیتابیس و ... مقاله را ادامه می دهیم.

از ایرانجیب تر و تمیزتر و راحت تر برای گرفتن با DOM هست اما ایران جیب را مثال می زنم که در کد چند نکته را اشاره کرده باشم.

در پوشه src یک پوشه Classes می سازم و درون آن فایل Iranjib.php را با محتوی ذیل خواهم ساخت:

<?php namespace Hootan\Crw\Classes; use Symfony\Component\DomCrawler\Crawler; class Iranjib{ private $crawl_url,$dom,$crawler,$updatedate; function __construct($url){ $this->crawl_url=$url; $this->dom=file_get_contents($this->crawl_url); $this->crawler=new Crawler($this->dom); } function toEnNumber($input) { $replace_pairs = array( '۰' => '0', '۱' => '1', '۲' => '2', '۳' => '3', '۴' => '4', '۵' => '5', '۶' => '6', '۷' => '7', '۸' => '8', '۹' => '9', '٠' => '0', '١' => '1', '٢' => '2', '٣' => '3', '٤' => '4', '٥' => '5', '٦' => '6', '٧' => '7', '٨' => '8', '٩' => '9', ','=>'' ); return strtr( $input, $replace_pairs ); } function getPrices(){ if($this->crawler){ $this->priceRows=[]; $this->crawler->filter('.expand')->each(function (Crawler $crawler) { foreach ($crawler as $node) { $node->parentNode->removeChild($node); } }); $this->crawler->filter('.grp-tabl')->each(function (Crawler $crawler) { foreach ($crawler as $node) { $node->parentNode->removeChild($node); } }); $this->crawler->filter('.header')->each(function (Crawler $crawler) { foreach ($crawler as $node) { $node->parentNode->removeChild($node); } }); $this->updatedate=$this->crawler->filter('.group-refreshdate-wrapper')->text(); $brand=&quot&quot $i=0; foreach($this->crawler->filter('.items_table > tr') as $content) { $element = new Crawler($content); if($element->matches('.catsection')){ $this->brand=$element->text(); }else{ $this->priceRows[$i][&quotbrand&quot]=$this->brand; $this->priceRows[$i][&quotupdate&quot]=$this->updatedate; $this->priceRows[$i][&quotname&quot]=trim($element->filter('td')->text(),&quot\xC2\xA0 &quot); if($element->filter('span.lastprice')->eq(0)->count() > 0){ $this->priceRows[$i][&quotprice1&quot]=$this->toEnNumber($element->filter('span.lastprice')->eq(0)->text()); } if($element->filter('span.lastprice')->eq(1)->count() > 0){ $this->priceRows[$i][&quotprice2&quot]=$this->toEnNumber($element->filter('span.lastprice')->eq(1)->text()); } $i++; } } return $this->priceRows; }else{return false;} } }

اکنون ساختار پوشه بندی و فایل ها بصورت ذیل است، محتوی فایل های index.php و Iranjib.php را هم در بالا خدمتتان گذاشته ام :

اکنون با باز کردن آدرس لیست قیمت استخراج شده از صفحه مدنظر را خواهیم داشت، دقت بفرمایید در فایل index.php می توان سایر صفحات قیمت ایرانجیب را هم با تعریف یک متغییر جدید از نوع کلاس Iranjib و ارسال آدرس صفحه به آن خواند.

نکات :

  • دقت بفرمایید مثلاً ۱۹۲,۰۰۰,۰۰۰ عدد نیست، کاراکتر فارسی است، برای تبدیل اینها به عدد از این استفاده کردم. عدد کردم که بعداً بتونیم روش پردازش های عددی و sort و ... انجام بدهیم و خودمون فرمت کنیم.
  • باید از
$element->filter('span.lastprice')->eq(0)->count() > 0

استفاده بفرمایید، وگرنه اگر تنها یکی از نودها خالی باشد خطای

Got error 'PHP message: PHP Fatal error: Uncaught InvalidArgumentException: The current node list is empty.

خواهید گرفت، try/catch اینجا درست کار نمی کند، تنها روش چک کردن خالی نبودن نود و نگرفتن خطا همین است.

  • همان ابتدا در خطوط 28تا44 ردیف های تبلیغات و ... را فیلتر و حذف کرده ام.
  • به نحوه استفاده از trim در اینجا دقت بفرمایید. بدون آن انتهای نام خودرو 2 فاصله می افتد.

کل پروژه را بصورت فایل zip می توانید از اینجا بگیرید و مشکلی برای اجرا روی سرور دارای PHP هفت و خورده ای نباید داشته باشد که اگر نکته ای را هم جا انداخته بودم از روی کد مرور بفرمایید.

phpdomلاراولsymfonyوب کاوی
هوتن القاس پور
شاید از این پست‌ها خوشتان بیاید