scrapy - 奇怪的csv输出和丢失的请求

时间:2015-01-14 16:03:34

标签: python scrapy scrapy-spider

如果您可以帮助我改进代码并解决以下两个问题,那将是很好的:

  • 忽略流程['DE101096','AT231']开始时的其中一个
  • 使用scrapy crawl bot -o result.csv进行爬网时,结果为csv,格式如下:

transactionID transactionDate acq_id tra_id

DE101096 2011-02-21 11:05:23.312
DE101096 2011-02-21 11:05:23.312 Anlagenkonto Oxyfuelanlage
DE101096 2011-02-21 11:05:23.312 Anlagenkonto Oxyfuelanlage N ationalkonto - Ausgabe

我显然希望只有一行包含transactionID,transactionDate,acq_id和tra_id。我知道这个问题显然取决于我的代码,它将transactionID,transactionDate传递给由于后续两个请求而重复的项目。但是,我无法找到更接近预期输出的解决方案。

如何解决上述问题以及如何让我的蜘蛛更加有效。我也尝试过基于规则的方法,但这根本没有用。

我为所有投入感到高兴!

import csv

from scrapy.contrib.spiders import CrawlSpider
from scrapy.selector import Selector
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.http import FormRequest, Request

from etsbot.items import TransactionItem
from etsbot.middlewares import RandomProxy

class EuetsbotdetSpider(CrawlSpider):
    name = 'euetsbotdet'
    allowed_domains = ['ec.europa.eu']
    start_urls = [
        'http://ec.europa.eu/environment/ets/transaction.do'
    ]

    def parse(self, response):
    #self.data = csv.DictReader(open('/home/...t/items.csv','r'))
    #self.tids = []
    #for self.row in self.data:
    #    self.tids.append(self.row['transactionID'])
    self.tids = ['DE101096','AT231']

    for self.id in self.tids:
        return FormRequest.from_response(
            response,
            formname='transactions_maxlength',
            formdata={'transactionID':self.id},
            clickdata={'name': 'search'},callback=self.parseLinks
            )

def parseLinks(self,response):
    lex = LinkExtractor(allow=('http://ec.europa.eu/environment/ets/singleTransaction.do',),unique=True)
    for l in lex.extract_links(response):
        yield Request(l.url,method='GET',callback=self.parseDetail,)                

def parseDetail(self,response):
    sel  = Selector(response)
    item = TransactionItem()
    item['transactionID']   = sel.xpath('//table/tr/td/input[@name="transactionID"]/@value').extract()
    item['transactionDate'] = sel.xpath('//table/tr/td/input[@name="transactionDate"]/@value').extract()

    lext  = LinkExtractor(unique=True,restrict_xpaths = ('//*[@id="tblTransactionBlocksInformation"]/tr/td[6]/a[@class="resultlink"]'),)        
    for l in lext.extract_links(response):
        yield Request(l.url,method='GET',meta={'item':item},callback=self.parseAccounttr)

    lexa  = LinkExtractor(unique=True,restrict_xpaths = ('//*[@id="tblTransactionBlocksInformation"]/tr/td[7]/a[@class="resultlink"]'),)        
    for l in lexa.extract_links(response):
        yield Request(l.url,method='GET',meta={'item':item},callback=self.parseAccountac)
        yield item         

def parseAccounttr(self,response):
    sel   = Selector(response)
    item  = response.meta['item']
    item['tra_id'] = sel.xpath('//*[@id="tblAccountInfoReadonly"]/tr/td/input[@name="identifierInReg"]/@value').extract()
    yield item  

def parseAccountac(self,response):
    sel = Selector(response)
    item = response.meta['item']
    item['acq_id'] = sel.xpath('//*[@id="tblAccountInfoReadonly"]/tr/td/input[@name="identifierInReg"]/@value').extract()
    yield item

修改:

借助paul trmbrth的好评,我改写了我的代码。我没有像上面的代码那样在两组中拆分下载,而是在一个流中完成所有操作。这意味着当我为每个transactionID / transactionDate运行抓取spider.py -o时,我有两行,第一行是"卖家" "买方"。显然,这些信息应该在一行中。我现在的想法是在后处理中自动纠正这一点,即通过transactionID / transactionDate将每个奇数项与后续偶数项合并(我希望这是明确的)。但是我怎么能这样做呢?

import csv

from scrapy.contrib.spiders import CrawlSpider
from scrapy.selector import Selector
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.http import FormRequest, Request

from etsbot.items import TransactionItem
#from etsbot.middlewares import RandomProxy
from etsbot.inline_requests import inline_requests


class EuetsbotdetSpider(CrawlSpider):
    name = 'euetsbotdet'
    allowed_domains = ['ec.europa.eu']
    start_urls = [
        'http://ec.europa.eu/environment/ets/transaction.do'
    ]


    def parse(self, response):
        self.tids = ['DE101096']
        for self.id in self.tids:
            yield FormRequest.from_response(
            response,
            formname='transactions_maxlength',
            formdata={'transactionID': self.id},
            clickdata={'name': 'search'},
            callback=self.parseDetail,
            )


    def parseDetail(self, response):
        lex = LinkExtractor(allow=    ('http://ec.europa.eu/environment/ets/singleTransaction.do',),unique=True)
        for l in lex.extract_links(response):
            yield Request(l.url, method='GET', callback = self.parseAccount)


    def parseAccount(self, response):
        sel  = Selector(response)
        self.transactionID = sel.xpath('//table/tr/td/input[@name="transactionID"]/@value').extract()
        self.transactionDate = sel.xpath('//table/tr/td/input[@name="transactionDate"]/@value').extract()

        lex  = LinkExtractor(unique=True,restrict_xpaths=('//*[@id="tblTransactionBlocksInformation"]/tr/td/a[@class="resultlink"]'),)
        for l in lex.extract_links(response):
            yield Request(l.url,method='GET',callback=self.parseAgents)

    def parseAgents(self,response):
        sel  = Selector(response)
        ag   = sel.xpath('//*[@id="tblAccountInfoReadonly"]/tr/td/input[@name="identifierInReg"]/@value').extract()
        ah   = sel.xpath('//*[@id="tblAccountInfoReadonly"]/tr/td/input[@name="accountHolder"]/@value').extract()
        item = TransactionItem() 
        item['transactionID'] = self.transactionID
        item['transactionDate'] = self.transactionDate
        item['identifierInReg'] = ag
        item['accountHolder'] = ah
        yield item

2 个答案:

答案 0 :(得分:1)

关于第1点,当你写:

for self.id in self.tids:
    return FormRequest.from_response(
        response,
        formname='transactions_maxlength',
        formdata={'transactionID':self.id},
        clickdata={'name': 'search'},callback=self.parseLinks
        )

循环在第一次迭代时停止,因为您使用return

将其更改为“yield循环”:

for self.id in self.tids:
    yield FormRequest.from_response(
        response,
        formname='transactions_maxlength',
        formdata={'transactionID':self.id},
        clickdata={'name': 'search'},callback=self.parseLinks
        )

关于第二点,正如@EricValente所说,如果每个交易ID需要1个CSV行,则每个交易ID只需要产生/返回1个项目。

您可以开始在parseDetail中构建项目,并在meta中传递此项目,就像您正在做的那样,但是您需要跟踪有多少请求未知,以便知道何时返回处理完每个回复时的项目。 这样做可能非常棘手。例如,您必须捕获请求失败。 您可以尝试使用scrapy-inline-requests,这非常方便。

另一种选择是在第一次刮擦后按事务ID进行后处理和分组,因此您可以手动构建CSV。

另一个选择是不对parseAccounttr()parseAccountac()执行这些额外请求:据我所知,在2个交易ID中,tr[6]中链接的文本值tr[7]中的parseDetail()与您在后续回调中获取的identifierInReg属性具有相同的值

答案 1 :(得分:0)

我没有测试过这段代码,但问题是你在scrape的不同部分放置了3次项目(这就是为什么它们每个只包含你要查找的字段的子集)。您最后只需要一个“收益项目”。我重写了代码,你可以在http://pastebin.com/dxsHZ7fZ找到它。