Используя Scrapy, я борюсь с тем, как добиться определенного результата.Я пытаюсь очистить финансовые документы от Комиссии по ценным бумагам и биржам.Задача кратко изложена ниже.
- Создание URL-адресов запроса компании из Центрального ключа индекса (cik) [каталог] [1]
- Следуйте документу компании [страница индекса] [2] иизвлеките ссылки
document
. - Перейдите к документу [ссылки] [3] и извлеките ссылки
10-K.txt
( Описание: полный текстовый файл для отправки ). - Очистите
.txt
и сохраните документ в documents
.
Используя эту процедуру, формат, который я пытаюсь достичь, показан ниже.Если при очистке отдельной страницы выполнить вставку в несколько полей, как это, не было проблемой, но при очистке произвольного числа элементов на нескольких страницах возникают проблемы.
[{cik_number : company_index_1,
documents : [document_1,
...
document_n]},
{cik_number : company_index_2,
documents : [document_1,
...
document_n]}
]
В настоящее время я получаю следующий вывод (для удобства сокращен, но это демонстрирует проблему).Как мне добавить документы к одному элементу вместо создания нескольких элементов для одной компании?Я не совсем уверен, что делать с несколькими вставками в одно и то же поле.
[{'cik': ['1011290'],
'documents': [document_1},
...
{'cik': ['1011290'],
'documents': [document_n]}
]
Код, использованный для генерации этого вывода, приведен ниже. Только для целей тестирования, я просто добавляю значение response.url
к documents
, чтобы упростить вывод.
#spider.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.loader import ItemLoader
from document_scraper.items import CikItem
class MySpider(scrapy.Spider):
name = 'demo'
start_urls = ["https://www.sec.gov/divisions/corpfin/organization/cfia-123.htm"]
custom_settings = {
'DOWNLOAD_DELAY' : 0.25,
'FEED_FORMAT' : 'json',
'FEED_URI' : 'item.json'
}
def parse(self, response):
for sel in response.xpath('(//*[@id="cos"]//tr)[last()]'):
loader = ItemLoader(item = CikItem(), response = response)
loader.add_value('cik', sel.xpath("td[2]//text()").extract_first())
yield response.follow(
'https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={}&type=10-K' \
'&dateb=&owner=exclude&count=40'.format(sel.xpath("td[2]//text()").extract_first()),
callback = self.index,
meta = {"item" : loader.load_item()}
)
def index(self, response):
document_buttons = response.xpath('//*[@id="documentsbutton"]/@href')
for _url in document_buttons.extract():
yield response.follow(
_url,
callback=self.parse_page_two,
meta={"item": response.meta['item']}
)
def parse_page_two(self, response):
filing_txt = response.xpath('(//*[contains(@href, ".txt")])[last()]/@href')
for _url in filing_txt.extract():
yield response.follow(
_url,
callback=self.parse_page_three,
meta={"item": response.meta['item']}
)
def parse_page_three(self, response):
next_loader = ItemLoader(item = response.meta['item'], response = response)
next_loader.add_value('documents', response.url)
yield next_loader.load_item()
Файл items.py
выглядит следующим образом.
from scrapy.item import Item, Field
from scrapy.loader.processors import TakeFirst, Identity
class CikItem(Item):
cik = Field()
documents = Field()
Обновление
Мне удалось получить фрагмент кода, который достигает желаемого результата.Файл spider.py
выглядит следующим образом.
class MySpider(scrapy.Spider):
name = 'demo'
start_urls = ["https://www.sec.gov/divisions/corpfin/organization/cfia-123.htm"]
custom_settings = {
'DOWNLOAD_DELAY' : 0.25,
'FEED_FORMAT' : 'json',
'FEED_URI' : 'item.json'
}
def parse(self, response):
for sel in response.xpath('(//*[@id="cos"]//tr)[last()]'):
item = CikItem()
item['cik'] = sel.xpath("td[2]//text()").extract_first()
item['documents'] = []
yield response.follow(
'https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={}&type=10-K' \
'&dateb=&owner=exclude&count=40'.format(sel.xpath("td[2]//text()").extract_first()),
callback = self.index,
meta = {'item' : item}
)
def index(self, response):
document_buttons = response.xpath('//*[@id="documentsbutton"]/@href')
for _url in document_buttons.extract():
yield response.follow(
_url,
callback=self.parse_page_two,
meta={'item': response.meta['item']}
)
def parse_page_two(self, response):
filing_txt = response.xpath('(//*[contains(@href, ".txt")])[last()]/@href')
for _url in filing_txt.extract():
yield response.follow(
_url,
callback=self.parse_page_three,
meta={'item': response.meta['item']}
)
def parse_page_three(self, response):
item = response.meta['item']
item['documents'].append(response.url)
if len(self.crawler.engine.slot.inprogress) == 1:
return item
Мне было бы интересно узнать, как можно улучшить этот код и можно ли реализовать ItemLoader()
для выполнения этой задачи.