Поиск по блогу

пятница, 24 октября 2014 г.

Работа над ошибкой в первом xml спайдере XMLFeedSpider привела к рождению метафоры "Сознание-Мозг"

Начал было писать pipelines для Первый вариант паука XMLFeedSpider (без pipelines) Но паук никак не желал работать. На поиск простой ошибки я потратил целый день. Здесь я пытаюсь воспроизвести свои действия и решения, дабы хоть немного поумнеть.
014-10-24 18:27:23+0400 [proxylists] ERROR: Spider error processing

Результат написания этого поста

Старые представления: искать причину (разбираться с объектами и процессами) занятие трудоемкое, это инженерный подход. Подход современных "молодых" это "метод тыка"... Ноые представления: Старые представления нечетки, а значит их надо конкретизировать. Отлаживаемый код должен быть разбит на фрагменты и тестироваться в процессе написания, а потом при сборке. Чтобы организовать свой квазипроцесс junit... надо научится дробить код на идеально мелкие фрагменты, ...а внутри фрагмента можно и методом тыка... Бросаю все и перечитываю дебаггеры. Еще один вопрос, как откатить к старому коду (модулю, файлу) риторический, ответ - надо использовать репозитории. Поэтому пока будем про него помнить и ждать, когда у меня появится первый десяток "боевых" пауков... их и посадим в репозиторий.

Update_1: после четвертого прочтения Практикум 1 по статье "pdb – Interactive Debugger"

Просмотрел предыдущие посты этого блога (метка debugger) и (оказывается) в четвертый раз прочитал статью, при этом в консоли открыл дебаггер и скопировал в новый пост основные описания (например, (Pdb) help w ). С четвертого раза удалось прочитать и запомнить все... Об этом отдельный пост, а здесь стоит отметить, что все мои рассуждения далее кажутся лишними..., кроме заявлений о методиках проверки "простой и короткий алгоритм отладки" (квазитестов). Но чтобы их выбрать, нужно попробовать разные варианты ...

Далее первоначальный текст поста (в самом конце о борьбе Сознание-Мозг)

Начнем с результата. Вот решение - паук, который работает

In [5]:
%load C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists.py
In []:
from scrapy.contrib.spiders import XMLFeedSpider
from XMLFeedSpider.items import XmlfeedspiderItem
import pdb 
class ProxylistsSpider(XMLFeedSpider):
    name = 'proxylists'
    #allowed_domains = ['proxylists.net']
    start_urls = ['file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider/proxylists_short.xml']
    #instead of online ['http://www.proxylists.net/feed.xml']
    namespaces = [('prx','http://www.proxyrss.com/specification.html'),('content','http://purl.org/rss/1.0/modules/content/')]
    iterator = 'xml' # you can change this; see the docs
    itertag = 'prx:proxy' # change it accordingly
    #pdb.set_trace()
    
    def parse_node(self, response, selector):
        i = XmlfeedspiderItem()
  #i['title'] = selector.select('title').extract()
        #i['link'] = selector.select('link').extract()
        #i['descr'] = selector.xpath('description').extract()
        pdb.set_trace()
        i['prxip'] = selector.xpath("//prx:proxy/prx:ip/text()").extract()
#        i['prxport'] = selector.xpath("//prx:port/text()").extract() 
#        i['prxtype'] = selector.xpath("//prx:type/text()").extract() 
#        i['prxcountry'] = selector.xpath("//prx:country/text()").extract() 
#        i['prxcheck_timestamp'] = selector.xpath("//prx:check_timestamp/text()").extract()
        return i
In []:
#Чтобы он заработал, нужно было заменить строчку
iterator = 'iternodes' # you can change this; see the docs
#на медленный нерекомендуемый в документации вариант
iterator = 'xml' # you can change this; see the docs

Итак, что же происходит. Я пишу middleware для своего паука (см. посты xmlfeed), думаю о наборе полей для итератора, конвертации строки в дату, критериях фильтрации полей... Потом запускаю все вместе До этого запускал паука, чтобы разобраться с namespces:

In [6]:
%load C:\\Users\\kiss\\Documents\\GitMyScrapy\\scrapy_xml_1\\XMLFeedSpider\\XMLFeedSpider\\spiders\\proxylists_bak.py
In []:
"""
Created on Fri Oct 24 14:57:31 2014

@author: kiss
"""
from scrapy.contrib.spiders import XMLFeedSpider
from XMLFeedSpider.items import XmlfeedspiderItem

