Блог Игоря Шевченко

Cейчас мы будем скрейпить весь интернет питоном

Почти весь технический контент русскоязычного интернета публикуется либо в маленьких блогах, где его почти никто не видит, либо пишется специально для «Хабрахабра», который посещают уже миллионы человек в день. В англоязычном интернете работает «средний» вариант. Существуют социальные новостные сайты вроде реддита с разделом /r/programming или Hacker News, куда одни пользователи отправляют ссылки на интересные материалы, а другие плюсуют, минусуют, обсуждают. Любой может опубликовать что-то у себя в блоге, а потом разместить ссылку на свой пост там, чтобы привлечь читателей и комментаторов.

Но при этом существует проблема с тем, чтобы пользоваться этим. Например, на Hacker News количество публикаций за день достигает почти что тысячи. Поэтому просто подписаться на RSS с новыми постами нельзя: они забьют всю ленту, а найти что-то хорошее среди них будет тяжело. Приходится придумывать сложные ритуалы. К примеру, на реддит я захожу стабильно по воскресеньям, открываю лучшее за неделю и делаю закладки на ссылках, чтобы посмотреть их на следующей неделе.

С тем самым Hacker News оказалось еще сложнее. Хотя контент там часто попадается интересный, непонятно, как за ним следить. Даже ритуала никакого не придумать. Постов много, списков лучшего за какой-то период нет. Только определяемое по неизвестной формуле лучшее на данный момент. Так что единственный вариант — постоянно вспоминать и заходить на сайт. Мне это не очень нравилось. Подумав и вдохновившись многочисленными статьями из серии «сейчас мы будем скрейпить весь интернет питоном», я решил написать штуку, которая поможет мне.

Этой штукой был скрейпер, который каким-то образом должен с HN некоторое количество лучших постов за вчерашний день и создавать RSS-поток, чтобы я без лишних действий мог получать его себе в ленту. При этом хотелось делать всё культурно, не напрягая, в первую очередь, сам сайт количеством запросов или чем-то еще. Уже не помню где, но видел, как Пол Грэм, создатель сайта, негативно отзывался о ботах, которые грузят не только первую страницу списка постов, но и следующие по ссылке «More», хотя в robots.txt написано так не делать. Я так делать не и стал, хотя это и исключало самый первый логичный вариант: заходить в раз день, загружать пару десятков страниц со списком постов за день, извлекать из них лучшие и публиковать. Можно было на странице с новыми постами только собирать список всех постов за день, а перед публикацией заходить непосредственно в эти посты и получать число голосов за них. Но в том же самом robots.txt установлено требование не делать запросы чаще, чем раз в 30 секунд. Так на загрузку всех постов за день может уйти часов восемь, а то и больше. Такой вариант тоже не подходит хотя бы потому, что за это время число голосов на первых загруженных страницах уже успеет измениться, появятся новые посты, в общем, результат получится неконсистентным.

Я сделал так: с некоторой периодичностью бот получает список постов, которые сейчас находятся на главной странице. Это тоже логично: много ссылок за день и не нужно, а топ-10, например, уж точно побывает на главной. Запросы я решил делать раз в полчаса, потому что, опять же, даже если какой-то пост успеет появиться на главной и снова пропасть в течение получаса, то он нас скорее всего интересовать и не будет.

Следующая часть веб-скрейпинга после загрузки — это разбор полученной страницы и выделение нужной информации. В данном случае нужная — это ссылка, заголовок, количество очков и идентификатор каждого поста. Несмотря на то, что сайт в духе старой школы свёрстан таблицами, трудностей с этим не было, если не считать пару внезапно обнаруженных фич. Например, оказалось, что на HN в списке могут быть рекламные ссылки, за которые голосовать нельзя, и очков у них нет. Другая неожиданность — если ссылка ведёт на pdf-файл, то этот файл загружается еще и на сервис scribd, а заголовок содержит уже две ссылки (исходную и на скрибд). Эту вещь я обнаружил далеко не сразу, и она меня заставила какое-то время поразбираться — а почему же ссылки, которые хранятся у меня для некоторых постов, не соответствуют ссылкам, которые в этих постах на самом деле.

Для парсинга я сначала использовал библиотеку Beautiful Soup. Потом вспомнил, что в обсуждении каждой из того самого множества статей про скрейпинг на питоне обязательно кто-то говорит, что lxml работает быстрее, чем Beautiful Soup (а еще всегда кто-то должен сказать: «А я использую для этого Scrapy»). Переделал на lxml, сравнил скорость: действительно, быстрее. В 90 раз. Поэтому оставил вариант на lxml, хотя временные затраты на парсинг в любом случае меньше, чем на скачивание страницы по сети.

Наконец, получив информацию обо всех постах, нужно выводить десять лучших за день. Здесь проблема была не в том, как выбрать лучшие, — есть же голоса пользователей, ничего придумывать для этого не надо. Вопрос в том, какие считать сегодняшними. Во-первых, дата и время публикации на сайте выводятся в неудобном формате: «5 часов назад», «1 день назад», а в другом виде их никак не получить (обычно делают, чтобы хотя бы при наведении курсора мыши всплывала полная дата). Во-вторых, даже если бы я мог точно сказать про любой пост — этот опубликован сегодня, а этот еще вчера, возникла бы другая проблема. Предположим, что какой-то пост был опубликован за десять минут до окончания дня. К моменту публикации подборки он еще не набирает достаточно голосов, чтобы попасть в нее. За следующий день он выходит на главную страницу, набирает их, даже, может быть, висит на первом месте, но в подборку опять не попадает. Потому что он вчерашний.

Поэтому я решил вообще не принимать во внимание дату публикации, а просто выбирать каждый раз десять новых ссылок с самым большим числом голосов. При таком подходе подборка всё равно будет свежей. А что-то старое (вчерашнее, например) если и будет встречаться, то ближе к концу, да и то вполне заслужено. Кроме того, это решение позволило назначать время публикации подборки совсем произвольно.

За время публикации у меня отвечает cron, который регулярно запускает две самодельные команды для manage.py: одну для загрузки информации с сайта, другую для публикации подборки. Это время я назначил на 23:00 UTC. Это пять утра по челябинскому времени. Пять часов — это отличное время. С одной стороны, ссылки с утра уже успеют появиться в ленте, чтобы их можно было посмотреть по дороге или в университете. С другой, это уже достаточно поздно, чтобы засидевшись вечером не «дождаться» внезапного появления подборки в ленте. Для меня, по крайней мере, это время удобно. А в том, что кто-то еще будет пользоваться этой штукой, я не очень уверен.

http://igorshevchenko.ru/hnews/

15 апреля 2013 22:53

Комментарии

Володя Коган 25 мая 2013 04:30

Полезный сервис, я даже подзалип ненадолго :)
Но как обычно бывает в топ попадают попсовые новости о крупных корпорациях, а самое интересное в середине.

Игорь Шевченко 25 мая 2013 22:57

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

Оставить комментарий

Вы можете войти через Твитер или ВКонтакте