маршрутизация результатов от yield до файла - PullRequest
0 голосов
/ 21 июня 2019

У меня есть следующий скрипт Python, использующий Scrapy:

import scrapy


class ChemSpider(scrapy.Spider):
    name = "site"

    def start_requests(self):
        urls = [
            'https://www.site.com.au'
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        category_links = response.css('li').xpath('a/@href').getall()
        category_links_filtered = [x for x in category_links if 'shop-online' in x] # remove non category links
        category_links_filtered = list(dict.fromkeys(category_links_filtered)) # remove duplicates 

        for category_link in category_links_filtered:
            if "medicines" in category_link:
                next_page = response.urljoin(category_link) + '?size=10'
                self.log(next_page)
                yield scrapy.Request(next_page, callback=self.parse_subcategories)

    def parse_subcategories(self, response):
        for product in response.css('div.Product'):
            yield {
                'category_link': response.url,
                'product_name': product.css('img::attr(alt)').get(),
                'product_price': product.css('span.Price::text').get().replace('\n','')
            }

Мое решение будет запускать несколько экземпляров этого скрипта, каждый из которых будет собирать различное подмножество информации из разных «категорий».Я знаю, что вы можете запустить scrapy из командной строки для вывода в файл json, но я хочу сделать вывод в файл из функции, поэтому каждый экземпляр записывает в свой файл.Будучи новичком в Python, я не уверен, куда идти со своим сценарием.Мне нужно получить вывод yield в файл во время выполнения скрипта.Как мне этого добиться?Будут очищены сотни строк, и я недостаточно знаком с тем, как работает yield, чтобы понять, как «вернуть» из него набор данных (или список), которые затем можно записать в файл.

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Сначала позвольте мне предложить вам некоторые изменения в вашем коде. Если вы хотите удалить дубликаты, вы можете использовать set, например:

category_links_filtered = (x for x in category_links if 'shop-online' in x) # remove non category links
category_links_filtered = set(category_links_filtered) # remove duplicates

обратите внимание, что я также изменяю [ на (, чтобы сделать генератор вместо списка и сохранить немного памяти. Узнайте больше о генераторах : https://www.python -course.eu / python3_generators.php

ОК, тогда решение вашей проблемы - это использование Item Pipeline (https://docs.scrapy.org/en/latest/topics/item-pipeline.html),), что делает некоторые действия с каждым элементом, полученным из вашей функции parse_subcategories. Что вы делаете, так это добавляете класс в вашем файле pipelines.py и включите этот конвейер в settings.py. Это:

  • В settings.py:

    ITEM_PIPELINES = {
        'YOURBOTNAME.pipelines.CategoriesPipeline': 300, #the number here is the priority of the pipeline, dont worry and just leave it
    }
    
  • В pipelines.py:

    import json
    from urlparse import urlparse #this is library to parse urls
    
    class CategoriesPipeline(object):
        #This class dynamically saves the data depending on the category name obtained in the url or by an atrtribute
        def open_spider(self, spider):
            if hasattr(spider, 'filename'):
                #the filename is an attribute set by -a filename=somefilename
                filename = spider.filename
            else:
                #you could also set the name dynamically from the start url like this, if you set -a start_url=https://www.site.com.au/category-name
                try:
                    filename = urlparse(spider.start_url).path[1:] #this returns 'category-name' and replace spaces with _
                except AttributeError:
                    spider.crawler.engine.close_spider(self, reason='no start url') #this should not happen
            self.file = open(filename+'.jl', 'w')
    
        def close_spider(self, spider):
            self.file.close()
    
        def process_item(self, item, spider):
            line = json.dumps(dict(item)) + "\n"
            self.file.write(line)
            return item
    
  • В spiders/YOURBOTNAME.py изменить это:

    class ChemSpider(scrapy.Spider):
        name = "site"
        if !hasattr(self, 'start_url'):
            spider.crawler.engine.close_spider(self, reason='no start url') #we need a start url
        start_urls = [ self.start_url ] #see why this works on https://docs.scrapy.org/en/latest/intro/tutorial.html#a-shortcut-for-creating-requests
    
        def parse(self, response):#...
    

и затем вы начинаете сканирование с помощью этой команды: scrapy crawl site -a start_url=https://www.site.com.au/category-name, и вы можете добавить -a filename=somename

.
0 голосов
/ 21 июня 2019

Вы хотите добавить файл. Но, поскольку файл записывает операцию ввода-вывода, вам нужно заблокировать файл от записи другими процессами во время записи процесса.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...