class ProxylistsSpider(XMLFeedSpider):
    name = 'proxylists_bak'
    #allowed_domains = ['proxylists.net']
    start_urls = ['file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpider/proxylists_short.xml']
    #instead of online ['http://www.proxylists.net/feed.xml']
    iterator = 'iternodes' # you can change this; see the docs
    itertag = 'item' # change it accordingly

    def parse_node(self, response, selector):
        i = XmlfeedspiderItem()
        i['title'] = selector.select('title').extract()
        i['link'] = selector.select('link').extract()
        #i['description'] = selector.select('description').extract()
        return i

И прямо из этого кода начал делать proxylists.py (Этот вариант старого файла выше я восстановил вчера, когда искал ошибку).

Итак, я получил ошибку, после того, как запустил измененного паука с подключенными к нему двумя модулями pipelines.. . Ошибка не выдавалась в консоль. Почему? Много времени ушло на то, чтобы понять, что не парсит сам паук, хотя выводились строчки

In []:
...
2014-10-24 13:12:04+0400 [proxylists] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2014-10-24 13:12:05+0400 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:6023
2014-10-24 13:12:05+0400 [scrapy] DEBUG: Web service listening on 0.0.0.0:6080
2014-10-24 13:12:05+0400 [proxylists] DEBUG: Crawled (200) <GET file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpide
r/proxylists.xml> (referer: None)
2014-10-24 13:12:05+0400 [proxylists] INFO: Closing spider (finished)
...

Надпись [proxylists] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) я заметил, но придал ей значения. Следствие: начал искат ошибку в файлах pipelines.

Почему??? Наверное, потому, что я до этого с ними работал. Контекст энграммы - это те участки мозга, которые были активны только что. Надо было переключать контекст..., а у меня просто не на что переключать... у меня нет:
1. Опыта работы с дебаггером
2. Понимания namespaces
3. Специфики Scrapy (объект XMLFeedSpider)

Можно ли, не зная всего этого, сразу определить сначала объект (namespaces), а потом найти способ исправления?
Очевидно, ДА! Рецепт выведем чуть ниже. А сейчас продолжим работу над ошибками.

Как я догадался, что причина в модуле spider/proxylists.py ?

Я сначала тупо находил "опечатки" во всех модулях, потом, после того, как заработал код, я продолжал искать во всех модулях. Очевидно, что запускать код надо было последовательно подключая (наращивая модули). Каждый модуль надо доводить до "рабочего" состояния, даже если изменения незначительны, все равно - ПРОВЕРИТЬ. Соответственно и в моем случае, если уж сцепи сразу несколько модулей, то начинай отладку по частям.

Инструменты для отладки по частям

  1. Научится подключать дебаггер PDB, чтобы для любого кода быстро посмотреть результат. Результат должен быть "распечатан".
  2. В Scrapy дебаггер влючен по умолчанию, есть раздел руководства *Debugging Spiders

После локализации ошибки я начал искать и нашел "пример", а он "неправильный"

Что же еще я сделал неправильно? Вместо того, чтобы внимательно прочитать сообщения из консоли..., я начал искать ПРИМЕР (потому что прочитал ранее и помнил, что он есть...) полез в документацию... и там быстро нашел вот этот "неправильный" пример:

Пример из документации namespaces, который ссбил меня с толку

*A list of (prefix, uri) tuples which define the namespaces available in that document that will be processed with this spider. The prefix and uri will be used to automatically register namespaces using the register_namespace() method.

You can then specify nodes with namespaces in the itertag attribute.*

In []:
#Example:

class YourSpider(XMLFeedSpider):

    namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
    itertag = 'n:url'
    # ...

Здесь просто не указано (забыли), что надо указать iterator = 'xml'

Далее в пауке я закомментировал все возможные причины ошибок Но продолжал тупо "тыкать", вместо того, чтобы почитать и подумать над вот этим:

