Need help to design web crawler.

kolmem

Превет всем,
у кого нибудь есть документацию для того чтобы проектировал хороший webcrawler? Мне нужно чтобы он быстро скачал(сотни за секунду).

sergeikozyr

слишком толсто

okis

Why don't you take some existing crawler library like Könguló as a basis? You may also look at this thread: http://stackoverflow.com/questions/1853673/writing-a-faster-...

kolmem

Thanks you, but i need a good design. I know about Larbin, DataparkSearch (author is a VMK MSU) but it don't help me.

AlexV769

государственный поисковик отаке?

okis

What do you consider to be a good design? Have you read http://infolab.stanford.edu/~backrub/google.html ?

kolmem

yes, i've read it, and also this http://www.slideshare.net/gnap/design-and-implementation-of-... but i want to find more detail.

yolki

национальный™

Werdna

у кого нибудь есть документацию для того чтобы проектировал хороший webcrawler? Мне нужно чтобы он быстро скачал(сотни за секунду).
Самый крутолько качальщик — это Бачан, он сечет круче всех ИМХО. Но так как Бачан не всегда хочет общаться, то могу я рассказать основы.
Во-первых, качать нужно только в один поток. Потоков может быть несколько, один в том смысле, что надо неблокирующе качать.
1: Надо ли писать на диск? Если надо, то число качающих потоков по числу дисков (физических).
Разумеется, надо использовать для скачивания curl, он прекрасно работает как в примитивном режиме, так и на мультплексе. Бачан даже его с libev скрещивал, что просто гениально.
Во-вторых, лучше всего первичную обработку делать сразу, а не писать на диск. Чаще всего надо что-то взять со странички, проиндексировать или что-то ещё, очень редко надо сохранять всё скачанное.
2: Насколько сложная обработка данных? Данные лучше обрабатывать сразу, число потоков — по числу ядер в системе.
Не повредит хороший балансёр, отправляющий на скачку. Нужно отправлять ровно столько урлов на скачивание, сколько будет успевать обрабатываться.
Если всё сделать так как я описал, то дальше тонкости ещё есть. Опиши что делаешь, что качаешь — может ещё что-то подскажу.

erotic

curl хреново работает в мультиплексе, у него проблемы ловить таймауты. Кое-как прикрутил их ловлю в сам libevent, но это довольно костыльное решение. Без него таймаутящаяся закачка могла подвисать и больше о себе не напоминать никакими событиями.
И да, почему в один поток-то качать? Чаще всего этого хватает, конечно, но бывают ситуации, когда даже тупая асинхронная качалка во что-то упирается, т.к. top показывал для качающего потока большую загрузку проца, то думаю, что в проц. Но такое редко бывало, и наверное просто из-за некоторой обработки по ходу скачивания.

Werdna

curl хреново работает в мультиплексе, у него проблемы ловить таймауты. Кое-как прикрутил их ловлю в сам libevent, но это довольно костыльное решение. Без него таймаутящаяся закачка могла подвисать и больше о себе не напоминать никакими событиями.
Слушай, а скинь мне в личку, интересно, я не знал о таком деле.
И да, почему в один поток-то качать?

Под одним потоком я имел скорее в виду "конечное число по числу ядер". А так вот Возбу, тоже уже специалист по закачке. :)
Топикстартер, ау! Ты где?

slonishka

