معرفی و آموزش پیاده‌سازی Apache Lucene

سلام،

Apache Lucene

حدود یک سال و نیم قبل در مطلبی به معرفی خزنده‌های وب (Web crawlers) و آموزش راه اندازی یک خزنده وب ساده به زبان جاوا پرداختم. در طول این مدت افراد بسیاری در مورد خزنده‌های وب با من تماس گرفتند و سوالات مختلفی در مورد این خزنده‌ها داشتند. در این مطلب به معرفی و آموزش Apache Lucene که به منظور جستجو فایل‌های متین استفاده می شود، می پردازم. همچنین در آینده به بررسی ۳ محصول Hadoop, Apache Solr و Apache Nutch خواهم پرداخت.

Apache Lucene

آپاچی لوسن کتابخانه ای برای راه اندازی موتورهای جستجوی متن می باشد. این کتابخانه آزاد بوده و تحت لایسنس Apache Licene 2.0 منتشر می شود. این کتابخانه به زبان جاوا (Java) نوشته شده و سپس به زبان های Delphi, Perl, C#, C++, Python, Ruby, و PHP پیاده سازی (پورت) شده است.
این کتابخانه این امکان را برای شما فراهم می آورد تا هر نوع موتور جستجوی متنی مانند موتور جستوی وب، لوکال (محلی) ویا فقط ویژه یک وب سایت را ایجاد نمایید. در نظر داشته باشید که Apache Lucene فقط برای جستجو استفاده می‌شود و برای جمع‌آوری اطلاعات و تحلیل آن‌ها نیاز به ابزارهای دیگری مانند Solr و Nutch دارید.
برای دریافت این کتابخانه به صفحه دانلود آپاچی لوسن مراجعه نمایید. همچنین می توانید نسخه فعلی (۳.۶.۰) را از آدرس زیر دریافت نمایید:
دانلود کتابخانه آپاچی لوسن نسخه ۳.۶.۰

استفاده از کتابخانه Apache Lucene

در ادامه مراحل مختلف برای آماده‌سازی و انجام جستجو را بررسی کرده و در انتها سورس کامل این کلاس را مشاهده خواهید کرد.

مرحله اول: ایندکس کردن اطلاعات

قبل از هر چیز شما باید اطلاعاتی را که قصد جستجو در آن‌ها را دارید ایندکس نمایید. ایندکس کردن اطلاعات فواید زیادی دارد که یکی از مهم‌ترین آن‌ها مرتب‌سازی و افزایش سرعت جستجو است.
ایندکس اطلاعات در لوسن توسط دو کلاس Document و Field صورت می پذیرد. Document سند شما و Field اطلاعات مرتبط با سند مانند عنوان، محتوا و… است. این وظیفه شماست که اطلاعات خود از جمله رشته‌ها، انواع فایل‌ها، اطلاعات ذخیره شده در پایگاه داده و… را به کلاس Document تبدیل و ایندکس نمایید. در زیر تعامل Index, Document و Field را مشاهده می کنید:

 Index 
Document 1

Field A (name/value)

Field B (name/value)
Document 2

Field A (name/value)

Field B (name/value)

بعد از آماده سازی Document باید توسط کلاس IndexWriter، ایندکس را ذخیره می کنیم. پارامتر اول در فراخوانی IndexWriter دایکتوری ذخیره‌سازی ایندکس را مشاهده می‌کند.
با کدهای زیر چند عبارت را در حافظه دسترسی تصادفی (RAM) ذخیره کردیم:

Directory index = new RAMDirectory();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, analyzer);

IndexWriter w = new IndexWriter(index, config);
addDoc(w, "Lucene in Action");
addDoc(w, "Lucene for Dummies");
addDoc(w, "Managing Gigabytes");
addDoc(w, "The Art of Computer Science");
w.close();

متد addDoc رشته‌ها را به ایندکس اضافه می کند:

private static void addDoc(IndexWriter w, String value) throws IOException {
    Document doc = new Document();
    doc.add(new Field("title", value, Field.Store.YES, Field.Index.ANALYZED));
    w.addDocument(doc);
  }
}

