محمد افاضاتی

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

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

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

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

 

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 ها رو سلکت کن همه رو بهت میده

ارسال نظر

کاربران بیان میتوانند بدون نیاز به تأیید، نظرات خود را ارسال کنند (ورود به سایت)