محمد افاضاتی

دیده ها، شنیده ها و علاقه مندی های یک برنامه نویس
سه شنبه, ۱۷ آبان ۱۳۹۰، ۰۵:۰۳ ب.ظ

چگونه یک روبات جمع آوری اطلاعات از یک سایت بنویسیم؟

در این مقاله قرار است یک روبات بنویسیم که مطالب یک سایت را بخواند و در دیتابیس ذخیره کند ولی چگونه؟

من برای اینکار از چند تا کتابخانه استفاده خواهم کرد.

 

import urllib

site = urllib.urlopen('http://www.google.com')
site_data = site.read()
site.close()

 

در urllib به سادگی می توانید یک آدرس را دانلود کنید. ما برای scrap باید آدرس را بسازیم و هر دفعه دوباره دانلود کنیم.

لایبری بعدی که باید استفاده کنیم lxml است که با آن می توانیم دقیقا element مشخصی را با استفاده از Xpath یا selector از کل html خارج کنیم

 

from lxml import etree, html

content = html.fromstring(site_data)
content.find_class("title")

خوب من یک کد کوچیک می نویسم که کارش اینه که آدرس سایت را بگیرد و هنگام کار بگویید چه کلاسی را می خواهید و در نتایجش که یک دیکشنری است ذخیره کند.

from HTMLParser import HTMLParser
from lxml import etree, html
import urllib

class Stripper(HTMLParser):
   
    def __init__(self):
        self.reset()
        self.fed = []
       
    def handle_data(self, d):
        self.fed.append(d)
       
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html_code):
    s = Stripper()
    s.feed(html_code)
    return s.get_data()

   
class scrap(object):
    """
    scrap site with addres and you can access to final_scrap for complete data
    """
    final_scrap = dict()
   
    def __init__(self, url=None):
        """
        download site
        """
        if not url:
            site = open('persian.html', 'r')
            site.seek(0)
        else:
            site = urllib.urlopen(url)
           
        self.content = site.read()
        self.content = html.fromstring(self.content)
       
    def normalize(self, data):
        striped = etree.tostring(data, encoding='utf-8')
        striped = strip_tags(striped)
        striped = striped.replace('\t','').replace('\n','')
        return striped

    def find_and_insert(self, name, element_class, element_number=None):
        """
        read self.content and find element_class form html with find_class
        """
        scrap_element = self.content.find_class(element_class)
        self.final_scrap[name] = self.normalize(scrap_element[element_number or 0])

خوب حالا من چگونه از این کلاس می تونم استفاده کنم؟

برای این کار من فایل پایتونی scrap رو اجرا می کنم

$ ipython -i scrap.py
Welcome to rlcompleter2 0.96
for nice experiences hit <tab> multiple times
Python 2.6.7 (r267:88850, Aug  3 2011, 11:33:52)
Type "copyright", "credits" or "license" for more information.

IPython 0.10.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: a = scrap('http://127.0.0.1')

In [2]: a.find_and_insert("title","title")

In [3]: a.fin
a.final_scrap      a.find_and_insert 

In [3]: a.final_scrap
Out[3]: {'title': '\xd9\xbe\xd8\xb1\xd8\xb4\xdb\x8c\xd9\x86'}

In [4]: print a.final_scrap['title']
پرشین

همونطوری که می بینید تونست صفحه رو از localhost من بخونه و نتایج رو درست برگرداند. حالا شما که می خواهید یک مجموعه صفحه را بخوانید و اطلاعاتشان را بدست آورید در یک حلقه آدرس صفحات مختلف را بسازید و اطلاعات مربوطه را که یک دیکشنری است در پایگاه داده خود ذخیره کنید.

به طور مثال من سایتی دارم که آدرسی نظیر http://www.pesarak.ir/?p=320 دارد . همانطور که می بینید ۳۲۰ یک عدد است که با عوض کردنش می توانم به صفحات دیگر دسترسی پیدا کنم. خوب با بررسی بیشتر متوجه می شوم این سایت اخرین صفحه ای که دارد همان ۳۲۰ است پس یک حلقه درست می کنم که از ۱ تا ۳۲۰ برود و یک string که همان آدرس است درست می کنم و به برنامه scrap می دهم و برایم دریافت می کند و در دیتابیس ذخیره می کنم.

دقت کنید احتمال دارد که با گذاشتن اعداد مختلف به صفحه ای برسید که وجود ندارد برای همین بگویید اگر مثلا صفحه مقدار title داشت ذخیره کن وگرنه برو صفحه بعدی ( title فقط مثال است )

روبات شما الان به راحتی می تواند در مدت کوتاهی تمام اطلاعات یک سایت را بدست آورد.

 

موفق باشید

 



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

محمد افاضاتی

دیده ها، شنیده ها و علاقه مندی های یک برنامه نویس
محمد افاضاتی
محمد افاضاتی برنامه نویس python php دیتابیس های postgresql mongodb و ...