مرحله دوم: ایجاد کوئری (Query)

در این برنامه ما عبارت مورد نظر برای جستجو را از ورودی (stdin) دریافت می‌کنیم:

String querystr = args.length > 0 ? args[0] : "lucene";
Query q = new QueryParser(Version.LUCENE_36, "title", analyzer).parse(querystr);

مرحله سوم: انجام جستجو

در این مرحله کوئری (جستار؟) ساخته شده را بر روی ایندکس جستجو می کنیم. همچنین از کلاس TopScoreDocCollector برای بدست آوردن ۱۰ نتیجه مرتبط‌تر استفاده کرده ایم:

int hitsPerPage = 10;
IndexReader reader = IndexReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

مرحله چهارم: مشاهده نتایج

در انتها نتایج بدست آمده را نمایش می‌دهیم:

System.out.println("Found " + hits.length + " hits.");
for(int i=0;i<hits.length;++i) {
    int docId = hits[i].doc;
    Document d = searcher.doc(docId);
    System.out.println((i + 1) + ". " + d.get("title"));
}

همین!

کلاس HelloLucene

در زیر کلاس HelloLucene را یکجا مشاهده می فرمایید:

import java.io.IOException;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

public class HelloLucene {
    public static void main(String[] args) throws IOException, ParseException {
        // 0. Specify the analyzer for tokenizing text.
        //    The same analyzer should be used for indexing and searching
        StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);

        // 1. create the index
        Directory index = new RAMDirectory();

        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, analyzer);

        IndexWriter w = new IndexWriter(index, config);
        addDoc(w, "Lucene in Action");
        addDoc(w, "Lucene for Dummies");
        addDoc(w, "Managing Gigabytes");
        addDoc(w, "The Art of Computer Science");
        w.close();

        // 2. query
        String queryStr = args.length > 0 ? args[0] : "lucene";

        // the "title" arg specifies the default field to use
        // when no field is explicitly specified in the query.
        Query q = new QueryParser(Version.LUCENE_35, "title", analyzer).parse(queryStr);

        // 3. search
        int hitsPerPage = 10;
        IndexReader reader = IndexReader.open(index);
        IndexSearcher searcher = new IndexSearcher(reader);
        TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
        searcher.search(q, collector);
        ScoreDoc[] hits = collector.topDocs().scoreDocs;

        // 4. display results
        System.out.println("Found " + hits.length + " hits.");
        for (int i = 0; i < hits.length; ++i) {
            int docId = hits[i].doc;
            Document d = searcher.doc(docId);
            System.out.println((i + 1) + ". " + d.get("title"));
        }

        // searcher can only be closed when there
        // is no need to access the documents any more.
        searcher.close();
    }

    private static void addDoc(IndexWriter w, String value) throws IOException {
        Document doc = new Document();
        doc.add(new Field("title", value, Field.Store.YES, Field.Index.ANALYZED));
        w.addDocument(doc);
    }
}

منابع

شاد و پیروز باشید :)

خزنده وب (Web Crawler)

سلام :)

مطلب امروز ضمن آشنا کردن شما با Web crawler و معماری آن، سورس خزنده وب ساده ای به زبان جاوا را ارائه می دهد.

خزنده وب (Web Crawler) چیست؟

خزنده وب که بیشتر ما آن را با عنوان Web crawler می شناسیم به برنامه کامپیوتری اطلاق می شود که World Wide Web (وب جهان گستر) را به صورت مرتب و سلسله مراتبی بازدید کرده و اطلاعات آن را مورد پردازش قرار می دهد. از Web crawler ها با عناوین دیگری مانند ants, automatic indexers, bots, Web spiders و Web robots نیز یاد می شود.

کاربرد ها

۱- موتورهای جستجو

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

۲- مدیریت فنی وب سایت

مدیریت فنی وب سایت بخشی از کار این خزنده هاست که شامل یافتن لینک های شکسته (Broken Link) ، اعتبار سنجی (Validation) کدهای HTML، فایل های CSS و … می باشد.

۳- جمع آوری اطلاعات خاص

