Верно, что в документации Scrapy четко не указано, где разместить экспонента товаров. Чтобы использовать элемент Экспортер, выполните следующие действия.
pipeline.py
в каталоге проекта. Это может быть предопределенный Item Exporter (например, XmlItemExporter
) или определенный пользователем (например, FanItemExporter
, определенный в вопросе) pipeline.py
. Создайте экземпляр импортированного Item Exporter в этом классе. Подробности будут объяснены в более поздней части ответа. settings.py
. Ниже приводится подробное объяснение каждого шага , Решение вопроса включено в каждый шаг.
scrapy.exporters
. Пример: from scrapy.exporters import XmlItemExporter
exporters.py
. Поместите этот файл в папку проекта (где settings.py
, items.py
находятся). При создании нового подкласса всегда рекомендуется импортировать BaseItemExporter
. Было бы целесообразно, если мы намерены полностью изменить функциональность. Однако в этом вопросе большая часть функциональности близка к JsonLinesItemExporter
. Следовательно, я прикрепляю две версии одного и того же ItemExporter. Одна версия расширяет класс BaseItemExporter
, а другая расширяет JsonLinesItemExporter
класс
Версия 1 : Расширение BaseItemExporter
Поскольку BaseItemExporter
является родительским класс start_exporting()
, finish_exporting()
, export_item()
должен быть переопределен в соответствии с нашими потребностями.
from scrapy.exporters import BaseItemExporter
from scrapy.utils.serialize import ScrapyJSONEncoder
from scrapy.utils.python import to_bytes
class FanItemExporter(BaseItemExporter):
def __init__(self, file, **kwargs):
self._configure(kwargs, dont_fail=True)
self.file = file
self.encoder = ScrapyJSONEncoder(**kwargs)
self.first_item = True
def start_exporting(self):
self.file.write(b'{\'product\': [')
def finish_exporting(self):
self.file.write(b'\n]}')
def export_item(self, item):
if self.first_item:
self.first_item = False
else:
self.file.write(b',\n')
itemdict = dict(self._get_serialized_fields(item))
self.file.write(to_bytes(self.encoder.encode(itemdict)))
Версия 2 : Расширение JsonLinesItemExporter
JsonLinesItemExporter
обеспечивает точно такую же реализацию метода export_item()
. Поэтому переопределяются только методы start_exporting()
и finish_exporting()
.
Реализация JsonLinesItemExporter
показана в папке python_dir\pkgs\scrapy-1.1.0-py35_0\Lib\site-packages\scrapy\exporters.py
from scrapy.exporters import JsonItemExporter
class FanItemExporter(JsonItemExporter):
def __init__(self, file, **kwargs):
# To initialize the object we use JsonItemExporter's constructor
super().__init__(file)
def start_exporting(self):
self.file.write(b'{\'product\': [')
def finish_exporting(self):
self.file.write(b'\n]}')
Примечание: при записи данных в файл, важно отметить, что стандартные классы Item Exporter ожидают двоичные файлы. Следовательно, мы должны открыть файл с двоичным режимом (b
). По той же причине метод write()
в обеих версиях записывает bytes
в файл.
Создание класса Pipeline Item.
from project_name.exporters import FanItemExporter
class FanExportPipeline(object):
def __init__(self, file_name):
# Storing output filename
self.file_name = file_name
# Creating a file handle and setting it to None
self.file_handle = None
@classmethod
def from_crawler(cls, crawler):
# getting the value of FILE_NAME field from settings.py
output_file_name = crawler.settings.get('FILE_NAME')
# cls() calls FanExportPipeline's constructor
# Returning a FanExportPipeline object
return cls(output_file_name)
def open_spider(self, spider):
print('Custom export opened')
# Opening file in binary-write mode
file = open(self.file_name, 'wb')
self.file_handle = file
# Creating a FanItemExporter object and initiating export
self.exporter = FanItemExporter(file)
self.exporter.start_exporting()
def close_spider(self, spider):
print('Custom Exporter closed')
# Ending the export to file from FanItemExport object
self.exporter.finish_exporting()
# Closing the opened output file
self.file_handle.close()
def process_item(self, item, spider):
# passing the item to FanItemExporter object for expoting to file
self.exporter.export_item(item)
return item
Теперь, когда мы определили Конвейер экспорта товаров, нам необходимо зарегистрировать конвейер в файле settings.py
. Мы также используем поле FILE_NAME
из файла settings.py
. Это поле содержит имя файла выходного файла.
Добавьте следующие строки в файл settings.py
.
FILE_NAME = 'path/outputfile.ext'
ITEM_PIPELINES = {
'project_name.pipelines.FanExportPipeline' : 600,
}
Если ITEM_PIPELINES
уже раскоментирован, добавьте следующую строку в словарь ITEM_PIPELINES
.
'project_name.pipelines.FanExportPipeline' : 600,
Таким образом, мы можем создавать настраиваемые конвейеры экспорта товаров и использовать их в нашем проекте.
Учитывая теги, я предполагаю, что Вы имеете в виду в.NET - и я приму решение говорить о C#, поскольку это - то, о чем я знаю.
foreach
оператор (обычно) использует IEnumerable
и IEnumerator
или их универсальные кузены. Оператор формы:
foreach (Foo element in source)
{
// Body
}
, где source
реализации IEnumerable<Foo>
примерно [1 116] эквивалентные:
using (IEnumerator<Foo> iterator = source.GetEnumerator())
{
Foo element;
while (iterator.MoveNext())
{
element = iterator.Current;
// Body
}
}
Примечание, что эти IEnumerator<Foo>
расположен в конце, однако выходы оператора. Это важно для блоков итератора.
Для реализации IEnumerable<T>
или IEnumerator<T>
самостоятельно самый легкий путь состоит в том, чтобы использовать блок итератора. Вместо того, чтобы писать все подробности здесь, вероятно, лучше просто отослать Вас к [1 113] глава 6 C# подробно , который является бесплатной загрузкой. Вся глава 6 находится на итераторах. У меня есть еще несколько статей о моем C# подробно сайт, также:
Как быстрый пример, хотя:
public IEnumerable<int> EvenNumbers0To10()
{
for (int i=0; i <= 10; i += 2)
{
yield return i;
}
}
// Later
foreach (int x in EvenNumbers0To10())
{
Console.WriteLine(x); // 0, 2, 4, 6, 8, 10
}
Для реализации IEnumerable<T>
для типа можно сделать что-то как:
public class Foo : IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
yield return "x";
yield return "y";
}
// Explicit interface implementation for nongeneric interface
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator(); // Just return the generic version
}
}
(Я принимаю C# здесь)
, Если у Вас есть список пользовательских объектов, можно просто использовать foreach таким же образом, как Вы делаете с любым другим объектом:
List<MyObject> myObjects = // something
foreach(MyObject myObject in myObjects)
{
// Do something nifty here
}
, Если Вы хотите создать свой собственный контейнер, можно использовать ключевое слово урожая (от.Net 2.0, и вверх я верю) вместе с интерфейсом IEnumerable.
class MyContainer : IEnumerable<int>
{
private int max = 0;
public MyContainer(int max)
{
this.max = max;
}
public IEnumerator<int> GetEnumerator()
{
for(int i = 0; i < max; ++i)
yield return i;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
И затем используют его с foreach:
MyContainer myContainer = new MyContainer(10);
foreach(int i in myContainer)
Console.WriteLine(i);