رونوشتی از علاقه مندی هایم برای ماندگار تر شدن!

تبلیغات

طبقه بندی موضوعی

Feedburner RSS

Others

توییتر

در این مقاله قرار است یک روبات بنویسیم که مطالب یک سایت را بخواند و در دیتابیس ذخیره کند ولی چگونه؟

من برای اینکار از چند تا کتابخانه استفاده خواهم کرد.

 

import urllib

site = urllib.urlopen('http://www.google.com')
site_data = site.read()
site.close()

 

در urllib به سادگی می توانید یک آدرس را دانلود کنید. ما برای scrap باید آدرس را بسازیم و هر دفعه دوباره دانلود کنیم.

لایبری بعدی که باید استفاده کنیم lxml است که با آن می توانیم دقیقا element مشخصی را با استفاده از Xpath یا selector از کل html خارج کنیم

 

from lxml import etree, html

content = html.fromstring(site_data)
content.find_class("title")

خوب من یک کد کوچیک می نویسم که کارش اینه که آدرس سایت را بگیرد و هنگام کار بگویید چه کلاسی را می خواهید و در نتایجش که یک دیکشنری است ذخیره کند.

from HTMLParser import HTMLParser
from lxml import etree, html
import urllib

class Stripper(HTMLParser):
   
    def __init__(self):
        self.reset()
        self.fed = []
       
    def handle_data(self, d):
        self.fed.append(d)
       
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html_code):
    s = Stripper()
    s.feed(html_code)
    return s.get_data()

   
class scrap(object):
    """
    scrap site with addres and you can access to final_scrap for complete data
    """
    final_scrap = dict()
   
    def __init__(self, url=None):
        """
        download site
        """
        if not url:
            site = open('persian.html', 'r')
            site.seek(0)
        else:
            site = urllib.urlopen(url)
           
        self.content = site.read()
        self.content = html.fromstring(self.content)
       
    def normalize(self, data):
        striped = etree.tostring(data, encoding='utf-8')
        striped = strip_tags(striped)
        striped = striped.replace('\t','').replace('\n','')
        return striped

    def find_and_insert(self, name, element_class, element_number=None):
        """
        read self.content and find element_class form html with find_class
        """
        scrap_element = self.content.find_class(element_class)
        self.final_scrap[name] = self.normalize(scrap_element[element_number or 0])

خوب حالا من چگونه از این کلاس می تونم استفاده کنم؟

برای این کار من فایل پایتونی scrap رو اجرا می کنم

$ ipython -i scrap.py
Welcome to rlcompleter2 0.96
for nice experiences hit <tab> multiple times
Python 2.6.7 (r267:88850, Aug  3 2011, 11:33:52)
Type "copyright", "credits" or "license" for more information.

IPython 0.10.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: a = scrap('http://127.0.0.1')

In [2]: a.find_and_insert("title","title")

In [3]: a.fin
a.final_scrap      a.find_and_insert 

In [3]: a.final_scrap
Out[3]: {'title': '\xd9\xbe\xd8\xb1\xd8\xb4\xdb\x8c\xd9\x86'}

In [4]: print a.final_scrap['title']
پرشین

همونطوری که می بینید تونست صفحه رو از localhost من بخونه و نتایج رو درست برگرداند. حالا شما که می خواهید یک مجموعه صفحه را بخوانید و اطلاعاتشان را بدست آورید در یک حلقه آدرس صفحات مختلف را بسازید و اطلاعات مربوطه را که یک دیکشنری است در پایگاه داده خود ذخیره کنید.

به طور مثال من سایتی دارم که آدرسی نظیر http://www.pesarak.ir/?p=320 دارد . همانطور که می بینید ۳۲۰ یک عدد است که با عوض کردنش می توانم به صفحات دیگر دسترسی پیدا کنم. خوب با بررسی بیشتر متوجه می شوم این سایت اخرین صفحه ای که دارد همان ۳۲۰ است پس یک حلقه درست می کنم که از ۱ تا ۳۲۰ برود و یک string که همان آدرس است درست می کنم و به برنامه scrap می دهم و برایم دریافت می کند و در دیتابیس ذخیره می کنم.

دقت کنید احتمال دارد که با گذاشتن اعداد مختلف به صفحه ای برسید که وجود ندارد برای همین بگویید اگر مثلا صفحه مقدار title داشت ذخیره کن وگرنه برو صفحه بعدی ( title فقط مثال است )

روبات شما الان به راحتی می تواند در مدت کوتاهی تمام اطلاعات یک سایت را بدست آورد.

 

موفق باشید

 

نظرات  (۹)

فکر میکنم کتابخانه های جانبی هم وجود داشته باشن که روند کار رو سریعتر ، بهینه تر و آماده کنن.
البته من پیتون کار نیستم ولی خود در زبان هایی که کار کردم همیشه جا داره که دنبال چیزای سریعتر گشت.

