2017-01-23 3 views
0

Это следование по вопросу от Python Scrapy & YieldPython Scrapy и плодоношения

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

скребковые:

  1. Царапина одна страницы, чтобы получить список диапазонов дат
  2. использует эти даты диапазонов форматировать URLS для затем соскрести другую страницу, которая содержит списки, которые разбиваются на страницах в группы из 10 списков
  3. Я бы хотел отменить все эти URL-адреса, которые ссылаются на 10 объявлений
  4. Затем на этих страницах я хотел бы отказаться от всех списков и извлечь данные из них. Эти индивидуальные списки также содержат 4 «вкладки», которые необходимо очистить.

    class MyScraper(scrapy.Spider): 
    name = "myscraper" 
    
    start_urls = [ 
    ] 
    
    
    def parse(self, response): 
        rows = response.css('table.apas_tbl tr').extract() 
        for row in rows[1:]: 
         soup = BeautifulSoup(row, 'lxml') 
         url = soup.find_all("a")[1]['href'] 
         yield scrapy.Request(url, callback=self.parse_page_contents) 
    
    def parse_page_contents(self, response): 
        rows = response.xpath('//div[@id="apas_form"]').extract_first() 
        soup = BeautifulSoup(rows, 'lxml') 
        pages = soup.find(id='apas_form_text') 
        for link in pages.find_all('a'): 
         url = link['href'] 
         yield scrapy.Request(url, callback=self.parse_page_listings) 
    
    def parse_page_listings(self, response): 
        rows = response.xpath('//div[@id="apas_form"]').extract_first() 
        soup = BeautifulSoup(rows, 'lxml') 
        resultTable = soup.find("table", { "class" : "apas_tbl" }) 
    
        for row in resultTable.find_all('a'): 
         url = row['href'] 
         yield scrapy.Request(url, callback=self.parse_individual_listings) 
    
    
    def parse_individual_listings(self, response): 
        rows = response.xpath('//div[@id="apas_form"]').extract_first() 
        soup = BeautifulSoup(rows, 'lxml') 
        fields = soup.find_all('div',{'id':'fieldset_data'}) 
        data = {} 
        for field in fields: 
         data[field.label.text.strip()] = field.p.text.strip() 
    
        tabs = response.xpath('//div[@id="tabheader"]').extract_first() 
        soup = BeautifulSoup(tabs, 'lxml') 
        links = soup.find_all("a") 
        for link in links: 
         yield scrapy.Request(
          urlparse.urljoin(response.url, link['href']), 
          callback=self.parse_individual_tabs, 
          meta={'data': data} 
         ) 
        print data 
    
    def parse_individual_tabs(self, response): 
        data = {} 
        rows = response.xpath('//div[@id="tabContent"]').extract_first() 
        soup = BeautifulSoup(rows, 'lxml') 
        fields = soup.find_all('div',{'id':'fieldset_data'}) 
        for field in fields: 
         data[field.label.text.strip()] = field.p.text.strip() 
    
        yield json.dumps(data) 
    

Скребок в настоящее время, кажется, немного работы с проблемами, хотя. Основная проблема на данный момент:

ERROR: Spider must return Request, BaseItem, dict or None, got 'str' in

также существует некоторое дублирование URL-адресов, которые очищаются. Мне интересно (а), что вызывает вышеприведенную ошибку, и (б) правильно ли форматируется выходная настройка?

+0

Вам не обязательно, чтобы вы se 'yield': вы можете просто построить список, в котором вы храните все элементы' yield'ed и возвращаете список. –

+0

@WillemVanOnsem, пока вы правы, это более удобно и, как правило, лучше использовать значения 'yield' вместо того, чтобы хранить их во временном списке и затем возвращать весь список.Не только 'yield' выглядит лучше, но использование' yield' вместо 'return' превращает функцию в генератор, который работает лучше. – Granitosaurus

+1

@ Granitosaurus: Да, я использую 'yield' все время (несмотря на то, что это единственное, что нравится в моей рабочей области). Это также лучше для памяти (скажем, вы испускаете миллион объектов, каждый из которых занимает несколько мегабайт, а список не может их хранить). Но в этом случае кривая обучения, вероятно, менее крутая при использовании списков. –

ответ

0

Вы возвращаете строку в одном из ваших методов синтаксического анализа:

