Как написать гусеничный?

Вопрос задан: 11 лет назад Последняя активность: 2 года назад
up 61 down

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

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

10 ответов

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

Реклама

up 146 down

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

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

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

Алгоритм это:

while(list of unvisited URLs is not empty) {
    take URL from list
    remove it from the unvisited list and add it to the visited 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's not already in either the visited or unvisited list
              add it to the unvisited list
        }
    }
}
up 29 down

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

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

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

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

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

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

  • Неверно сформированные HTML-коды

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

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

  • Переписать процедуры и приоритеты.

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

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

  • Респект robots.txt

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

up 8 down

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

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

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

Сканер использует BFS для обхода всемирной паутины.

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

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

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

     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;
         }
      }
    

up 5 down

Гусеницы просты в концепции.

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

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

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

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

up 5 down

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

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

Некоторые проблемы (есть и другие):

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

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

Тем не менее, я бы не стал писать свой сканер. Это большая работа, и так как вам нужен только «простой сканер», я думаю, все, что вам действительно нужно, это готовый гусеничный ход. Существует много бесплатных сканеров с открытым исходным кодом, которые, скорее всего, сделают все, что вам нужно, с минимальной работой с вашей стороны.

up 2 down

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

up 1 down

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

up 0 down

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

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

up 0 down

я сделал простой веб-сканер, используя реактивное расширение в .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"));