کاربرد دیگر خزنده های  وب جمع آوری اطلاعات خاصی مانند آدرس های ایمیل است. معمولا هدف از اینکار ارسال هرزنامه (spam) می باشد. برای جلوگیری از ثبت آدرس ایمیل توسط این خزنده ها، می توانید آدرس ایمیل خود را به صورت saeidREMOVEME AT جیمیل و یا موارد مشابه دیگر بنویسید.

نحوه کار

به صورت عمومی نحوه کار Web crawler ها به این صورت است که ابتدا لیستی از URL ها (آدرس های وب) که به عنوان seed شناخته می شوند را برای بازدید پردازش می کنند. هنگام پردازش این آدرس ها، لیست لینک ها و آدرس های موجود در صفحات آن ها را گردآوری کرده و به لیست ابتدایی اضافه می کنند. بقیه اطلاعات را نیز با توجه به نیاز و هدف خود ذخیره  و پردازش می نمایند.

معماری خزنده وب

خزنده وب بخش اصلی و مرکزی هر موتور جستجویی را تشکیل می دهد. به همین جهت الگوریتم و معماری آن ها به شدت مخفی نگه داشته می شود. با این وجود معماری سطح بالای (High-level architecture) آن به شکل زیر می باشد:

معماری سطح بالای یک خزنده وب استاندارد (عکس از ویکی پدیا)

عادی کردن آدرس (URL normalization)

منظور از عادی کردن آدرس، یکی کردن آدرس هایی می باشد که دارای خروجی یکسانی هستند. هدف از این کار جلوگیری از جمع آوری اطلاعات یکسان از چندین URL می باشد. URL normalization با نام URL canonicalization نیز شناخته می شود که همان فرآیند تغییر آدرس برای استاندارد شدن می باشد.

مراحل فرایند عادی کردن آدرس

• تبدیل آدرس به حروف کوچک

HTTP://www.Example.com/ → http://www.example.com/

• افزودن / به آدرس در صورت نیاز

http://www.example.com → http://www.example.com/

• حذف آدرس ایندکس دایرکتوری

http://www.example.com/default.asp → http://www.example.com/
http://www.example.com/a/index.html → http://www.example.com/a/

• بزرگ کردن حروف encode شده یا همان حروف بعد از علامت ٪

http://www.example.com/a%c2%b1b → http://www.example.com/a%C2%B1b

• حذف بخش زاید

http://www.example.com/bar.html#section1 → http://www.example.com/bar.html

• حذف و تبدیل آی پی به دامنه

http://208.77.188.166/ → http://www.example.com/

• اعمال محدودیت بر روی پروتکل ها مانند تبدیل https به http

https://www.example.com/ → http://www.example.com/

• حذف پورت پیش فرض (پورت ۸۰ به صورت پیش فرض برای http می باشد)

http://www.example.com:80/bar.html → http://www.example.com/bar.html

• حذف / های تکراری

http://www.example.com:80/bar.html → http://www.example.com/bar.html

• حذف . ها (dot-segments)

http://www.example.com/../a/b/../c/./d.html → http://www.example.com/a/c/d.html

• حذف www از اول دامنه

http://www.example.com/ → http://example.com/

• مرتب کردن متغییرهای صفحه فعال

http://www.example.com/display?lang=en&article=fred
 → http://www.example.com/display?article=fred〈=en

• حذف متغییرهای اختیاری از query-string

http://www.example.com/display?id=123&fakefoo=fakebar
 → http://www.example.com/display?id=123

• حذف متغییرهای پیش فرض از query-string

http://www.example.com/display?id=&sort=ascending
 → http://www.example.com/display

• حذف علامت ? هنگامی که query-string خالی باشد

http://www.example.com/display? → http://www.example.com/display

• استانداردکردن encoding کاراکترها

http://www.example.com/display?category=foo/bar+baz
 → http://www.example.com/display?category=foo%2Fbar%20baz

شناسایی خزنده وب

خزنده های وب معمولا با استفاده از فیلد User-agent داده HTTP request خود را معرفی می کنند. شما با استفاده از لاگ وب سرور خود می توانید لیست این Web crawler های را مشاهده کنید. فیلد User agent ممکن است شامل URL ای باشد که به سایت سازنده خزنده اشاره می کند.