curl хреново работает в мультиплексе, у него проблемы ловить таймауты. Кое-как прикрутил их ловлю в сам libevent, но это довольно костыльное решение. Без него таймаутящаяся закачка могла подвисать и больше о себе не напоминать никакими событиями.
я сначала хотел ответить по существу, а потом заметил (внезапно
что абзац написан "в стиле пианиста" и это прикольно.
по существу.
если я правильно понимаю, курл предполагает мультиплекс типа селект(2 когда все почти как в easy
и мультиплекс типа ваш-собственный-евент-драйвер(7 когда евент-драйвер - ваш, собственный, костыльный.
то есть, с libevent там все вроде бы так и задумано делать, как ты делаешь, и "без него таймаутящаяся (ааааа,
какое слово охуенное) закачка могла подвисать", в случае libevent, makes no sense, потому что собственный-
евент-драйвер(7) без обработки таймаутов по DIY-принципу makes no sense either.
короче, я ничего не понял из того, что ты написал, но решил, тем не менее, поделиться мыслями
и ссылкой на мою любимую статью про таймауты, которую написал марк леманн:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev...
интересная часть называется "Be smart about timeouts".
прекрасное название!
что касается подвисаний вообще, вот это полезно помнить,
как мне кажется (часть про EVBACKEND_EPOLL):
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIO...

kolmem

для скачания страницы, я собираю написать на python. Это часть называется downloader ещё часть называется crawlApp для обработки данных и сохранении новых урлов в очереди (или БД) от сюда downloader будет скачать с этими урлами. Эти части будет разговаривать через сокет. Как Вы думаете так нормально?

Dasar

производительность какая необходима?
хочется считать 10 страниц, тысячу, миллионы?

erotic

"Внезапно" - это в стиле Максима. Ты специально?
Поведаю историю вопроса. За основу был взять один твой демон, который так никогда и не эксплуатировался. Из него я выдрал curl'о-libevent'овский механизм и обернул в классик. Таймауты там выставлялись через curl_easy механизм (curl_easy_setopt) для каждой закачки, т.е. на DNS, transfer и connect. Кстати, прикольно, щас в мане увидел
CURLOPT_LOW_SPEED_TIME
Pass a long as parameter. It contains the time in seconds that the transfer should be below the CURLOPT_LOW_SPEED_LIMIT for the
library to consider it too slow and abort.

Ну вот. Это попытались эксплуатировать, но качало очень плохо, там были десятки или сотни одновременных закачек, и много из них фейлилось. Я перекопал весь код, перечитал по использованию curl_multi манов и примеров в интернете, мне казалось, что все правильно. Когда я уже окончательно сломал голову, админы подняли в конфиге тайм-аут, и стало работать гораздо лучше :)
Вывод отсюда такой - таймауты в multi в том виде, в котором я использовал, есть. При этом libevent на сокеты сам таймауты не ставил, все делал curl, он мог выставить закачке код ошибки CURLE_OPERATION_TIMEDOUT.
Потом однажды я заметил, что есть висящие закачки. Не помню точно, что происходило, но они висели долго, и никаких событий на сокете не происходило.
Я почесал репу, сходил на сайт курла, как раз вышла версия 7.20.0, почитал Changelog (http://curl.haxx.se/changes.html увидел строчку
CURLM_CALL_MULTI_PERFORM fix for multi socket timeout calls
, обрадовался, собрал все с этим курлом.
Вроде стало лучше работать, но точно не помню, проблемы все равно были, поэтому я, тем не менее, повесил таймаут на качающий сокет, как сумму таймаутов connect+transfer, и в итоге закачку теперь обрубает libevent, если это не может сделать curl.
Возможно, конечно, я где-то что-то не так понял или что-то упустил, но вот так оно сейчас работает. Может быть на самом деле закачка просто идет очень медленно, а курловский таймаут ставится не на всю закачку, а только на период, когда данные не приходят совсем, и каждый чанк данных его сбрасывает. Хотя, на мой взгляд, это противоречило бы ману, который обещает таймаут на весь трансфер:
CURLOPT_TIMEOUT
Pass a long as parameter containing the maximum time in seconds that you allow the libcurl transfer operation to take. Normally,
name lookups can take a considerable time and limiting operations to less than a few minutes risk aborting perfectly normal oper-
ations. This option will cause curl to use the SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless CURLOPT_NOSIGNAL is set.

slonishka

дефайн CMP curl_multi_perform(3)
дефайн CMS curl_multi_socket(3)
дефайн CMSA curl_multi_socket_action(3)
Я когда переходил с CMP на CMSA, вся документация по последним противоречила
действительности, и я пошел читать архив рассылки:
http://news.gmane.org/gmane.comp.web.curl.library
Там тогда как раз появился пример использования нового интерфейса (в текущей
версии deprecated-вызовов CMS нет, вместо них используется CMSA с нулевой
ev_bitmask, то есть на устаревшую версию можно смело забить):
http://curl.haxx.se/libcurl/c/hiperfifo.html
И вот из него-то я и сделал вывод о том, что, во-первых, CMSA дает программисту
возможность управлять таймаутами так же гибко, как если бы curl-абстракции не
было вовсе, а во-вторых, что это один из поинтов CMSA-интерфейса - разным
прикладным областям нужны разного рода таймауты, поэтому для тех, кого не
устраивает просто таймаут на всю операцию, мы придумали CMSA. Это касается
любых эвентов на сокете, а не только таймаутов - гибкая обработка в обмен на
усложнение кода.
Не очень давно hiperfifo.c какой-то чувак из рассылки портировал на libev,
пианист про него, я думаю, говорил в первом своем сообщении:
http://curl.haxx.se/libcurl/c/evhiperfifo.html
Теперь, чтобы понять, нужны ли нам гибкие таймауты или мы можем забить на being
smart about them и пользоваться CURLOPT_*TIMEOUT, нам нужно ясно представлять
себе, чем мы рискуем in the latter case.
Okay, Let's Go (c):
CURLOPT_TIMEOUT
Pass a long as parameter containing the maximum time in seconds that you allow
the libcurl transfer operation to take. Normally, name lookups can take a
considerable time and limiting operations to less than a few minutes risk
aborting perfectly normal operations. This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.
Метод используемый курлом для выставления таймаутов - это полный пиздец. This
option will cause curl to use the SIGALRM to enable time-outing system calls.
Иными словами, минимальный код (без обработки ошибок) какой-то такой:

{
sigfunc *sf;

sf = signal(SIGALRM, conn_alrm);
alrm(CURLOPT_CONNECT_TIMEOUT);

if (0 > connect(sd, ...
{
close(sd);

if (EINTR == errno)
{
errno = ETIMEDOUT;
}
}

...

}

static void
conn_alrm(int signo)
{
return;
}

Первая проблема - перегрузка EINTR, кто-то может его использовать и тогда нам
надо либо делать sigsetjmp(3) и siglongjmp(3 что само по себе довольно хорни,
либо какие-то свои дескрипторы/идентификаторы придумывать, что я даже боюсь
начинать делать, потому что предполагаю, что это невозможно.
Вторая - в мультитредных приложениях SIGALRM приходит в зависимости от
блокировок сигналов и прочей шляпы хуй знает в какой тред.
В принципе, у меня уже взорвался мозг от того, что там может получиться только
засчет этой пары опасностей, но я могу попробовать придумать еще!
###############################################################################
"Внезапно" - это в стиле Максима. Ты специально?
ну меня просто развеселило неожиданное наблюдение: твой пост показался
не как обычно обстоятельным и богатым деталями, а списком хрен знает как,
на первый взгляд, связанных между собой, но на самом деле важных тезисов. =)
когда я утром перечитал, впечатление несколько ослабло, though.
CURLOPT_LOW_SPEED_TIME
Pass a long as parameter. It contains the time in seconds that the transfer
should be below the CURLOPT_LOW_SPEED_LIMIT for the library to consider it too
slow and abort.
Мне кажется, эту фичу для всяких видео/аудио-стриминг-приложений сделали. Не
глядя в код, конечно, хрен его знает, как оно на html-страницах себя поведет.
Звучит, как сложная логика, прикручивая которую норвежские программисты
наверняка опять все сломали. Но чем черт не шутит... может что-то вдруг и
починили.

slonishka

Жаль, что у вас UNPv1 нету (UNPv2, правда, лежал на русском).
В APUE про alrm-технику не было этих тонкостей.
Весной, кстати, научились брауни делать по рецепту Стивенса.
http://www.kohala.com/start/recipes/deirdre.brownie.html
Охуенный. Попробуйте тоже. :)

slonishka

ну вернее научилАсь, конечно.

slonishka

для скачания страницы, я собираю написать на python
the important thing is technique, not technology. python has a lot of modules with
the same functionality (on a brief view but completely different purposes. so you
should know, for example, which of html-processing modules fits your purposes
well to use python.

slonishka

более того, там по-моему вообще ничего приличного вместо UNPv1 нету.
и вместо TCPv1 тоже ничего, без которого TCPv2 программисту хуже пойдет имхо.
выпросьте у злых плантаторов стивенса всего хотя бы.
а то левис не фанат TCP/IP, а больше я не знаю, кто зачетные книги приносил.

erotic

Сейчас только CMSA в коде есть.
Как ты из примера http://curl.haxx.se/libcurl/c/hiperfifo.html сделал выводы и гибкости CMSA? В смысле, кто мешал не использовать в старых интерфейсах никаких таймаутов и делать все таймауты при обработке событий самому?
Кстати, мне еще странно, что они в своих примерах на каждую закачку создают curl_easy_init и уничтожают curl_easy_cleanup. Возможно, в силу простоты примеров, но однажды профилировщик показал, что эти операции занимают немало времени, а надобности в них никакой, поэтому я предпочел сделать пул этих хэндлеров, переиспользовать их быстрее.
На тему CURL_*_TIMEOUT и сигналов - как-то в man неоднозначно сказано, по-моему:
This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.

Из второго предложения я делаю вывод, что при установке флага CURLOPT_NOSIGNAL сигналы использоваться не будут. Хотя из первого делаю вывод, что будут :D
На самом деле, может быть все работает, потому что сначала все сигналы блокирую, потом рождаю дочерние потоки, они наследуют блокированность сигналов, потом уже в главном потоке расставляю обработчики сигналов, и только ему они приходят. При этом curl в своем потоке может разрешить SIGALRM, и он тогда будет приходить только в этот поток. Но все равно мне кажется, что он сигналы не использует с CURLOPT_NOSIGNAL.
Еще наткнулся в коде на коммент, ведущий меня к known bug (http://curl.haxx.se/docs/knownbugs.html):
62. CURLOPT_TIMEOUT does not work properly with the regular multi and
multi_socket interfaces. The work-around for apps is to simply remove the
easy handle once the time is up. See also:
http://curl.haxx.se/bug/view.cgi?id=2501457

erotic

Охуенный. Попробуйте тоже. :)
Рецепт жесть.

erotic

а то левис не фанат TCP/IP
О, он проповедует IPX?

slonishka

ну мы искали с ним как-то, откуда SYN_RCVD-ы на скольких-то-там-ка и он,
как почти всегда бывает, угадал лучше, потому что знал про мегасхему!
мегасхема: http://www.j3qq4.org/pic/megaschema.gif
но вообще сказал, что ему этим не оч прикольно заниматься.
он по всякому сжатию данных рубится вроде.

slonishka

Рецепт жесть.
ыыы! в каком смысле? =)
мы его адаптировали под суровые российские реалии,
я пришлю, если хочешь, он на бумажке просто. =)

slonishka

Как ты из примера http://curl.haxx.se/libcurl/c/hiperfifo.html сделал выводы и
гибкости CMSA? В смысле, кто мешал не использовать в старых интерфейсах никаких
таймаутов и делать все таймауты при обработке событий самому?
А как?
Кстати, мне еще странно, что они в своих примерах на каждую закачку создают
curl_easy_init и уничтожают curl_easy_cleanup. Возможно, в силу простоты
примеров, но однажды профилировщик показал, что эти операции занимают немало
времени, а надобности в них никакой, поэтому я предпочел сделать пул этих
хэндлеров, переиспользовать их быстрее.
Они неаккуратные. Либу читать невозможно.
Пул прекрасное решение!
Теоретически можно было бы заставить курл делать динамические аллокейты
(предполагаю, что они в клинапе тормозят) как тебе хочется и эффективно
распределять уже саму память, но это сложно и нужно мб только тем, кто хочет
память очень-очень экономить (предполагаю, что хендлеры из пула до клинапа не
освобождают память, выделенную под мегабайтную страницу, поэтому когда мы потом
в этом хендлере качаем килобайтную, мы вроде как неэффективно память
юзаем).
На тему CURL_*_TIMEOUT и сигналов - как-то в man неоднозначно сказано,
по-моему:

This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.
Из второго предложения я делаю вывод, что при установке флага CURLOPT_NOSIGNAL
сигналы использоваться не будут. Хотя из первого делаю вывод, что будут.
Wow! Ты прав, я сначала невнимательно прочитал про NOSIGNAL. Пугает то, что в
мане не написано, что делать, если заданы и таймаут, и носигнал. Возвращать
ошибку из setopt-а? Не получится (носигнал может быть задан позже и у него
приоритет). Не использовать таймауты? Ты говоришь, что они вроде бы срабатывают.
Пойду потом тоже код почитаю, видимо. =)
Еще есть надежда, что в мане опечатка и таймауты всегда ставятся с помощью ALRM,
тогда с ними все прозрачно.
...

slonishka

На самом деле, может быть все работает, потому что сначала все сигналы блокирую,
потом рождаю дочерние потоки, они наследуют блокированность сигналов, потом уже
в главном потоке расставляю обработчики сигналов, и только ему они приходят. При
этом curl в своем потоке может разрешить SIGALRM, и он тогда будет приходить
только в этот поток. Но все равно мне кажется, что он сигналы не использует с
CURLOPT_NOSIGNAL.
Ага, мне теперь тоже так кажется. Прикольно, тем не менее, что сигналы,
видимо, тоже будут работать. Хотел там race condition найти где-нибудь,
а его и нету походу.
Еще наткнулся в коде на коммент, ведущий меня к known bug (http://curl.haxx.se/docs/knownbugs.html):
 
62. CURLOPT_TIMEOUT does not work properly with the regular multi and
    multi_socket interfaces. The work-around for apps is to simply remove the
    easy handle once the time is up. See also:
    http://curl.haxx.se/bug/view.cgi?id=2501457
Ссылка крутая.
Что касается таймаута операции: а что он характеризует в случае неблокирующегося
скачивания нескольких файлов одновременно? По идее нужен таймер молчания на
каждый сокет.
Шутки ради, можно еще "общее" "время" молчания считать, это будет,
видимо, аналогом таймаута операции в блокирующемся режиме.

slonishka

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

erotic

часто бывает так, что размер данных почти не колеблется от запроса к
запросу, поэтому можно заменить "правильный" таймаут между чтениями
на более понятный эмпирически таймаут операции.
Я уже не очень хорошо все помню, но смотри: когда CURL говорит, что на сокет надо поставить событие, я его ставлю с таймаутом. Событие ставится не как EV_PERSIST, поэтому после его наступления его надо будет перепоставить. Т.к. я сам не переставляю событие в его обработчике, значит, CURL после наступления события опять вызывает свой колбек для сообщения мне, что надо поставить событие, и я его ставлю. Таким образом, таймаут устанавливается заново каждый раз, когда приходят данные, т.е. реализуется "правильный" вариант, хотя на самом деле я тогда желал сделать "неправильный", но вышло, что вышло.

erotic

предполагаю, что хендлеры из пула до клинапа не
освобождают память, выделенную под мегабайтную страницу, поэтому когда мы потом
в этом хендлере качаем килобайтную, мы вроде как неэффективно память
юзаем
А почему CURL вообще должен держать в памяти мегабайтную страницу? Тебе, конечно, виднее, раз ты его внутренности читал, но я считал, что процесс происходит так:
на сокет приходят данные, срабатыавет триггер, он вызывает курловые функции, тот вызывает recv на свои 16 кб, пока есть данные, для каждых 16 кб данных вызывается пользовательский колбек. Т.е. данные кочуют с сетевой карты в 16 кб буфер курла (ну или чуть больше в случае разжатия данных и оттуда сразу в пользовательский колбек. Где они тут могут храниться?

slonishka

про мегабайт - это "грубо говоря" все, забей на серый текст

slonishka

ага, я тоже плохо помню (и libevent-интерфейс плохо помню).
с этими оговорками звучит все хорошо вроде.
если при этом не использовать CURLOPT_*TIMEOUT,
то ошибки должны совсем исчезнуть.

slonishka

но вообще вот такие таймауты - это хорошие таймауты.
не парься даже, это не костыль ни разу! =)
ощущение костыля мб разве что потому что сам курл - это один большой костыль.
Оставить комментарий
Имя или ник:
Комментарий: