Как написать сканера?

У меня были мысли о попытке написать простой искатель, который мог бы сканировать и составлять список его результатов для наших сайтов и контента на НКО.

Есть ли у кого-нибудь мысли о том, как это сделать? Куда вы указываете поисковый робот для начала? Как он отсылает свои выводы и продолжает сканировать? Как он знает, что он находит, и т.д. И т.д.

Ответ 1

Конечно, вы будете изобретать колесо. Но вот основы:

  • Список невидимых URL-адресов - укажите это с одной или несколькими стартовыми страницами
  • Список посещенных URL-адресов - поэтому вы не ходите по кругу.
  • Набор правил для URL-адресов, которые вам не интересны - поэтому вы не индексируете весь Интернет

Поместите их в постоянное хранилище, чтобы вы могли остановить и запустить сканер без потери состояния.

Алгоритм:

while(list of unvisited URLs is not empty) {
    take URL from list
    fetch content
    record whatever it is you want to about the content
    if content is HTML {
        parse out URLs from links
        foreach URL {
           if it matches your rules
              and it not already in either the visited or unvisited list
              add it to the unvisited list
        }
    }
}

Ответ 2

Сложная часть искателя - это если вы хотите масштабировать его на огромное количество веб-сайтов/запросов. В этой ситуации вам придется иметь дело с некоторыми проблемами, такими как:

  • Невозможность хранить информацию в одной базе данных.

  • Не хватает ОЗУ для работы с огромным индексом

  • Многопоточная производительность и concurrency

  • Ловушки-искатели (бесконечный цикл, созданный путем изменения URL-адресов, календарей, идентификаторов сеансов...) и дублированного содержимого.

  • Сканирование с нескольких компьютеров

  • Отформатированные HTML-коды

  • Постоянные ошибки HTTP с серверов

  • Базы данных без сжатия, которые делают вашу потребность в пространстве примерно на 8 раз больше.

  • Повторить программы и приоритеты.

  • Использовать запросы со сжатием (Deflate/gzip) (полезно для любого искателя).

И некоторые важные вещи

  • Respect robots.txt

  • И задержка поискового робота по каждому запросу, чтобы не задушить веб-серверы.

Ответ 3

Многопоточный веб-сканер

Если вы хотите обходить большой размер сайта, вам следует написать многопоточный искатель. подключение, выборка и запись обходной информации в файлы/базу данных - это три этапа сканирования, но если вы используете один поток, чем ваш процессор, а использование сети будет залить.

Многопоточный веб-искатель нуждается в двух структурах данных - linksVisited (это должно быть реализовано как hashmap или trai) и linksToBeVisited (это очередь).

Веб-искатель использует BFS для перемещения по всемирной сети.

Алгоритм базового веб-искателя: -

  • Добавьте один или несколько URL-адресов семян в linksToBeVisited. Метод добавления URL-адреса в linksToBeVisited должен быть синхронизирован.
  • Поместите элемент из linksToBeVisited и добавьте его в linksVisited. Этот метод pop для pop url из linksToBeVisited должен быть синхронизирован.
  • Извлеките страницу из Интернета.
  • Разберите файл и добавьте ссылку до сих пор, не найденную на странице, в linksToBeVisited. URL-адрес можно фильтровать, если необходимо. Пользователь может предоставить набор правил для фильтрации того, какой URL-адрес должен быть отсканирован.
  • Необходимая информация, найденная на странице, сохраняется в базе данных или файле.
  • повторите шаги с 2 по 5 до тех пор, пока очередь не станет связью LinksBoVidited.

    Вот фрагмент кода о том, как синхронизировать потоки....

     public void add(String site) {
       synchronized (this) {
       if (!linksVisited.contains(site)) {
         linksToBeVisited.add(site);
         }
       }
     }
    
     public String next() {
        if (linksToBeVisited.size() == 0) {
        return null;
        }
           synchronized (this) {
            // Need to check again if size has changed
           if (linksToBeVisited.size() > 0) {
              String s = linksToBeVisited.get(0);
              linksToBeVisited.remove(0);
              linksVisited.add(s);
              return s;
           }
         return null;
         }
      }
    

Ответ 4

Если ваши сайты NPO являются относительно большими или сложными (с динамическими страницами, которые будут эффективно создавать "черную дыру", как календарь со ссылкой "на следующий день" ), вам лучше использовать реальный веб-искатель, например Heritrix.

Если на сайтах всего несколько страниц, вы можете уйти, просто используя curl или wget или свой собственный. Просто помните, если они начнут становиться большими или вы начнете делать ваш script более сложным, просто используя реального искателя или, по крайней мере, посмотрите на его источник, чтобы узнать, что они делают и почему.

Некоторые проблемы (есть больше):

  • Черные дыры (как описано)
  • Повторы (что, если вы получаете 500?)
  • Перенаправление
  • Контроль потока (иначе вы можете быть обузой на сайтах)
  • реализация robots.txt

