2015-05-26 4 views
2

Я создал паук, который получает данные о запасах для данного запаса с такого количества страниц, которое имеет запас (это может быть 1 страница данных запаса или 20 от Yahoo! Finance). Он хорошо просматривает все страницы и собирает все данные, как следует. Однако по какой-то причине он не сохранит ни одну из данных в фактическом элементе scrapy, так что я могу экспортировать его как csv.Scrapy собирает данные, но не сохраняет их в элементе

См. Обновление внизу этого вопроса!

Сейчас я покажу вам код, а затем пример того, что он печатает:

Кодекс: (Пожалуйста, голые со мной, это довольно долго)

class DmozSpider(Spider): 


    name = "dnot" 
    allowed_domains = ["finance.yahoo.com", "http://eoddata.com/"] 
    start_urls = ['http://finance.yahoo.com/q?s=CAT'] 


    def stocks1(self, response): 

     current_page = response.url 
     print current_page 
     # If the link is not the same as the first page, ie. stocks1 is requested through stocks2, get the stock data from stocks2 
     if initial_ending not in current_page[-iel:]: 
      returns_pages = response.meta.get('returns_pages') 
      # Remove the last stock price from the stock list, because it is the same as the first on the new list 
      if not not returns_pages: 
       if len(returns_pages) > 2: 
        returns_pages = returns_pages[:-1] 
     else: 
      # Else, if the link does match that of the first page, create a new list becuase one does not exist yet 
      returns_pages = [] 

     # This grabs the stock data from the page 
     rows = response.xpath('//table[@class="yfnc_datamodoutline1"]//table/tr')[1:] 
     print "stocks1" 
     print returns_pages 
     for row in rows: 
      cells = row.xpath('.//td/text()').extract() 
      try: 
       values = cells[-1] 
       try: 
        float(values) 
        # And adds it to returns_pages 
        returns_pages.append(values) 
       except ValueError: 
        continue 
      except ValueError: 
       continue 
     print "after" 
     print returns_pages 

     # exp determines if there is a 'Next page' or not 
     exp = response.xpath('//td[@align="right"]/a[@rel="next"]').extract() 
     # If there is a 'Next Page': 
     if not not exp: 
      # And this is the first page: 
      if initial_ending in current_page[-iel:]: 
       #create necessary url for the 2nd page 
       next_page = current_page + "&z=66&y=66" 
      # If this is not the first page 
      else: 
       # This increases the end of the link by 66, thereby getting the next 66 results on for pages 2 and after 
       u = int(current_page[-6:].split("=",1)[1]) 
       o = len(str(u)) 
       u += 66 
       next_page = current_page[:-o] + str(u) 
       print next_page, "66&y in curr_page" 
      # Then go back to self.stocks1 to get more data on the next page 
      yield Request(next_page, self.stocks2, meta={'returns_pages': returns_pages}) 
     # Else, if there is no 'Next Link' 
     else: 
      # Send the retuns to finalize.stock to be saved in the item 
      yield Request(current_page, self.finalize_stock, meta={'returns_pages': returns_pages}) 

    def stocks2(self, response): 

     # Prints the link of the current url 
     current_page = response.url 
     print current_page 

     # Gets the returns from the previous page 
     returns_pages = response.meta.get('returns_pages') 
     # Removes the last return from the previous page because it will be a duplicate 
     returns_pages = returns_pages[:-1] 
     print "stocks2" 
     print returns_pages 
     # Gets all of the returns on the page 
     rows = response.xpath('//table[@class="yfnc_datamodoutline1"]//table/tr')[1:] 
     for row in rows: 
      cells = row.xpath('.//td/text()').extract() 
      try: 
       values = cells[-1] 
       try: 
        float(values) 
        # And adds it to the previous returns 
        returns_pages.append(values) 
       except ValueError: 
        continue 
      except ValueError: 
       continue 

     print "after 2" 
     print returns_pages 

     # exp determines if there is a 'Next page' or not 
     exp = response.xpath('//td[@align="right"]/a[@rel="next"]').extract() 
     # If there is a 'Next Page': 
     if not not exp: 
      # And somehow, this is the first page (should never be true) 
      if initial_ending in current_page[-iel:]: 
       # Add necessary link to go to the second page 
       next_page = current_page + "&z=66&y=66" 
       print next_page, "66&y not in curr_page" 
      # Else, this is not the first page (should always be true) 
      else: 
       # add 66 to the last number on the preceeding link in order to access the second or later pages 
       u = int(current_page[-6:].split("=",1)[1]) 
       o = len(str(u)) 
       u += 66 
       next_page = current_page[:-o] + str(u) 
       print next_page, "66&y in curr_page" 
      # go back to self.stocks1 to get more data on the next page 
      yield Request(next_page, self.stocks1, meta={'returns_pages': returns_pages}) 
     else: 
      # If there is no "Next" link, send the retuns to finalize.stock to be saved in the item 
      yield Request(current_page, self.finalize_stock, meta={'returns_pages': returns_pages}) 
      print "sending to finalize stock" 

    def finalize_stock(self,response): 

     unformatted_returns = response.meta.get('returns_pages') 
     returns = [float(i) for i in returns] 
     global required_amount_of_returns, counter 
     if counter == 1 and "CAT" in response.url: 
      required_amount_of_returns = len(returns) 
     elif required_amount_of_returns == 0: 
      raise CloseSpider("'Error with initiating required amount of returns'") 

     counter += 1 
     print counter 

     # Iterator to calculate Rate of return 
     # ==================================== 
     if data_intervals == "m": 
      k = 12 
     elif data_intervals == "w": 
      k = 4 
     else: 
      k = 30 

     sub_returns_amount = required_amount_of_returns - k 
     sub_returns = returns[:sub_returns_amount] 
     rate_of_return = [] 
     RFR = Risk_free_rate 

     # Make sure list is exact length, otherwise rate_of_return will be inaccurate 
     # Returns has not been checked by pipeline yet, so small lists will be in the variable 

     if len(returns) == required_amount_of_returns or "CAT" in response.url: 
      for number in sub_returns: 
       numerator = number - returns[k] 
       rate = numerator/returns[k] 
       if rate == '': 
        rate = 0 
       rate_of_return.append(rate) 
       k += 1 

     item = Website() 
     items = [] 
     item['url'] = response.url 
     item['name'] = response.xpath('//div[@class="title"]/h2/text()').extract() 
     item['avg_returns'] = numpy.average(rate_of_return) 
     item['var_returns'] = numpy.cov(rate_of_return) 
     item['sd_returns'] = numpy.std(rate_of_return) 
     item['returns'] = unformatted_returns 
     item['rate_of_returns'] = rate_of_return 
     item['exchange'] = response.xpath('//span[@class="rtq_exch"]/text()').extract() 
     item['ind_sharpe'] = ((numpy.average(rate_of_return) - RFR)/numpy.std(rate_of_return)) 
     items.append(item) 
     yield item 

