Аналитикам иногда нужно отвечать на вопросы вроде таких: «сколько сайтов используют WordPress, а сколько Ghost», «какое покрытие у Google Analytics, а какое у Метрики», «как часто сайт X ссылается на сайт Y». Самый честный способ на них ответить — пройтись по всем страничкам в интернете и посчитать. Эта идея не такая безумная, как может показаться. Существует проект Сommoncrawl, который каждый месяц публикует свежий дамп интернета в виде gzip-архивов суммарным размером в ~30Тб. Данные лежат на S3, поэтому для обработки обычно используется MapReduce от Amazon. Есть масса инструкций про то, как это делать. Но с текущим курсом доллара такой подход стал немного дороговат. Я хотел бы поделиться способом, как удешевить расчёт примерно в два раза.


Commoncrawl публикует список ссылок на S3. За июль 2015, например, он выглядит так:
common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438042981460.12/warc/CC-MAIN-20150728002301-00000-ip-10-236-191-2.ec2.internal.warc.gz
common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438042981460.12/warc/CC-MAIN-20150728002301-00001-ip-10-236-191-2.ec2.internal.warc.gz
common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438042981460.12/warc/CC-MAIN-20150728002301-00002-ip-10-236-191-2.ec2.internal.warc.gz
common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438042981460.12/warc/CC-MAIN-20150728002301-00003-ip-10-236-191-2.ec2.internal.warc.gz
common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438042981460.12/warc/CC-MAIN-20150728002301-00004-ip-10-236-191-2.ec2.internal.warc.gz
...

По каждой ссылке доступен архив на ~800Мб, примерно такого содержания:
WARC/1.0
WARC-Type: request
WARC-Date: 2015-08-05T12:38:42Z
WARC-Record-ID: <urn:uuid:886377b3-62eb-4333-950a-85caa9a8bce8>
Content-Length: 322
Content-Type: application/http; msgtype=request
WARC-Warcinfo-ID: <urn:uuid:54b96beb-b4cc-4f71-a1bf-b83c72aac9ad>
WARC-IP-Address: 88.151.247.138
WARC-Target-URI: http://0x20.be/smw/index.php?title=Special:RecentChangesLinked&hideanons=1&target=Meeting97

GET /smw/index.php?title=Special:RecentChangesLinked&hideanons=1&target=Meeting97 HTTP/1.0
Host: 0x20.be
Accept-Encoding: x-gzip, gzip, deflate
User-Agent: CCBot/2.0 (http://commoncrawl.org/faq/)
Accept-Language: en-us,en-gb,en;q=0.7,*;q=0.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8



WARC/1.0
WARC-Type: response
WARC-Date: 2015-08-05T12:38:42Z
WARC-Record-ID: <urn:uuid:17460dab-43f2-4e1d-ad99-cc8cfceb32fd>
Content-Length: 21376
Content-Type: application/http; msgtype=response
WARC-Warcinfo-ID: <urn:uuid:54b96beb-b4cc-4f71-a1bf-b83c72aac9ad>
WARC-Concurrent-To: <urn:uuid:886377b3-62eb-4333-950a-85caa9a8bce8>
WARC-IP-Address: 88.151.247.138
WARC-Target-URI: http://0x20.be/smw/index.php?title=Special:RecentChangesLinked&hideanons=1&target=Meeting97
WARC-Payload-Digest: sha1:6Z77MXWXHJYEHC75LGTN3UQMYVJAEPPL
WARC-Block-Digest: sha1:MQ4GSG7X7EU6H26SMF2NS5MADZULHOPK
WARC-Truncated: length

HTTP/1.1 200 OK
Date: Wed, 05 Aug 2015 12:31:01 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.19
X-Content-Type-Options: nosniff
Content-language: en
X-Frame-Options: SAMEORIGIN
Vary: Accept-Encoding,Cookie
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: private, must-revalidate, max-age=0
Last-Modified: Fri, 31 Jul 2015 02:16:39 GMT
Content-Encoding: gzip
Connection: close
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html lang="en" dir="ltr" class="client-nojs">
<head>
<meta charset="UTF-8" /><title>Changes related to "Meeting97" - Whitespace (Hackerspace Gent)</title>
...

Куча заголовков и с ходу ничего не понятно. Существуют специальные библиотеки для парсинга таких данных, но я использую код вида:
url_line = None
for line in sys.stdin:
    if line.startswith('WARC-Target-URI'):
        url_line = line
    elif url_line is not None:
        if 'www.google-analytics.com/analytics.js' in line:
            # Strip 'WARC-Target-URI: ' and '\r\n'
            yield url_line[17:-2]

Так получается раз в 10 быстрее по скорости и попроще при деплое. Можно прямо со своего компьютера запустить команду вида:
curl https://aws-publicdatasets.s3.amazonaws.com/common-crawl/crawl-data/CC-MAIN-2015-32/segments/1438044271733.81/warc/CC-MAIN-20150728004431-00294-ip-10-236-191-2.ec2.internal.warc.gz | gunzip | python grep.py

И получить список страничек с Google Analytics. Это совершенно бесплатно. Проблема только в том, что процесс займёт несколько минут и его нужно будет повторить ~30 000 раз для разных кусочков архива. Чтобы как-то ускорить расчёт обычно используют Elastic MapReduce и обрабатывают кусочки параллельно. Важно заметить, что воркеры также качают данные из S3, а не читают их из HDFS. Сколько будет стоить такое решение? Это зависит от региона и от машинок, которые будут использоваться.

С регионом понятно — нужно брать тот, который поближе к данным, то есть «Запад США». А вот с машинками непросто. Если взять совсем дешёвых будет долго считаться, если взять мощных будет дорого. Чтобы определиться, я арендовал 6 разных тачек и запустил на них команду, как в примере выше. Получилось, что на самой простой машине я смогу обрабатывать ~10 кусочков архива за час, а на самой крутой — ~1000. Умножив это на цены за аренду машин и за настройку на них MapReduce, увидел интересное: выгоднее брать самые супер пупер крутые тачки. Финальная цена получится меньше и кластер будет компактнее.


Здорово, но цена в ~70$ не радует, особенно если нужно повторить расчёт на нескольких месяцах, чтобы проследить динамику. У Амазона есть такая прекрасная штука — спот-инстансы. Цены на них значительно ниже и меняются во времени.


Самую крутую модель, которая обычно стоит 1.68$ за час, можно арендовать за ~0.3$. В чём подвох? Если найдётся, кто-то кто захочет дать больше, машину жёстко тушат, ничего не сохраняют и передают этому человеку. Чтобы погрепать интернет спот-инстансы подходят идеально. Даже если вычисления прервут, их легко возобновить. Жалко только, что за настройку MapReduce на спот-инстансах Амазон скидки не даёт:


Уже значительно лучше. Но теперь обидно платить за MapReduce ~0.3$, когда вся машина стоит ~0.3$. Особенно учитывая, что ни HDFS, ни reduce-ступень не используется. Здесь, наверное, и становится понятно, как можно сократить стоимость расчёта в два раза. Грубо говоря, нужно запилить свой MapReduce на коленочке. Для данной задачи это сделать несложно. Нужно просто написать скрипт, который всё делает сам: скачивает архив, распаковывает его и грепает. И запустить его за нескольких машинах с разными параметрами, я делал это через screen примерно так:
scp run_grep.py ubuntu@machine1:~
ssh ubuntu@machine1 -t 'screen -d -r "python run_grep.py 2015-07 0 1000"'
scp run_grep.py ubuntu@machine2:~
ssh ubuntu@machine2 -t 'screen -d -r "python run_grep.py 2015-07 1000 2000"'
scp run_grep.py ubuntu@machine3:~
ssh ubuntu@machine3 -t 'screen -d -r "python run_grep.py 2015-07 2000 3000"'
...

Если ещё немного напрячься и настроить сбор логов с машин, можно сделать себе роскошный мониторинг:

Комментарии (16)


  1. fomistoklus
    05.10.2015 01:04

    можно прикрутить configuration tool (like chef or puppet) чтобы разворачивание происходило автоматически + логику написать, которая будет выдавать этому CT спот инстансы и вести учет того, что посчиталось и что должно посчитаться.


  1. andyN
    05.10.2015 04:40

    То есть получается 18 тыс. долларов чтобы погрепать весь интернет? Верно?


    1. lopatoid
      05.10.2015 05:34

      Сначала тоже удивился, а потом понял, что это 18 долларов.


    1. alexkuku
      05.10.2015 12:22
      +1

      ~10$


  1. mickvav
    05.10.2015 07:16
    +5

    Если вас устраивает результат с некой погрешностью — можно ещё сделать выборку случайного подмножества, например 1000 дампов из 30000 уже буду давать неплохую точность на запросах вида 'сколько сайтов используют wordpress'


  1. VioletGiraffe
    05.10.2015 08:04
    +1

    Какая степень сжатия у архивов? 30 ТБ — не маловато ли? Мы так мало настрочили за столько лет?


    1. kashey
      05.10.2015 08:13
      +4

      Говорят, что большую часть интернета занимает порно. Но тут «без картинок»


    1. Xfrid
      05.10.2015 08:13

      Тоже интересно, этот паук дотягивается до страничек в фейсбуке и контакте?


      1. VioletGiraffe
        05.10.2015 08:21

        Во-во! Я не пытался оценить (можно попробовать), но не удивлюсь, если в одном только ФБ больше 30 ТБ текста.


        1. Xfrid
          05.10.2015 08:27

          Ну, полная коллекция флибусты в полмиллиона книг весит 388гб в сжатом виде, а там форматы не чисто текстовые, весят прилично и сжимаются хуже.


    1. alexkuku
      05.10.2015 12:16
      +2

      ~5. После распаковки получается ~150Тб. Да, там не весь интернет. Они прокачивают некоторый конечный набор урлов. Что хуже всего не раскрывается, как формируется этот набор. Вроде бы, его предоставляет Blekko. По опыту могу сказать, что там находится ~10% урлов от того, что должно было найтись.


  1. joger
    05.10.2015 10:01
    +1

    проверил через urlsearch.commoncrawl.org пару доменов. либо ничего, либо только главная. хотя никаких запретов через роботс.тхт или мета. так что придётся и дальше самому :(


  1. Stas911
    05.10.2015 18:30
    +1

    Боюсь спросить, сколько должен стоить час времени работы аналитика-инженера, чтобы такие оптимизации окупались. Но в целом да, оптимизации интересные.


    1. Stas911
      05.10.2015 18:34

      Интенесно было б еще посмотреть на цифры для Azure HDInsight


  1. bkh
    08.10.2015 04:20

    А чем вам для этой задачи не подошел apache spark? Там и скрипты для разворачивания кластера на ec2 есть, и спот инстансы можно заказать. Не говоря уже о таких штуках как автоматическое распиливание датасета c s3 и агрегация результата.


    1. alexkuku
      08.10.2015 11:19

      Тем что я им не пользовался никогда