Столкновение с SGMLParser’ом

5 Сен
2011

По воле случая на последнем месте работы познакомился с каркасом для сбора данных с 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’а.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх