Scrapy: удаление строки Feeds / n из списка - PullRequest
0 голосов
/ 09 июля 2020

Я абсолютно новичок ie в рассмотрении scrapy / python и даже программирования, но мне нужно выучить и понять это. Я создал небольшого паука, который сканирует веб-сайт, извлекает с него некоторую информацию и объединяет ее в файл csv. Пока что я получил довольно успешный результат, однако у меня проблема с самим содержанием сайта. Селектор xpath для элемента содержимого сайта возвращает содержимое в виде списка; однако я хочу, чтобы он превратился в одну строку без перевода строки et c. Я знаю, что normalized-space и strip () будут путем к go, однако они удаляют только переводы начальной / конечной строки, в результате чего на выходе получается ['']. У меня есть трудности с реализацией al oop в коде, который выполняет итерацию по списку и в конце объединяет строку, которую можно правильно сохранить в csv. Кто-нибудь может мне помочь?

Вот паук:

import scrapy
class QuotesSpider(scrapy.Spider):

    name = "nbtest"
    allowed_domains = ['norisbank.de']
    start_urls = ['https://www.norisbank.de']

    custom_settings={ 'FEED_URI': "norisbank_%(time)s.csv",
                      'FEED_FORMAT': 'csv',
                      }

     def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'nbtest-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

        #Content Extraction
        print(response.url)
        sitename = response.xpath("//meta[@property='og:site_name']/@content")[0].getall()
        siteurl = response.xpath("//link[@rel='canonical']/@href")[0].getall()
        dbCategory = response.xpath("//meta[@name='dbCategory']/@content")[0].getall()
        titles = response.css('title::text').extract()
        descriptions = response.xpath("//meta[@name='description']/@content")[0].getall()
        date = response.xpath("//meta[@name='date']/@content")[0].getall()
        version = response.xpath("//meta[@name='v']/@content")[0].getall()
        time = response.xpath("//meta[@name='time']/@content")[0].getall()
        sitecontent = response.xpath('//body//p//text()').extract()

        #Sort extracted conten in rows
        for item in zip(sitename,siteurl,dbCategory,titles,descriptions,date,version,time,sitecontent):

            scraped_info = {
                'sitename' : item[0],
                'siteurl' : item[1],
                'dbCategory' : item [2],
                'title' : item [3],
                'description' : item [4],
                'date' : item [5],
                'version' : item [6],
                'time' : item [7],
                'sitecontent' : item [8]
            }

            yield scraped_info

        all_pages = response.xpath('//a[contains(@href, "html")]/@href').getall()
        for next_page in all_pages :
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

Ответы [ 3 ]

1 голос
/ 09 июля 2020

Вы можете использовать ItemLoaders, которые поместят ваши значения в список, однако для преобразования списка в строку можно применить нечто, называемое обработчиком вывода, называемое Join ().

См. здесь

ItemLoaders

ItemLoaders - удобный и немного более лаконичный способ получения данных из ответа, чем элементы или создание словаря. Прелесть класса ItemLoader заключается в том, что он дает вам доступ к процессорам ввода и вывода, которые позволяют изменять данные до и после ввода в словарь элементов с помощью встроенных методов scrapy. Но что еще лучше, он позволяет вам создавать свои собственные методы / функции, чтобы каждый элемент можно было изменять любым способом.

Итак, вместо использования элементов, мы создаем экземпляр класса загрузчика элементов. Это помещает данные, собранные с помощью xpath / css, и добавляет их в список, связанный с полем элемента. Преимущество этого заключается в том, что любая очистка или изменение данных в словаре элементов.

Пример

В вашем скрипте scrapy spider

from scrapy.loader import ItemLoader 
from ..item import XXXItem 

def parse(self,response):
   l = ItemLoader(item=XXXItem(), response=response)
   l.add_xpath('field_name1','//div[@class="name"]')
   l.add_xpath('field_name2','//div[@class="title"]')
   yield l.load_item()

Мы указываем класс элемента в качестве первого аргумента в загрузчике элементов и в ответе. Затем мы используем метод add_xpath для определения имени поля элемента и xpath для получения нужных нам данных, а также используем метод load_item () для заполнения словаря элементов.

Нет необходимости в extract () или get ( ) вы можете просто ввести имя поля элемента и xpath, и оно заполнит это поле. Вы можете добавить столько значений к каждому имени поля, Itemloaders заполняет список для каждого поля элемента.

В items.py

class XXXItem(Item): 
    field_name1 = scrapy.Field()
    field_name2 = scrapy.Field()

Теперь вы спросили об изменении списка, который вы извлекли, сейчас scrapy позволяет делать это с помощью ItemLoaders и метода Join (). См. здесь

Внутри items.py

class XXXItem(Item): 
    field_name1 = scrapy.Field(output_processor=Join())
    field_name2 = scrapy.Field()

Мы определяем в методе поля output_processor, любой элемент будет обрабатываться методом Join. В случае одного значения в списке это эквивалентно "" .join (field_name1).

Допустим, у вас есть список из одного элемента, он преобразует его в строку.

Стоит потратить время на изучение этой функциональности, поскольку, хотя ваш вопрос о преобразовании списка в строку немного похож на молоток для решения проблемы, ItemLoaders может быть очень полезен для более сложного изменения данных и дает вам гораздо больше гибкости, чем создание словаря.

