По воле случая на последнем месте работы познакомился с каркасом для сбора данных с web сайтов Scrapy, который реализован на Python. Сфера его использования – сбор структурированных данных со страниц. И хотя его область применения достаточно широка и включает в себя мониторинг и автоматизированное тестирование, но использовали мы его по большей части для сбора информации с сайтов. О самом фреймворке я напишу позже. Сейчас же опишу одну проблему, с которой я столкнулся, реализую один проект «для себя».

В рамках Scrapy я реализовал простой краулер для сбора информации о произведениях, которые размещены на сайте litru.ru. Я создал краулер, описал правила и начал спокойно описывать что откуда брать и куда класть, как это у нас происходило при написании краулеров. Решил проверить, запустить и просто полюбоваться на отладочный вывод, что краулер обходит сайт как надо и страницы парсятся правильно. Несколько секунд ожидания и вывод с информацией об ошибке:

2009-10-25 02:04:07+0300 [litru.ru] ERROR: Spider exception caught while processing <http://www.*******.ru/> (referer: <None>): [Failure instance: Traceback: <class 'sgmllib.SGMLParseError'>: expected name token at '<!\xe2\x80\x93 google_ad_sect'
 /usr/lib/python2.5/site-packages/twisted/internet/defer.py:312:_startRunCallbacks
 /usr/lib/python2.5/site-packages/twisted/internet/defer.py:328:_runCallbacks
 /usr/lib/python2.5/site-packages/twisted/internet/defer.py:243:callback
 /usr/lib/python2.5/site-packages/twisted/internet/defer.py:312:_startRunCallbacks
 --- <exception caught here> ---
 /usr/lib/python2.5/site-packages/twisted/internet/defer.py:328:_runCallbacks
 /usr/lib/python2.5/site-packages/scrapy/contrib/spiders/crawl.py:70:parse
 /usr/lib/python2.5/site-packages/scrapy/contrib/spiders/crawl.py:115:_response_downloaded
 /usr/lib/python2.5/site-packages/scrapy/contrib/spiders/crawl.py:95:_requests_to_follow
 /usr/lib/python2.5/site-packages/scrapy/contrib/linkextractors/sgml.py:100:extract_links
 /usr/lib/python2.5/site-packages/scrapy/contrib/linkextractors/sgml.py:42:extract_links
 /usr/lib/python2.5/site-packages/scrapy/contrib/linkextractors/sgml.py:25:_extract_links
 /usr/lib/python2.5/sgmllib.py:99:feed
 /usr/lib/python2.5/sgmllib.py:169:goahead
 /usr/lib/python2.5/markupbase.py:98:parse_declaration
 /usr/lib/python2.5/markupbase.py:388:_scan_name
 /usr/lib/python2.5/sgmllib.py:106:error
 ]

Все дело в том, что «глючит» SGMLParser, которым пользуется SgmlLinkExtractor для получения ссылок. Эти ссылки используются для дальнейшего обхода сайта. Причиной этого «глюка» является неправильно оформленный комментарий html. Вместо «<!–» в начале комментария и «–!>» в его конце, используется странно укороченная форма «<!-» и «-!>». SGMLParser считает все, что начинается с «<!» декларацией (как, например «<!DOCTYPE …>») и соответствующим способом пытается ее разобрать, а получает произвольный набор символов комментария.

В моей системе SGMLParser находится по следующему пути: /usr/lib/python2.5/sgmllib.py . Для того, чтобы все работало, я применил достаточно грязный «хак», т.е. заменил «<!» на «<!DOCTYPE» для того, чтобы неправильно определенный комментарий не попадал в обработку как декларация. Сейчас думаю о том, как сделать это изящнее, т.к. конечно изменять core libraries не есть комильфо :) .

В следующем блоке:

if rawdata.startswith("<!", i):
 # This is some sort of declaration; in "HTML as
 # deployed," this should only be the document type
 # declaration ("<!DOCTYPE html...>").
 k = self.parse_declaration(i)
 if k < 0: break
 i = k

Я поменял в первой строке «<!» на «<!DOCTYPE». Строка эта у меня носит номер 165, но все зависит от версии SGMLParser’а.

2 комментариев на «Столкновение с SGMLParser’ом»

  1. Ну все правильно, SGMLParser для идеалистов, а в реальном мире мужики пользуются html5lib (http://code.google.com/p/html5lib/), или BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/). :-) Html5lib судя по всему пока лучше справляется с ошибками: http://hlabs.spb.ru/news/beautifulsoup_vs_html5lib.html

  2. Мне тут хороший знакомый lxml использовать. По крайней мере его реализация примерно того, что мы делали на scrapy понравилась.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>