Scrapy 在文件中保存 200 个带有空项目的状态 url

Scrapy saving 200 status urls with empty items in a file

我在 scrapy 日志中注意到一些 url 返回 200 状态但不包含任何项目。这似乎是网站的稳定性,因为重新抓取这些网址 1-2 次会再次产生项目。我想将这些网址保存在单独的文件中以供重新抓取。

我试图在蜘蛛程序中创建字典 class 来存储这些 url,但是没有简单的方法可以将字典保存到文件中。

我尝试的另一种方法是为 url 创建第二个项目 class 并使用项目管道。它仍然输出空文件。我不太先进,无法编写自己的管道。这是我的代码。

import scrapy
class MyItem(scrapy.Item):
   productCode = scrapy.Field()
   productName = scrapy.Field()
   ...

class UrlItem(scrapy.Item):
   eurl = scrapy.Field()

解析

class MySpider(scrapy.Spider):
    custom_settings = {
        'FEEDS':{
            '%(filename)s.csv':{'format':'csv', 'encoding':'utf-8',},
                },
        'FEED_EXPORTERS': {'csv': 'scrapy.exporters.CsvItemExporter',},}

    def parsePage(self, response):
        products = response.xpath(...)
        if len(products) == 0:
            url = UrlItem()
            url['eurl'] = response.url
            yield url
        else:
            item = MyItem()
            item['...'] = ...
            ...
            yield item

流水线

from .items import MyItem, UrlItem
import csv
class UrlPipeline:
    def open_spider(self, spider):
        self.file = open('%s.csv' % "noProductsUrls", 'w')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, url, spider):
        if isinstance(url, UrlItem):
            csvWriter = csv.writer(self.file)
            csvWriter.writerow(ItemAdapter(url)) 

from itemadapter import ItemAdapter
from scrapy.exceptions import DropItem
class MyPipeline:
    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
    
        if isinstance(item, MyItem):
            adapter = ItemAdapter(item)
            if adapter['productCode'] in self.ids_seen:
                raise DropItem(f"Duplicate item found: {item!r}")
            else:
                self.ids_seen.add(adapter['productCode'])
                return item

设置文件

'project.pipelines.MyPipeline': 300,
'project.pipelines.UrlPipeline': 300,

问题是我已经在 spider class 中设置了导出器,它保存了一个 csv。在管道中,我只想再添加一个 csv。两者有冲突吗?或者最好在管道中构建两个 csv 文件?

更新: 我选择了下面的@marcos 解决方案,它更胜一筹。

有一种在蜘蛛class中保存csv的方法是基于this post

def __init__(self):
    self.outfile = open("urls.csv", "w", newline = "")
    self.writer = csv.writer(self.outfile)

def closed(self,reason):
    self.outfile.close()  

在 def parse 中添加以下内容

if len(products) == 0:
    self.writer.writerow([response.url])

我建议您只要在页面上找不到产品时就发出重试请求,除非您有非常具体的理由来存储这些 URL。

代码如下:

class MySpider(scrapy.Spider):
    custom_settings = {
        'FEEDS':{
            '%(filename)s.csv':{'format':'csv', 'encoding':'utf-8',},
                },
        'FEED_EXPORTERS': {'csv': 'scrapy.exporters.CsvItemExporter',},}

    def parsePage(self, response):
        products = response.xpath(...)
        if not len(products):
            yield self._retry_request(response)
            return

        item = MyItem()
        item['...'] = ...
        ...
        yield item

    def _retry_request(self, response, max_retries=5):
        retries = response.meta.get('retry_time', 0)
        if retries < max_retries:
            return response.request.replace(
                meta={**response.meta, 'retry_time': retries + 1},
                dont_filter=True,
            )
        else:
            self.logger.warning(f'Max retries reached for {response.url}')