def parse_individual_tabs(self, response): 
    data = {} 
    rows = response.xpath('//div[@id="tabContent"]').extract_first() 
    soup = BeautifulSoup(rows, 'lxml') 
    fields = soup.find_all('div',{'id':'fieldset_data'}) 
    for field in fields: 
     data[field.label.text.strip()] = field.p.text.strip() 

    yield json.dumps(data) #<---- here 

В сообщении об ошибке говорит:

ERROR: Spider must return Request, BaseItem, dict or None, got 'str' in

yield data Так что вместо этого, так как данные являются ДИКТ.

Edit:
Что касается вашего второго вопроса - есть проблема с последними методами два разбора:

def parse_individual_listings(self, response): 
    # <..> 
    data = {} 
    for field in fields: 
     data[field.label.text.strip()] = field.p.text.strip() 

    # <..> 
    for link in links: 
     yield scrapy.Request(
      urlparse.urljoin(response.url, link['href']), 
      callback=self.parse_individual_tabs, 
      meta={'data': data} # <-- you carry data here to below 
     ) 

def parse_individual_tabs(self, response): 
    data = {} # <--- here's the issue 
    # instead you should retrieve data carried from above: 
    data = response.meta['data'] 
    # <..> 
    for field in fields: 
     data[field.label.text.strip()] = field.p.text.strip() 
    return data # also cine there's only 1 element you can just return it instead of yielding, it makes no difference 
+0

Спасибо за это. Это исправило ошибку. Однако теперь информация из каждой вкладки появляется сама по себе. Кажется, что он не связан с данными dict в parse_individual_listings. Есть ли ошибка в моей структуре доходности? –

+0

@ 19421608 Право, я вижу, ознакомьтесь с моим правлением относительно этого. – Granitosaurus

0

вы, дающим запросом в parse_individual_listings поэтому нет необходимости давать данные в parse_individual_tabs. потому что генераторы ленивы, поэтому нужно вернуться, чтобы запустить его.

Исправленный код:

import json 

из urllib.parse импорта urljoin

импорта Scrapy из BS4 импорта BeautifulSoup

класс MyScraper (scrapy.Spider): имя = "myscraper"

start_urls = [ 
] 

def parse(self, response): 
    rows = response.css('table.apas_tbl tr').extract() 
    for row in rows[1:]: 
     soup = BeautifulSoup(row, 'lxml') 
     url = soup.find_all("a")[1]['href'] 
     yield scrapy.Request(url, callback=self.parse_page_contents) 

def parse_page_contents(self, response): 

    rows = response.xpath('//div[@id="apas_form"]').extract_first() 
    soup = BeautifulSoup(rows, 'lxml') 
    pages = soup.find(id='apas_form_text') 
    for link in pages.find_all('a'): 
     url = link['href'] 
     yield scrapy.Request(url, callback=self.parse_page_listings) 


def parse_page_listings(self, response): 
    rows = response.xpath('//div[@id="apas_form"]').extract_first() 
    soup = BeautifulSoup(rows, 'lxml') 
    resultTable = soup.find("table", {"class": "apas_tbl"}) 

    for row in resultTable.find_all('a'): 
     url = row['href'] 
     yield scrapy.Request(url, callback=self.parse_individual_listings) 


def parse_individual_listings(self, response): 
    rows = response.xpath('//div[@id="apas_form"]').extract_first() 
    soup = BeautifulSoup(rows, 'lxml') 
    fields = soup.find_all('div', {'id': 'fieldset_data'}) 
    data = {} 
    for field in fields: 
     data[field.label.text.strip()] = field.p.text.strip() 

    tabs = response.xpath('//div[@id="tabheader"]').extract_first() 
    soup = BeautifulSoup(tabs, 'lxml') 
    links = soup.find_all("a") 
    for link in links: 
     yield scrapy.Request(
      urljoin(response.url, link['href']), 
      callback=self.parse_individual_tabs, 
      meta={'data': data} 
     ) 
    print 
    data 


def parse_individual_tabs(self, response): 
    data = {} 
    rows = response.xpath('//div[@id="tabContent"]').extract_first() 
    soup = BeautifulSoup(rows, 'lxml') 
    fields = soup.find_all('div', {'id': 'fieldset_data'}) 
    for field in fields: 
     data[field.label.text.strip()] = field.p.text.strip() 

    return json.dumps(data) 
Смежные вопросы