ذکر این نکته هم لازمه که برای کاوش در یک وب سایت و هوشمندتر کردن روبات ، خواه نا خواه احتیاج به regex  هست.
پاسخ:
یه سری ابزار وجود داره مثل این:
http://scrapy.org/
البته برای خودش قوانین خودشو داره و کار خودش و می طلبه ... که در نهایت core اصلی اونم یه همچین کاری کرده. ولی لزوما برای هر کاری این ابزارات بهتر نیست
برای regex هم اینو ببین
http://stackoverflow.com/questions/2755950/how-to-use-regular-expression-in-lxml-xpath
سلام
 منم مثل سینا پایتون کار نمی‌کنم. چون چند وقت پیش یه ربات با جاوا نوشتم برام جالب بود. فقط یه مسئله ریز بود که فکر کردم بد نیست اینجا بگم. اونم اینه که معمولا سایت‌ها فایلی به اسم robots.txt دارن که سیاستشون رو در خصوص کراولر‌ها ذکر می‌کنه و سینتاکس خیلی ساده‌ای هم داره. به نظرم یه متد کوچیک اینجا می‌تونست حداقل وجود اون فایل رو بررسی کنه و در صورت عدم وجود اون فایل کراولینگ رو انجام بده این باعث می‌شه این ربات قانونمند‌تر رفتار کنه (چیزی که تو کشور ما زیاد بهش اهمیت نمی‌دن)

یه مسئله دیگه هم اینکه، این ربات کوکی‌ هم قبول می‌کنه؟
پاسخ:
آره اینو ببین http://www.voidspace.org.uk/python/articles/cookielib.shtml
این بحث crawler نیست این بحث اینه که تو یه سری اطلاعات خاص رو می خواهی از یک سایت در بیاری . crawler ها معمولا full text می خونن و یه سری آدرس رو بررسی می کنه از لینک های داخلی می رسند. اینکه به طور مثال بگی من می خوام فلان ادرس بخونم بعد با robots.txt چک کنی مثل این می مونه که توی همون چیزی که قرار دربیاری شک کنی. کلا یه کم بحثش متفاوت تره
۲۷ آبان ۹۰ ، ۱۶:۵۱ میعاد بهرامی
سلام محمد جان مطلب خوبی بود حالا اگه بخوایم مثلا هر چی تگ A داره رو وسطش رو در بیاره خروجی بده چیکار باید بکنیم
پاسخ:
from lxml.cssselect import CSSSelector

کلا می تونی با این css سلکت کنی
۱۳ آذر ۹۰ ، ۱۴:۳۰ میعاد بهرامی
سلام محمد جان این css رو میکشه بیرون
میخوام هر لینکی که بین تگ A تو html هست رو در بیارم ؟

ممنون از جوابت
پاسخ:
خوب css همینه دیگه ... تو مثلا می خوای بگی همه a ها رو یه شکل کن چیکار می کنی؟ یه سلکتور می نویسی اینجا هم همین کار رو می کنی بهش می گی a ها رو سلکت کن همه رو بهت میده
سلام ، دوست عزیز این کار را با php چگونه میشه انجام داد !؟
پاسخ:
آره احتمالا می شود
اینو ببین
PHPCrawl webcrawler library for PHP -> http://phpcrawl.cuab.de/
سلام دوست عزیز چطوری میشه تو یه سایت لاگین کرد.
من request‌هارو در آوردم و فرستادم ولی بازم نشد تو برنامم هم agent گذاشتم گفتم فایرفاکس هست بازم نشد اگه نمونه کد یا راهنمایی خوبی میتونید بکنید ممنون میشم.
سلام دوست عزیز
خیلی کوتاه، من نیاز دارم تا خیلی فوری وقت سفارت آلمان بگیرم ولی متاسفانه هر هفته در هنگام باز شدن وقتها صدها وقت در مدت بسیار کوتاهی ( به گفته سایت سفارت آلمان در کمتر از 5 دقیقه) پر می شود. من با پایتون آشنایی دارم و قصد دارم تا برنامه با آن بنویسم تا بتواند تشخیص دهد چه زمان وقتها در سایت سفارت باز می شود تا بتوانم لااقل یکی از آنها را رزرو کنم، می خواستم روباتی با پایتون بنویسم که این کار را برای من انجام دهد. از کجا باید شروع کنم؟ ضمنا قبل از ورود به صفحه مورد نظر مانند شما کد امنیتی از کاربر خواسته می شود، چگونه باید روبات این کد را تشخیص داده و به صفحه بعد برود؟
ممنون از راهنمایی شما

مطلب بسیار مفیدی بود.. ممنون بابت سایت خوبتون و انتشار این مطلب
سلام من یه ربات ساختم که هرچی پست براش فوروارد میشه رو ذخیره کنه. می خواستم بدونم برای ذخیره شون تو یه دیتابیس باید چکار کنم؟
ممنون میشم پاسخ بدید.

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی