Scrapy 自定义管道输出文件是预期大小的一半
Scrapy custom pipeline outputting files half the size expected
我正在尝试为 Scrapy
项目创建自定义管道,将收集的项目输出到 CSV 文件。为了减小每个文件的大小,我想设置每个文件可以包含的最大行数。一旦达到当前文件中的行限制,就会创建一个新文件以继续输出项目。
幸运的是,我找到 a question where someone was looking to do the same thing. And there's an answer 那个显示示例实现的问题。
我实现了示例实现,但调整了访问 stats
的方式以与 Scrapy
的当前版本保持一致。
我当前的代码
from scrapy.exporters import CsvItemExporter
import datetime
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.stats = stats
self.stats.set_value('item_scraped_count', 0)
self.base_filename = "site_{}.csv"
self.next_split = self.split_limit = 100
self.create_exporter()
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.stats)
def create_exporter(self):
now = datetime.datetime.now()
datetime_stamp = now.strftime("%Y%m%d%H%M")
self.file = open(self.base_filename.format(datetime_stamp),'w+b')
self.exporter = CsvItemExporter(self.file)
self.exporter.start_exporting()
def process_item(self, item, spider):
if self.stats.get_value('item_scraped_count') >= self.next_split:
self.next_split += self.split_limit
self.exporter.finish_exporting()
self.file.close()
self.create_exporter()
self.exporter.export_item(item)
self.stats.inc_value('item_scraped_count')
return item
问题
管道确实会导致输出多个文件,但这些文件都只有 50 个项目,而不是预期的 100 个。
问题
我做错了什么导致文件大小只有预期的一半?
在process_item()
时我添加
print('>>> stat count:', self.stats.get_value('item_scraped_count'))
并删除
self.stats.inc_value('item_scraped_count')
然后我看到它仍然增加了这个变量。
这意味着其他代码已经计算了抓取的值,因此您不应该增加它。
如果我保留 inc_value()
,那么我会看到它对所有元素计数两次。
我不确定它是否只计算您添加到 CSV 中的元素,因此您可以使用分隔变量来计算它
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.count = 0
# ... code ...
def process_item(self, item, spider):
print('>>> count:', self.count)
if self.count >= self.next_split:
# ... code ...
# ... code ...
self.count += 1
return item
编辑:
管道需要此方法来关闭最后一个文件并保存此文件中的所有数据。
def close_spider(self, spider):
self.file.close()
最简单的工作示例。
我都放在一个文件里,不用创建项目也可以运行python script.py
。这样大家就可以轻松测试了。
因为我在每个文件中抓取了 10 个项目,所以它创建新文件的速度如此之快,以至于我不得不在文件名中添加微秒 (%f
) 以创建唯一的名称。
import scrapy
from scrapy.exporters import CsvItemExporter
import datetime
class MySpider(scrapy.Spider):
name = 'myspider'
# see page created for scraping: http://toscrape.com/
start_urls = ['http://books.toscrape.com/'] #'http://quotes.toscrape.com']
def parse(self, response):
print('url:', response.url)
# download images and convert to JPG (even if it is already JPG)
for url in response.css('img::attr(src)').extract():
url = response.urljoin(url)
yield {'image_urls': [url], 'session_path': 'hello_world'}
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.filename = "site_{}.csv"
self.split_limit = 10
self.count = 0
self.create_exporter()
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.stats)
def create_exporter(self):
now = datetime.datetime.now()
datetime_stamp = now.strftime("%Y.%m.%d-%H.%M.%S.%f") # %f for microseconds because sometimes it can create next file in less then 1 second and create the same name.
self.file = open(self.filename.format(datetime_stamp), 'w+b')
self.exporter = CsvItemExporter(self.file)
self.exporter.start_exporting()
def finish_exporter(self):
self.exporter.finish_exporting()
self.file.close()
def process_item(self, item, spider):
if self.count >= self.split_limit:
self.finish_exporter()
self.count = 0
self.create_exporter()
self.exporter.export_item(item)
self.count += 1
print('self.count:', self.count)
return item
def close_spider(self, spider):
self.finish_exporter()
# --- run without project and save in `output.csv` ---
from scrapy.crawler import CrawlerProcess
c = CrawlerProcess({
'USER_AGENT': 'Mozilla/5.0',
'ITEM_PIPELINES': {'__main__.PartitionedCsvPipeline': 1}, # used Pipeline create in current file (needs __main___)
})
c.crawl(MySpider)
c.start()
我正在尝试为 Scrapy
项目创建自定义管道,将收集的项目输出到 CSV 文件。为了减小每个文件的大小,我想设置每个文件可以包含的最大行数。一旦达到当前文件中的行限制,就会创建一个新文件以继续输出项目。
幸运的是,我找到 a question where someone was looking to do the same thing. And there's an answer 那个显示示例实现的问题。
我实现了示例实现,但调整了访问 stats
的方式以与 Scrapy
的当前版本保持一致。
我当前的代码
from scrapy.exporters import CsvItemExporter
import datetime
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.stats = stats
self.stats.set_value('item_scraped_count', 0)
self.base_filename = "site_{}.csv"
self.next_split = self.split_limit = 100
self.create_exporter()
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.stats)
def create_exporter(self):
now = datetime.datetime.now()
datetime_stamp = now.strftime("%Y%m%d%H%M")
self.file = open(self.base_filename.format(datetime_stamp),'w+b')
self.exporter = CsvItemExporter(self.file)
self.exporter.start_exporting()
def process_item(self, item, spider):
if self.stats.get_value('item_scraped_count') >= self.next_split:
self.next_split += self.split_limit
self.exporter.finish_exporting()
self.file.close()
self.create_exporter()
self.exporter.export_item(item)
self.stats.inc_value('item_scraped_count')
return item
问题
管道确实会导致输出多个文件,但这些文件都只有 50 个项目,而不是预期的 100 个。
问题
我做错了什么导致文件大小只有预期的一半?
在process_item()
时我添加
print('>>> stat count:', self.stats.get_value('item_scraped_count'))
并删除
self.stats.inc_value('item_scraped_count')
然后我看到它仍然增加了这个变量。
这意味着其他代码已经计算了抓取的值,因此您不应该增加它。
如果我保留 inc_value()
,那么我会看到它对所有元素计数两次。
我不确定它是否只计算您添加到 CSV 中的元素,因此您可以使用分隔变量来计算它
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.count = 0
# ... code ...
def process_item(self, item, spider):
print('>>> count:', self.count)
if self.count >= self.next_split:
# ... code ...
# ... code ...
self.count += 1
return item
编辑:
管道需要此方法来关闭最后一个文件并保存此文件中的所有数据。
def close_spider(self, spider):
self.file.close()
最简单的工作示例。
我都放在一个文件里,不用创建项目也可以运行python script.py
。这样大家就可以轻松测试了。
因为我在每个文件中抓取了 10 个项目,所以它创建新文件的速度如此之快,以至于我不得不在文件名中添加微秒 (%f
) 以创建唯一的名称。
import scrapy
from scrapy.exporters import CsvItemExporter
import datetime
class MySpider(scrapy.Spider):
name = 'myspider'
# see page created for scraping: http://toscrape.com/
start_urls = ['http://books.toscrape.com/'] #'http://quotes.toscrape.com']
def parse(self, response):
print('url:', response.url)
# download images and convert to JPG (even if it is already JPG)
for url in response.css('img::attr(src)').extract():
url = response.urljoin(url)
yield {'image_urls': [url], 'session_path': 'hello_world'}
class PartitionedCsvPipeline(object):
def __init__(self, stats):
self.filename = "site_{}.csv"
self.split_limit = 10
self.count = 0
self.create_exporter()
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.stats)
def create_exporter(self):
now = datetime.datetime.now()
datetime_stamp = now.strftime("%Y.%m.%d-%H.%M.%S.%f") # %f for microseconds because sometimes it can create next file in less then 1 second and create the same name.
self.file = open(self.filename.format(datetime_stamp), 'w+b')
self.exporter = CsvItemExporter(self.file)
self.exporter.start_exporting()
def finish_exporter(self):
self.exporter.finish_exporting()
self.file.close()
def process_item(self, item, spider):
if self.count >= self.split_limit:
self.finish_exporter()
self.count = 0
self.create_exporter()
self.exporter.export_item(item)
self.count += 1
print('self.count:', self.count)
return item
def close_spider(self, spider):
self.finish_exporter()
# --- run without project and save in `output.csv` ---
from scrapy.crawler import CrawlerProcess
c = CrawlerProcess({
'USER_AGENT': 'Mozilla/5.0',
'ITEM_PIPELINES': {'__main__.PartitionedCsvPipeline': 1}, # used Pipeline create in current file (needs __main___)
})
c.crawl(MySpider)
c.start()