Ответ 5

Сканеры просты в концепции.

Вы получаете корневую страницу через HTTP GET, анализируете ее, чтобы находить URL-адреса и помещать их в очередь, если они уже не были проанализированы (так что вам нужна глобальная запись страниц, которые вы уже проанализировали).

Вы можете использовать заголовок Content-type, чтобы узнать, каков тип содержимого, и ограничить искатель только анализом типов HTML.

Вы можете вычеркнуть теги HTML, чтобы получить простой текст, на который вы можете выполнить анализ текста (чтобы получить теги и т.д., мясо страницы). Вы даже можете сделать это на тегах alt/title для изображений, если у вас есть продвинутый.

И в фоновом режиме вы можете иметь пул потоков, которые будут питать URL из очереди и делать то же самое. Вы хотите ограничить количество потоков, конечно.

Ответ 6

В Википедии есть хорошая статья о веб-сканера, охватывающая многие из алгоритмов и соображений.

Однако я бы не стал писать собственный искатель. Это большая работа, и поскольку вам нужен только "простой искатель", я думаю, что все, что вам действительно нужно, это готовый сканер. Есть много бесплатных и open-source сканеров, которые, вероятно, сделают все, что вам нужно, с очень небольшим количеством работы с вашей стороны.

Ответ 7

Вы можете составить список слов и создать поток для каждого слова, найденного в google.
Затем каждый поток создаст новый поток для каждой ссылки, которую он найдет на странице.
Каждый поток должен писать то, что он находит в базе данных. Когда каждый поток заканчивает чтение страницы, он заканчивается.
И там у вас есть очень большая база данных ссылок в вашей базе данных.

Ответ 8

Я использую Open search server для внутреннего поиска моей компании, попробуйте следующее: http://open-search-server.com его также открыть soruce.

Ответ 9

Используйте wget, рекурсивный веб-сосать, который выгрузит все файлы на ваш жесткий диск, а затем напишет еще один script, чтобы просмотреть все загруженные файлы и проанализировать их.

Изменить: или, может быть, завиток, а не wget, но я не знаком с завитком, я не знаю, рекурсивные ли загрузки, например wget.

Ответ 10

Я сделал простой веб-искатель, используя реактивное расширение в .net.

https://github.com/Misterhex/WebCrawler

public class Crawler
    {
    class ReceivingCrawledUri : ObservableBase<Uri>
    {
        public int _numberOfLinksLeft = 0;

        private ReplaySubject<Uri> _subject = new ReplaySubject<Uri>();
        private Uri _rootUri;
        private IEnumerable<IUriFilter> _filters;

        public ReceivingCrawledUri(Uri uri)
            : this(uri, Enumerable.Empty<IUriFilter>().ToArray())
        { }

        public ReceivingCrawledUri(Uri uri, params IUriFilter[] filters)
        {
            _filters = filters;

            CrawlAsync(uri).Start();
        }

        protected override IDisposable SubscribeCore(IObserver<Uri> observer)
        {
            return _subject.Subscribe(observer);
        }

        private async Task CrawlAsync(Uri uri)
        {
            using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromMinutes(1) })
            {
                IEnumerable<Uri> result = new List<Uri>();

                try
                {
                    string html = await client.GetStringAsync(uri);
                    result = CQ.Create(html)["a"].Select(i => i.Attributes["href"]).SafeSelect(i => new Uri(i));
                    result = Filter(result, _filters.ToArray());

                    result.ToList().ForEach(async i =>
                    {
                        Interlocked.Increment(ref _numberOfLinksLeft);
                        _subject.OnNext(i);
                        await CrawlAsync(i);
                    });
                }
                catch
                { }

                if (Interlocked.Decrement(ref _numberOfLinksLeft) == 0)
                    _subject.OnCompleted();
            }
        }

        private static List<Uri> Filter(IEnumerable<Uri> uris, params IUriFilter[] filters)
        {
            var filtered = uris.ToList();
            foreach (var filter in filters.ToList())
            {
                filtered = filter.Filter(filtered);
            }
            return filtered;
        }
    }

    public IObservable<Uri> Crawl(Uri uri)
    {
        return new ReceivingCrawledUri(uri, new ExcludeRootUriFilter(uri), new ExternalUriFilter(uri), new AlreadyVisitedUriFilter());
    }

    public IObservable<Uri> Crawl(Uri uri, params IUriFilter[] filters)
    {
        return new ReceivingCrawledUri(uri, filters);
    }
}

и вы можете использовать его следующим образом:

Crawler crawler = new Crawler();
IObservable observable = crawler.Crawl(new Uri("http://www.codinghorror.com/"));
observable.Subscribe(onNext: Console.WriteLine, 
onCompleted: () => Console.WriteLine("Crawling completed"));