0 голосов
/ 09 июля 2020

Итак, Армин, вот код

Spider Script

import scrapy
from scrapy.loader import ItemLoader
from ..items import NorisbankItem

class TestSpider(scrapy.Spider):

    name = "nbtest"
    allowed_domains = ['norisbank.de']
    start_urls = ['https://www.norisbank.de']
    custom_settings={ 'FEED_URI': "norisbank_%(time)s.csv",
                  'FEED_FORMAT': 'csv',
                  }

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'nbtest-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

    #Content Extraction
        print(response.url)

        l = ItemLoader(NorisbankItem(), response=response)

        l.add_xpath('sitename', "//meta[@property='og:site_name']/@content")
        l.add_xpath('siteurl', "//link[@rel='canonical']/@href")
        l.add_xpath('dbCategory',"//meta[@name='dbCategory']/@content")
        l.add_css('title','title::text')
        l.add_xpath('descriptions',"//meta[@name='description']/@content")
        l.add_xpath('date',"//meta[@name='date']/@content")
        l.add_xpath('version',"//meta[@name='v']/@content")
        l.add_xpath('time',"//meta[@name='time']/@content")
        l.add_xpath('sitecontent','//body//p//text()')
        yield l.load_item()

        all_pages = response.xpath('//a[contains(@href, "html")]/@href').getall()
        for next_page in all_pages:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

Я удалил код, связанный с элементами. Все, что связано с изменением данных, я всегда использую загрузчики элементов. Вы видите, что нам нужно импортировать ItemLoader и Item Class в паук. Мы создаем экземпляр класса загрузчика элементов, мы должны определить класс элемента для его заполнения и убедиться, что загрузчик элементов может использовать ответ, чтобы мы могли выбрать XPATH для этого ответа.

Затем мы используем метод add_xpath (), первый Аргумент определяет поле элемента в items.py, а второй аргумент - это xpath. Обратите внимание, что нам не нужно выполнять get (). Если к этому xpath привязано несколько значений, загрузчики элементов поместят их в список. Фактически, это именно то, что делает Itemloaders: он помещает данные в список независимо от того, одно значение это или больше. Этот список затем заполняет словарь элементов. Мы должны предоставить метод load_item для заполнения этого словаря элементов в конце. Не слишком разные, но более компактные и лаконичные.

Items

import scrapy
from scrapy.item import Item,Field
from scrapy.loader.processors import MapCompose,Join

def clean(x):

    if x:
        return x

class NorisbankItem(Item):
     define the fields for your item here like:
     name = scrapy.Field()
     sitename = scrapy.Field()
     siteurl = scrapy.Field()
     dbCategory = scrapy.Field()
     title = scrapy.Field()
     descriptions = scrapy.Field()
     date = scrapy.Field()
     version = scrapy.Field()
     time= scrapy.Field()
     sitecontent = scrapy.Field(input_processor=MapCompose(str.strip,clean),
                                output_processor=Join(' '))

Здесь, в items.py, мы определяем поля элементов. Контент сайта - это то место, где мы применяем процессоры ввода и вывода, которые дают возможность загрузчикам элементов. Есть несколько способов использовать процессор ввода / вывода, самый простой я нахожу в коде. Процессоры ввода используются для изменения данных до того, как данные будут заполнены в словаре элементов. Здесь мы используем MapCompose, он принимает все значения и передает их в функцию / метод, который мы указываем, здесь мы используем метод удаления строки, мы удаляем строку и затем передаем каждое значение в функцию очистки.

Это Чистая функция - наша собственная, опять же другая мощь Itemloaders. Просто мы хотим вернуть значения, если они не пустые, т.е. не ''. Помните, что это будет помещено в список значений, что и делает Itemloader при заполнении словаря элементов. Здесь, в аутпроцессоре, мы берем каждое значение в списке и объединяем его, разделяя его пробелом.

Другие мысли

Старайтесь не путать селекторы CSS и селекторы XPATH при использовании itemloaders или любой скрипт scrapy. Я предпочитаю селекторы xpath, так как у вас больше гибкости. Если вы не хотите использовать загрузчики элементов или скрипт не вызывает их, используйте get () и getall () вместо extract (), это просто быстрее и немного короче.

0 голосов
/ 09 июля 2020

Аарон, большое спасибо за ваш ответ и за то, что вы нашли время просветить меня. ItemLoaders кажутся очень хорошим методом для улучшения моего паука, и я думаю (!) Я понял, как они работают. Но на первом этапе я бы хотел, чтобы текущий поисковый робот работал, и для этого, боюсь, я неправильно понял, как встроить ItemLoaders / join в мой код - мои попытки привели только к ошибкам - как уже было сказано, я новичок в этом, честно говоря, я работаю с python и уже неделю. Текущий результат для моего элемента siteconten выглядит так: '\ n' 'texttextext' '\ n' '\ n' 'text2text2' et c. Я хочу преобразовать это в: texttexttext text2text2. Таким образом, возникает вопрос: удалят ли загрузчики элементов переводы строк и как, в частности, заставить их работать с моим текущим кодом?

...