Spambot ها و سایر خزنده های مخرب معمولا فیلد User agent را به صورت غیر واقعی با اطلاعاتی مانند نام یک مرورگر پر می کنند.

فایل robots.txt

این فایل برای دادن اطلاعات اولیه در زمینه وب سایت مورد پردازش به خزنده های وب استفاده می گردد. به عنوان مثال با این فایل می توانید دسترسی خزنده های وب به بعضی زیر شاخه ها محدود کنید. دستورات زیر در فایل robots.txt از دسترسی خزنده ها به دایرکتوری /tmp/ جلوگیری می کند:

User-agent: *
Disallow: /tmp/

* فایل robots.txt یک استاندارد می باشد. به همین جهت خزنده وب (معمولا خزنده مخرب) می تواند آن را نادیده بگیرد.

معروف ترین خزنده های وب غیر آزاد

در زیر لیست معروف ترین خزنده های وب را مشاهده می کنید:

معروف ترین خزنده های متن باز

سورس خزنده وب به زبان جاوا

سایت sun در سال ۱۹۹۸ مقاله ای آموزشی با عنوان “Writing a Web Crawler in the Java Programming Language” را ارائه داد. در آن مقاله ضمن توضیح دادن Web crawler سورس برنامه آن نیز در اختیار عموم قرار گرفت. البته این برنامه خیلی ساده و در مرحله ابتدایی می باشد. شما باید با توجه به نیازهای خود آن را تغییر داده و مورد استفاده قرار دهید.

• دانلود سورس خزنده وب به زبان جاوا (لینک کمکی)

کاربرد خزنده وب برای من

برای سایت هایی که از نرم افزار های آماده مثل وردپرس استفاده نشده است و نیاز به موتور جستجو احساس می شد، با استفاده از خزنده ای که نوشتم، اطلاعات کل وب سایت مورد نظر را در داخل یک جدول در پایگاه داده ذخیره کردم. سپس توابعی برای جستجو بهینه بر روی داده ها در MySQL نوشتم و با استفاده از آن ها بخش جستجوی وب سایت مورد نظر را راه اندازی کردم. نمونه این جستجو ها در سایت های زیر فعال می باشد:

• داتک تله کام

• ارتباطات فرادانش سامان

منابع

موفق باشید ;)

جستجوی فایل در لینوکس

سلام :)

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

۱- دستور locate

دستور locate یکی از سریع ترین راه های یافتن فایل های مختلف در لینوکس است. ساده ترین نوع استفاده از آن را در زیر مشاهده می کنید:

$ locate filename

یا

$ locate "*.html"

یکی از ضعف های این دستور، عدم جستجو در فایل سیستم های مانت شده ویندوز است. به عبارت دیگر این دستور به صورت پیش فرض پارتیشن ویندوزی من را برای یافتن فایل جستجو نمی کند.
در صورت زیاد بودن نتایج جستجو، از پارامتر n دستور locate برای ایجاد محدودیت استفاده نمایید:

$ locate "*.php" -n 10

و نیز می توانید نتایج را با دستورات less یا more ترکیب کنید:

$ locate "*.php" | more

نکته: در هنگام استفاده از locate برای نخستین بار ممکن است با پیغام خطای مروبوط به عدم وجود پایگاه داده مواجه شوید. برای رفع این مشکل از دستور زیر برای بروز کردن پایگاه داده استفاده نمایید:

$ sudo updatedb

۲- دستور find

find یکی از انعطاف پذیرترین  و قوی ترین دستورات برای جستجوی فایل هاست. در زیر ساده ترین ساختار استفاده از دستور find آمده است:

$ find  / -name 'index.php'

دستور بالا تمامی فایل های index.php موجود در پارتیشن ریشه (/) و زیر شاخه های آن را نمایش می دهد.
درصورتی که به خطای اجازه دسترسی در جستجو برخورد کردید، با اضافه کردن 2>/dev/null آن را نادیده بگیرید. همچنین می توانید با اضافه کردن 2>errors.txt آن خطا ها را در فایل errors.txt ذخیره نمایید:

$ find  / -name 'index.php' 2>/dev/null
$ find  / -name 'index.php' 2>errors.txt

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

$ find /home/saeid -name 'index*'
$ find /home/saeid -iname 'index*'

دستور اول در بالا تمامی فایل هایی را که با عبارت index شروع شده و در دایرکتوری /home/saeid هستند را نمایش می دهد.
دستور دوم، عملکردی مشابه با دستور اول دارد، با این تفاوت در این دستور اندازه حروف (بزرگی و کوچکی) آنها در نظر گرفته نمی شود. یعنی فایل هایی مانند InDex.php یا iNDEX نیز در نتایج دیده می شوند.

$ find -name s*

دستور بالا تمامی فایل ها و دایکتوری هایی را که با حرف s شروع شده و در دایکتوری جاری می باشند را نمایش می دهد.

$ find /home/saeid/Music -name '*.mp3' -size -5000k
$ find / -size +10000k

دستور اول تمامی فایل های mp3 موجود در دایکتوری /home/saeid/Music که کمتر از ۵۰۰۰ کیلوبایت حجم دارند را نمایش می دهد.
دستور دوم تمامی فایل هایی را که بیش از ۱۰۰۰۰ کیلوبایت حجم دارند را نمایش می دهد.

$ find /home/saeid -amin -10 -name '*.php'
$ find /home/saeid -atime -2 -name '*.php'
$ find /home/saeid -mmin -10 -name '*.php'
$ find /home/saeid -mtime -2 -name '*.php'

دستور اول تمامی فایل های php موجود در پوشه خانگی کاربر saeid و زیر شاخه های آن را که در ۱۰ دقیقه اخیر مورد دسترسی قرار گرفته اند را نمایش می دهد.
دستور دوم عملکردی مشابه با دستور اول دارد، با این تفاوت که فایل های مورد دسترسی در ۲ ساعت اخیر را نمایش می دهد.
دستور سوم و چهار عملکردی مشابه با دستور اول و دوم دارند، با این تفاوت که فایل هایی را که در محتوای آنها تغییراتی صورت گرفته است را نمایش می دهند.

$ find / -mount -name 'win*'

دستور بالا تمامی فایل هایی را که با عبارت win شروع شده و در دایرکتوری / بوده و توسط فایل سیستمی مانت (mount) نشده باشند را نمایش می دهد. این دستور زمانی کاربردی تر است که پارتیشن های ویندوزی شما به صورت پیش فرض مانت می شوند و عبارت مورد جستجو (مانند عبارت win) نتایج بسیاری در این پارتیشن ها داشته باشد.

$ find /home/saeid/Music -name 'Metallica*' -and -size +10000k
$ find /home/saeid/Music -size +10000k ! -name "Metallica*"
$ find /home/saeid/Music -name 'Metallica*' -or -size +10000k

عملکرد های شرطی and و or و not دستور find را بسیار انعطاف پذیر می کنند. به عنوان مثال دستور دوم تمامی فایل هایی را که بیش از ۱۰۰۰۰ کیلو بایت حجم داشته و نام آن ها با عبارت Metallica شروع نشده باشد را نمایش می دهد.

گزینه exec یکی از مهمترین ویژگی های دستور find است. با استفاده از این گزینه شما می توانید دستوری را بر روی نتایج جستجو اعمال کنید. نمونه ساده استفاده از این دستور را در زیر مشاهده می کنید:

$ find / - name 'Metallica*' -exec ls -lh {} \;

دستور بالا یک لیست (ls -lh) با جزییات از نتایج جستجو را نمایش می دهد.

۳- دستور whereis

این دستور برای جستجو فایل های اجرایی، منبع و راهنمای یک دستور مورد استفاده قرار می گیرد:

$ wheris nano

۴- دستور which

دستور which برای بدست آوردن آدرس کامل دستور اجرایی مورد استفاده قرار می گیرد. به عنوان مثال خروجی دستور زیر عبارت /usr/bin/nano است.

$ which nano

موفق باشید