Я попытался прокомментировать все, чтобы кто-нибудь читал это.

Как это работает:

По существу, он идет к данной акции и принимает фондовый данные, представленные на первой странице. Затем он видит, есть ли ссылка «следующая страница». Если есть, он передаст его stocks2, если есть еще один next page, он вернет его обратно до stocks1 и продолжит делать это, пока не будет больше страниц. После того, как страниц больше нет, он отправит данные в finalize_stock, где предполагается сохранить все эти данные, а также обрабатывать данные, которые не имеют значения для целей этого вопроса.

Выход: (Небольшой образец)

print current_page 
http://finance.yahoo.com/q/hp?s=PZA.TO&a=04&b=19&c=2005&d=04&e=19&f=2006&g=d&z=66&y=198 
print "stocks 2" 
stocks2 
# print returns_pages | before scraped this page 
[u'4.75', u'4.78', u'4.78', u'4.83', u'4.87', u'4.90', u'4.90', u'4.97', u'4.99', u'4.92', u'4.95', u'4.90', u'4.90', u'4.93', u'4.92', u'4.90', u'4.89', u'4.88', u'4.95', u'4.90', u'4.95', u'4.95', u'4.95', u'4.90', u'4.90', u'4.90', u'4.90', u'4.95', u'4.91', u'4.91', u'4.90', u'4.92', u'4.92', u'4.92', u'4.91', u'4.92', u'4.91', u'4.91', u'4.90', u'4.92', u'4.95', u'4.95', u'4.85', u'4.83', u'4.91', u'4.90', u'4.92', u'4.95', u'4.95', u'4.92', u'4.92', u'4.92', u'4.87', u'4.88', u'4.92', u'4.90', u'4.94', u'4.90', u'4.97', u'4.97', u'4.94', u'4.90', u'4.92', u'4.83', u'4.83', u'4.83', u'4.83', u'4.75', u'4.75', u'4.78', u'4.80', u'4.81', u'4.81', u'4.84', u'4.80', u'4.78', u'4.83', u'4.80', u'4.83', u'4.81', u'4.87', u'4.97', u'5.02', u'4.97', u'4.91', u'4.91', u'4.92', u'4.90', u'4.90', u'4.83', u'4.79', u'4.83', u'4.83', u'4.83', u'4.83', u'4.77', u'4.78', u'4.81', u'4.83', u'4.83', u'4.85', u'4.80', u'4.87', u'4.87', u'4.87', u'4.87', u'4.83', u'4.83', u'4.83', u'4.87', u'4.85', u'4.83', u'4.83', u'4.87', u'4.83', u'4.83', u'4.85', u'4.83', u'4.80', u'4.80', u'4.78', u'4.71', u'4.72', u'4.71', u'4.73', u'4.71', u'4.54', u'4.29', u'4.25', u'4.23', u'4.25', u'4.27', u'4.25', u'4.30', u'4.32', u'4.30', u'4.30', u'4.27', u'4.27', u'4.22', u'4.13', u'4.12', u'4.15', u'4.15', u'4.22', u'4.22', u'4.27', u'4.27', u'4.27', u'4.25', u'4.46', u'4.22', u'4.39', u'4.37', u'4.36', u'4.57', u'4.63', u'4.68', u'4.68', u'4.67', u'4.73', u'4.80', u'4.83', u'4.84', u'4.84', u'4.85', u'4.85', u'4.81', u'4.78', u'4.83', u'5.06', u'5.09', u'5.02', u'5.12', u'5.09', u'5.12', u'5.14', u'5.07', u'5.06', u'4.99', u'5.00', u'4.97', u'4.98', u'4.98', u'4.95', u'4.92', u'4.98', u'4.92', u'4.93', u'4.93', u'4.95', u'4.94', u'4.92', u'4.90', u'4.85'] 
# print "after 2" 
after 2 
#Print returns_pages | after scraped this page 
[u'4.75', u'4.78', u'4.78', u'4.83', u'4.87', u'4.90', u'4.90', u'4.97', u'4.99', u'4.92', u'4.95', u'4.90', u'4.90', u'4.93', u'4.92', u'4.90', u'4.89', u'4.88', u'4.95', u'4.90', u'4.95', u'4.95', u'4.95', u'4.90', u'4.90', u'4.90', u'4.90', u'4.95', u'4.91', u'4.91', u'4.90', u'4.92', u'4.92', u'4.92', u'4.91', u'4.92', u'4.91', u'4.91', u'4.90', u'4.92', u'4.95', u'4.95', u'4.85', u'4.83', u'4.91', u'4.90', u'4.92', u'4.95', u'4.95', u'4.92', u'4.92', u'4.92', u'4.87', u'4.88', u'4.92', u'4.90', u'4.94', u'4.90', u'4.97', u'4.97', u'4.94', u'4.90', u'4.92', u'4.83', u'4.83', u'4.83', u'4.83', u'4.75', u'4.75', u'4.78', u'4.80', u'4.81', u'4.81', u'4.84', u'4.80', u'4.78', u'4.83', u'4.80', u'4.83', u'4.81', u'4.87', u'4.97', u'5.02', u'4.97', u'4.91', u'4.91', u'4.92', u'4.90', u'4.90', u'4.83', u'4.79', u'4.83', u'4.83', u'4.83', u'4.83', u'4.77', u'4.78', u'4.81', u'4.83', u'4.83', u'4.85', u'4.80', u'4.87', u'4.87', u'4.87', u'4.87', u'4.83', u'4.83', u'4.83', u'4.87', u'4.85', u'4.83', u'4.83', u'4.87', u'4.83', u'4.83', u'4.85', u'4.83', u'4.80', u'4.80', u'4.78', u'4.71', u'4.72', u'4.71', u'4.73', u'4.71', u'4.54', u'4.29', u'4.25', u'4.23', u'4.25', u'4.27', u'4.25', u'4.30', u'4.32', u'4.30', u'4.30', u'4.27', u'4.27', u'4.22', u'4.13', u'4.12', u'4.15', u'4.15', u'4.22', u'4.22', u'4.27', u'4.27', u'4.27', u'4.25', u'4.46', u'4.22', u'4.39', u'4.37', u'4.36', u'4.57', u'4.63', u'4.68', u'4.68', u'4.67', u'4.73', u'4.80', u'4.83', u'4.84', u'4.84', u'4.85', u'4.85', u'4.81', u'4.78', u'4.83', u'5.06', u'5.09', u'5.02', u'5.12', u'5.09', u'5.12', u'5.14', u'5.07', u'5.06', u'4.99', u'5.00', u'4.97', u'4.98', u'4.98', u'4.95', u'4.92', u'4.98', u'4.92', u'4.93', u'4.93', u'4.95', u'4.94', u'4.92', u'4.90', u'4.85', u'4.85', u'4.86', u'4.92', u'4.93', u'4.92', u'4.95', u'4.93', u'4.94', u'4.95', u'4.96', u'4.95', u'4.95', u'4.95', u'4.95', u'4.98', u'4.97', u'4.92', u'4.94', u'4.90', u'4.93', u'4.93', u'4.97', u'4.97', u'4.97', u'4.90', u'5.00', u'5.02', u'5.11', u'5.12', u'5.12'] 
2015-05-25 17:41:46-0700 [dnot] DEBUG: Crawled (200) <GET http://finance.yahoo.com/lookup?s=PVS.PR.D.TO> (referer: http://eoddata.com/stocklist/TSX/P.htm) 
2015-05-25 17:41:46-0700 [dnot] DEBUG: Redirecting (301) to <GET http://finance.yahoo.com/lookup?s=PUD.B.TO> from <GET http://finance.yahoo.com/lookup;_ylc=X3oDMTF2cTUxaTdhBGtleXcDUFVELkIuVE8EbWlkA21lZGlhcXVvdGVzc2VhcmNoBHNlYwNnZXRxdW90ZXNidG4Ec2xrA2xvb2t1cA--?s=PUD.B.TO> 
sending to finalize stock # See here, it does call the def finalize_stock function 

Однако ничто не сохранялось пунктов. Обычно scrapy будет печатать предметы, когда они будут сохранены, но это не делает, и я не могу понять, почему.

Если вам нужна дополнительная информация, просто спросите, и я отправлю ее как можно скорее.

Update: проблема найдена, но не решена:

раньше, в def stocks1 и def stocks2 я имел:

 else: 
     # If there is no "Next" link, send the retuns to finalize.stock to be saved in the item 
     yield Request(current_page, self.finalize_stock, meta={'returns_pages': returns_pages}) 
     print "sending to finalize stock" 

в нижней части каждой функции, которая в основном означает, что, когда нет рядом перейдите на страницу finalize_stock и сохраните эту информацию. print "sending to finalize stock" действительно становится печати, однако:

Защиту finalize_stock (я, ответ):

print "=====================" 
print "finalize_stock called" 
print "=====================" 

никогда не печатается! Поэтому по какой-то причине def finalize_stock никогда не запускается, и я понятия не имею, почему.

+0

В чем смысл всех статических переменных и 'if not not return_pages'? на линии 10ish что-то? – Zizouz212

+0

@ Zizouz212 'if not not returns_pages' гарантирует, что' returns_pages' не пуст, поэтому следующая строка 'len (returns_pages)' не возвращает ошибку. Что касается «Все статические переменные», я не уверен, что вы имеете в виду. Я новичок в python, поэтому я просто делаю то, что имеет смысл для меня, скорее всего, не самые эффективные методы, которые я все еще изучаю. 'def stocks1' и' def stocks2' не должны иметь значения, хотя, как они работают, кажется, что проблема заключается в отправке данных в 'def finalize_stocks' – thefoxrocks

+0

Вы работали в java или c или в любом стиле * brace * языки? Потому что в противном случае вы вообще не получите классы python ... – Zizouz212

ответ

2

Ваш код выглядит очень сложным и трудным для отладки. Я думаю, что нет необходимости в нескольких обратных вызовах и вычислениях для создания ссылки.

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

import scrapy 

class ValueItem(scrapy.Item): 
    value = scrapy.Field() 

class StockSpider(scrapy.Spider): 

    name = "yahoo_stock_spider" 
    allowed_domains = ['finance.yahoo.com'] 
    start_urls = ['http://finance.yahoo.com/q/hp?s=CAT&a=00&b=1&c=2015&d=04&e=26&f=2015&g=d' ] 

    def parse(self, response): 

     if 'item' in response.meta: 
      # If the response contains a 'item' from a previous page unwrap it 
      item = response.meta['item'] 
     else: 
      # if it contains no such item, it's the first page, so let's create it 
      item = ValueItem() 
      item['value'] = [''] 


     # Loop over the table rows 
     rows = response.xpath('//table[@class="yfnc_datamodoutline1"]//table//tr') 
     for row in rows[1:]: 

      cell_values = row.xpath('.//td/text()').extract() 
      item['value'] = item['value'] + [cell_values[-1]] 


     # Check if there is a 'Next' link 
     xpath_Next_Page = './/a[contains(.,"Next")]/@href' 
     if response.xpath(xpath_Next_Page): 
      # No need to calculate offset values. Just take the link ... 
      next_page_href = response.xpath(xpath_Next_Page).extract()[0] 
      url_next_page = 'http://finance.yahoo.com' + next_page_href 
      # ... build the request ... 
      request = scrapy.Request(url_next_page, callback=self.parse) 
      # ... and add the item with the collected values to the request 
      request.meta['item'] = item 
      yield request 
     else: 
      # No more 'Next' 
      # here simple output of uncleaned values 
      yield item 
+0

Вау! Это делает мой код похожим на позор! Всегда ли это следует за xpath_Next_Page? Например, если есть три или более страниц? – thefoxrocks

+1

Я никогда не знал, что вы можете отправить мета в рамках той же функции! Потрясающие! Не могу вас поблагодарить! – thefoxrocks

Смежные вопросы