تØليل المواقع باستخدام LWP
ar
سنتعلم كيÙ� نقوم بتنزيل، تØليل Ùˆ استخراج معلومات Ù…Ù�يدة من المواقع الالكترونية.
سنتعلم كيÙ� نقوم بتنزيل، تØليل Ùˆ استخراج معلومات Ù…Ù�يدة من المواقع الالكترونية. انظر التعريÙ� لمزيد من التÙ�صيل
تØليل المواقع هو بشكل عام عبارة عن عملية تجميع معلومات مختلÙ�Ø© من مواقع مختلÙ�Ø© Ùˆ من ثم ترتيبها Ùˆ ØÙ�ظها لاستخدامها Ù�ÙŠ اغراض اخرى. مثلا المدونات Ùˆ المواقع الاخبارية تقوم باستخراج معلومات مختلÙ�Ø© من مواقع اخرى Ùˆ من ثم تعرضها على موقعها بشكل موØد. مثال عربي : موقع شخصي يقوم بجلب الاخبار من موقع الجزيرة Ùˆ العربية Ùˆ يعرضها على صÙ�Øته الخاصة.
قبل ان تقوم بعملية تØليل لاي موقع تأكد من انك لا تخترق اي قوانين Ùˆ ان الموقع Ù�عليا ÙŠØ³Ù…Ø Ø¨ØªÙ†Ø²ÙŠÙ„ معلوماته Ùˆ استخدامه لاغراض اخرى.
اذا كان الموقع يقدم طريقة تواصل موØدة للمبرمجين Ù�الاÙ�ضل استخدامها
اقرا ات�اقية الاستخدام
لا تقم باستخراج و تجميع الايميلات و معلومات الاتصال و ما اشبه
اØترم
robots.txt
اÙ�ØµØ Ø¹Ù† هويتك بشكل ÙˆØ§Ø¶Ø Ù�ÙŠ عمليات الاتصال.
تأكد من انك لا تقوم بعملية اغراق و استهلاك لموارد الموقع الاخر
اقرا المقالات و الكتب المؤل�ة �ي هذا الخصوص
لغرض Ø§Ù„Ø´Ø±Ø Ù„Ù†Ù�ترض اننا قمنا بعمل سيرÙ�ر شخصي على المنÙ�Ø° http://127.0.0.1 Ùˆ توجد صÙ�Øات مختلÙ�Ø© مثلا صÙ�ØØ© البداية Ùˆ صÙ�ØØ© خطأ عدم العثور على الصÙ�ØØ© المطلوبة404
لجلب اي صÙ�ØØ© ويب يمكن ان نستخدم مكتبة LWP::UserAgent لتقوم بعملية طلب Ùˆ اØضار الصÙ�ØØ© الالكترونية . ملاØظة : يمكن استخدام اي مكتبة اخرى مثلا: HTTP::Tiny, HTTP::Lite
�ي المثال ادناه سنقوم بتن�يذ طلب جلب باستخدام الامر GET
use LWP::UserAgent;
my $ua =
LWP::UserAgent->new(agent => 'MyWebScaper/1.0 <http://example.com>');
my $response = $ua->get('http://127.0.0.1/');
if ($response->is_success) {
say $response->decoded_content;
} else {
die $response->status_line;
}
بعد تنÙ�يذ هذا الكود Ù�سنتØصل على نسخة من الصÙ�ØØ© التي طلبناها اذا تمت العملية بنجاØ. او Ù�ÙŠ Øالة Ù�شل عملية الاتصال Ùˆ تØميل الصÙ�ØØ© سيرجع لنا الكود رسالة خطأ
لنقم بمØاولة تنزيل صÙ�ØØ© غير موجودة على السيرÙ�ر:
use LWP::UserAgent;
my $ua =
LWP::UserAgent->new(agent => 'MyWebScaper/1.0 <http://example.com>');
my $response = $ua->get('http://127.0.0.1/not_found');
if ($response->is_success) {
say $response->decoded_content;
} else {
die $response->status_line;
}
هنا الخطأ سيقع �ي جهة السير�ر و سيقوم السير�ر بارجاع رسالة الخطأ، و لكن ماذا لو �شلت عملية الاتصال بالسير�ر اساسا؟
use LWP::UserAgent;
my $ua =
LWP::UserAgent->new(agent => 'MyWebScaper/1.0 <http://example.com>');
my $response = $ua->get('http://unknown.server');
if ($response->is_success) {
say $response->decoded_content;
} else {
die $response->status_line;
}
Ù�ÙŠ هذه الØالة سنتØصل على الخطأ 500. Ùˆ لكن يبدو ان السيرÙ�ر يعمل Ùˆ لكن بشكل غير صØÙŠØ Ùˆ هذا خلاÙ� الواقع. لكي نتعرÙ� على مصدر الخطا هل هو خارجي او داخلي توÙ�ر مكتبة LWP رسالة تنبيهية خاصة، انظر الكود التالية: use LWP::UserAgent;
my $ua =
LWP::UserAgent->new(agent => 'MyWebScaper/1.0 <http://example.com>');
my $response = $ua->get('http://unknown.server');
my $client_warning = $response->headers->header('Client-Warning');
if ($client_warning && $client_warning eq 'Internal response') {
die 'Internal error: ' . $response->status_line;
} else {
die 'Server error: ' . $response->status_line;
}
الان يمكننا ان نتأكد من ان الخطأ جاء من طر�نا.
قم بتنزيل صÙ�ØØ© من السيرÙ�ر الاÙ�تراضي http://127.0.0.1
Ùˆ قم بطباعة Øجمها بالبايتس
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
...
say ...
__TEST__
like($stdout, qr/183/, 'Should print correct size');
الان تعلمنا كيÙ� نقوم بتØميل الصÙ�Øات Ùˆ جلبها Ùˆ سنقوم الان بعملية تØليل او استخراج لبعض المعلومات. Ù�ÙŠ الامثلة القادمة لن نقوم بالتعامل مع الاخطاء طلبا للاختصار Ùˆ لكن Ù�ÙŠ الاستخدام الØقيقي يجب علينا التعامل مع كل Øالة خطأ ممكنة.
الصÙ�ØØ© الاÙ�تراضية تبدو بهذا الشكل :
# no-run
<html>
<head>
<title>A sample webpage!</title>
</head>
<body>
<h1></h1>
</body>
</html>
طبعا هذه اكواد html Ùˆ للتعامل معها سنØتاج الى Ù…Øلل الى html بالاضاÙ�Ø© الى ما يعرÙ� بمنقي Xpath Ùˆ CSS Øسب الØاجة طبعا.
بداية سنقوم بمØاولة تØليل الصÙ�ØØ© باستخدام HTML::TreeBuilder::XPath اكس باث هو عبارة عن لغة XML استعلامية. اذا لم تكن لديك اي معلومات مسبقة عن الاكس باث يمكنك ان تطلع هذا الملخص السريع: # no-run descendant-or-self::* all elements
//h1
<h1> element
descendant-or-self::h1/span
<span> within <h1>
descendant-or-self::h1 | descendant-or-self::span
<h1> and span
descendant-or-self::h1/descendant::span
<span> with parent <h1>
descendant-or-self::h1/following-sibling::*[name() = 'span' and (position() = 1)]
<span> preceded by <div>
descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' class ')]
Elements of class "class"
descendant-or-self::div[contains(concat(' ', normalize-space(@class), ' '), ' class ')]
<div> of class "class"
descendant-or-self::*[@id = 'id']
Element with id "id"
descendant-or-self::div[@id = 'id']
<div> with id "id"
descendant-or-self::a[@attr]
<a> with attribute "attr"
Ù�ÙŠ هذا المثال سنقوم باستخراج عنوان الصÙ�ØØ© :
use HTML::TreeBuilder::XPath;
my $html = <<'EOF';
<html>
<head>
<title>A sample webpage!</title>
</head>
<body>
<h1>Perltuts.com rocks!</h1>
</body>
</html>
EOF
my $tree = HTML::TreeBuilder::XPath->new;
$tree->ignore_unknown(0);
$tree->parse($html);
$tree->eof;
my @nodes = $tree->findnodes('//title');
say $nodes[0]->as_text;
استخرج ثم اطبع المØتويات المدرجة تØت h1
use HTML::TreeBuilder::XPath;
my $html = <<'EOF';
<html>
<head>
<title>A sample webpage!</title>
</head>
<body>
<h1>Perltuts.com rocks!</h1>
</body>
</html>
EOF
my $tree = HTML::TreeBuilder::XPath->new;
$tree->ignore_unknown(0);
$tree->parse($html);
$tree->eof;
my @nodes = $tree->findnodes(...);
say $nodes[0]->as_text;
__TEST__
like($stdout, qr/Perltuts.com rocks!/, 'Should print correct h1 content');
البعض من المبرمجين يعتبر CSS اكثر سهولة من الاكس باث. اذا كنت لا تعر� CSS �اليك هذا الملخص السريع:
# no-run
*
all elements
h1
<h1> element
h1 span
<span> within <h1>
h1, span
<h1> and span
h1 > span
<span> with parent <h1>
div + span
<span> preceded by <div>
.class
Elements of class "class"
div.class
<div> of class "class"
#id
Element with id "id"
div#id
<div> with id "id"
a[attr]
<a> with attribute "attr"
باستخدام HTML::Selector::XPath يمكننا ان نجعل من المكتبة السابقة منقي CSS ...
use HTML::TreeBuilder::XPath;
use HTML::Selector::XPath;
my $html = <<'EOF';
<html>
<head>
<title>A sample webpage!</title>
</head>
<body>
<h1>Perltuts.com rocks!</h1>
</body>
</html>
EOF
my $tree = HTML::TreeBuilder::XPath->new;
$tree->ignore_unknown(0);
$tree->parse($html);
$tree->eof;
my $xpath = HTML::Selector::XPath::selector_to_xpath('h1');
my @nodes = $tree->findnodes($xpath);
say $nodes[0]->as_text;
Ù�ÙŠ هذا التمرين اكمل الكود Ù�ÙŠ الاسÙ�Ù„ ليقوم بعملية جلب ثم استخراج Ùˆ طباعة لمØتويات h1 Ùˆ ذلك باستخدام منقي CSS :
use LWP::UserAgent;
use HTML::TreeBuilder::XPath;
use HTML::Selector::XPath;
my $ua = LWP::UserAgent->new;
my $response = ...
my $html = ...
my $tree = ...
my $xpath = HTML::Selector::XPath::selector_to_xpath(...);
my @nodes = $tree->findnodes($xpath);
say $nodes[0]->as_text;
__TEST__
like($stdout, qr/Perltuts.com rocks!/, 'Should print correct h1 content');
ليس من الغريب ان تقوم بعض الصÙ�Øات باعادة تØويل الى صÙ�Øات اخرى. من Øسن الØظ ان LWP::UserAgent تقوم بدعم التØويل اÙ�تراضيا. يمكن التØكم بعدد التØولات التي ستتولاها المكتبة من خلال max_redirect
Ù�ÙŠ المثال ادناه صÙ�ØØ© ريداركت التي سنقوم بطلبها ستقوم بعملية اعادة تØويل الى صÙ�ØØ© الاندكس او ما يسمى بالصØÙ�Ø© الاÙ�تراضية:
use LWP::UserAgent;
my $ua =
LWP::UserAgent->new(agent => 'MyWebScaper/1.0 <http://example.com>');
my $response = $ua->get('http://127.0.0.1/redirect');
say $response->decoded_content;
نلاØظ اننا لو قمنا باسناد قيمة صÙ�ر الى max_redirect Ù�انه لن يتم تØويلنا الى الصÙ�ØØ© الاÙ�تراضية
use LWP::UserAgent;
my $ua = LWP::UserAgent->new(
agent => 'MyWebScaper/1.0 <http://example.com>',
max_redirect => 0
);
my $response = $ua->get('http://127.0.0.1/redirect');
say $response->decoded_content;
متابعة الوصلات Ùˆ الروابط مهمة معتادة Ù�ÙŠ تØليل صÙ�Øات الويب. مرة اخرى باستخدام منقي CSS يمكننا الØصول على الـ a تاج الخاص بالروابط. لنجرب: use HTML::TreeBuilder::XPath; use HTML::Selector::XPath;
my $html = <<'EOF';
<html>
<head>
<title>A sample webpage!</title>
</head>
<body>
<h1>Perltuts.com rocks!</h1>
<a href="http://perltuts.com">perltuts.com</a>
</body>
</html>
EOF
my $tree = HTML::TreeBuilder::XPath->new;
$tree->ignore_unknown(0);
$tree->parse($html);
$tree->eof;
my $xpath = HTML::Selector::XPath::selector_to_xpath('a');
my @nodes = $tree->findnodes($xpath);
my @attrs = $nodes[0]->getAttributes();
say $attrs[0]->getValue();
انظر هذه المكتبات لمزيد من الاليات المساعدة Ù�ÙŠ عملية تØليل المواقع
Viacheslav Tykhanovskyi, [email protected]
Ali Yassen, [email protected]
Hey! The above document had some coding errors, which are explained below:
- Around line 3:
-
Non-ASCII character seen before =encoding in 'تØليل'. Assuming CP1252