In []:
2014-10-24 18:51:49+0400 [proxylists] DEBUG: Crawled (200) <GET file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XMLFeedSpide
r/proxylists_short.xml> (referer: None)
2014-10-24 18:51:49+0400 [proxylists] ERROR: Spider error processing <GET file://C:/Users/kiss/Documents/GitMyScrapy/scrapy_xml_1/XM
LFeedSpider/proxylists_short.xml>
        Traceback (most recent call last):
          File "C:\Users\kiss\Anaconda\lib\site-packages\twisted\internet\base.py", line 824, in runUntilCurrent
            call.func(*call.args, **call.kw)
          File "C:\Users\kiss\Anaconda\lib\site-packages\twisted\internet\task.py", line 638, in _tick
            taskObj._oneWorkUnit()
          File "C:\Users\kiss\Anaconda\lib\site-packages\twisted\internet\task.py", line 484, in _oneWorkUnit
            result = next(self._iterator)
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\utils\defer.py", line 57, in <genexpr>
            work = (callable(elem, *args, **named) for elem in iterable)
        --- <exception caught here> ---
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\utils\defer.py", line 96, in iter_errback
            yield next(it)
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spidermiddleware\offsite.py", line 23, in process_spider_out
put
            for x in result:
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spidermiddleware\referer.py", line 22, in <genexpr>
            return (_set_referer(r) for r in result or ())
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spidermiddleware\urllength.py", line 33, in <genexpr>
            return (r for r in result or () if _filter(r))
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spidermiddleware\depth.py", line 50, in <genexpr>
            return (r for r in result or () if _filter(r))
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spiders\feed.py", line 61, in parse_nodes
            for selector in nodes:
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\contrib\spiders\feed.py", line 87, in _iternodes
            for node in xmliter(response, self.itertag):
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\utils\iterators.py", line 32, in xmliter
            yield Selector(text=nodetext, type='xml').xpath('//' + nodename)[0]
          File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\selector\unified.py", line 77, in xpath
            raise ValueError("Invalid XPath: %s" % query)
        exceptions.ValueError: Invalid XPath: //prx:proxy

Здесь же черным по белому написано, что штатные модули Scrapy понимают 'prx:proxy', как ошибку, а раз в документации есть пример такого синтаксиса, то нужно разобраться, какие опции надо поменять... Если нет документации, то надо смотреть код.

Наверное, я просто не понимаю, что не умею "читать" код

Потому интуитивно предпочитаю путешествия по StackOverflow и GitHub..., где надеюсь найти готовые примеры. Мозг решает эти задачи так, как ему удобно. Как быстрее научиться понимать код с листа? Вопрос риторический. Очевидно, что нужно практиковаться и, что примеры должны быть выполнимыми. Но здесь стеки очень длинные (для меня)... Хотя, что здесь непонятного (во фрагменте выше)? Сначала Twisted, потом spidermiddleware... что там внутри происходит, я уже представляю..., так что интересны только то, что происходит в пауке ... spiders\feed.py

In []:
#  Вот она, ключевая строчка:
File "C:\Users\kiss\Anaconda\lib\site-packages\scrapy\utils\iterators.py", line 32, in xmliter
            yield Selector(text=nodetext, type='xml').xpath('//' + nodename)[0]
In []:
# Если предположить, что 
nodename = 'prx:proxy'
# воспринимается штатным модулем, как ошибка,
# то надо поменять другие параметры на входе
In []:
#Чтобы он заработал, нужно было заменить строчку
iterator = 'iternodes' # you can change this; see the docs
#на медленный нерекомендуемый в документации вариант
iterator = 'xml' # you can change this; see the docs

Это гениальное открытие я совершил после того, как хорошо выспался (на следующее утро). И вот сижу пол-дня пишу этот пост, чтобы исключить такие потери времени в будущем.

Когда надо думать, а когда "тыкать" методом тыка. Метафлоа "Сознание-мозг"

  1. Думать лучше, потому, что от этого умнеешь.
  2. "Тыкать" предпочитает мозг, потому что не хочет думать (когнитивная скупость).
  3. Если я сам думаю о Pipelines, то мозг я не контролирую, и он начинает "тыкать".
  4. Мне не хочется выгружать из сознания энграммы с Pipelines и загружать туда..., что???
  5. Нужен простой и короткий алгоритм отладки, а у меня его нет... так что же загружать в сознание?
  6. Нужно отложить все и сформировать ПРАВИЛА дебаггинга, иначе навсегда останусь ламером...


Посты чуть ниже также могут вас заинтересовать

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

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