【问题标题】:How to start a new request after the item_scraped scrapy signal is called?调用 item_scraped scrapy 信号后如何发起新请求?
【发布时间】:2015-10-15 13:21:47
【问题描述】:

我需要使用 Scrapy(http://example.com/itemview) 从网站上抓取每个项目的数据。我有一个 itemID 列表,我需要在 example.com 中以表格形式传递它。 每个项目都没有 url 更改。因此,对于我的蜘蛛中的每个请求,url 总是相同的。但内容会有所不同。

我不想使用 for 循环来处理每个请求。所以我按照下面提到的步骤。

  • 使用上述网址启动蜘蛛
  • 添加了 item_scraped 和 spider_close 信号
  • 通过了几个函数
  • 将抓取的数据传递到管道
  • 触发了 item_scraped 信号

之后它会自动调用 spider_close 信号。但我希望继续执行上述步骤,直到完成整个 itemID。

class ExampleSpider(scrapy.Spider):
  name = "example"
  allowed_domains = ["example.com"]
  itemIDs = [11111,22222,33333]
  current_item_num = 0

  def __init__(self, itemids=None, *args, **kwargs):
    super(ExampleSpider, self).__init__(*args, **kwargs)
    dispatcher.connect(self.item_scraped, signals.item_scraped)
    dispatcher.connect(self.spider_closed, signals.spider_closed)

  def spider_closed(self, spider):
     self.driver.quit()

  def start_requests(self):
     request = self.make_requests_from_url('http://example.com/itemview')
     yield request

 def parse(self,response):
     self.driver = webdriver.PhantomJS()
     self.driver.get(response.url)
     first_data = self.driver.find_element_by_xpath('//div[@id="itemview"]').text.strip()
     yield Request(response.url,meta={'first_data':first_data},callback=self.processDetails,dont_filter=True)

 def processDetails(self,response):
     itemID = self.itemIDs[self.current_item_num]
     ..form submission with the current itemID goes here...
     ...the content of the page is updated with the given itemID...
     yield Request(response.url,meta={'first_data':response.meta['first_data']},callback=self.processData,dont_filter=True)

def processData(self,response):
    ...some more scraping goes here...
   item = ExamplecrawlerItem()
   item['first_data'] = response.meta['first_data']
   yield item

def item_scraped(self,item,response,spider):
   self.current_item_num += 1
   #i need to call the processDetails function here for the next itemID 
   #and the process needs to contine till the itemID finishes
   self.parse(response)

我的管道:

class ExampleDBPipeline(object):
  def process_item(self, item, spider):
     MYCOLLECTION.insert(dict(item))
     return

【问题讨论】:

    标签: python-2.7 phantomjs scrapy-spider


    【解决方案1】:

    我希望我有一个优雅的解决方案。但相反,这是一种调用底层类的骇人听闻的方式。

                    self.crawler.engine.slot.scheduler.enqueue_request(scrapy.Request(url,self.yourCallBack))
    

    但是,您可以在生成项目后生成请求并将其回调到 self.processDetails。只需将其添加到您的 processData 函数中:

       yield item
       self.counter += 1
       yield scrapy.Request(response.url,callback=self.processDetails,dont_filter=True, meta = {"your":"Dictionary"}
    

    此外,PhantomJS 可以很好地让您的生活变得轻松,但它比常规连接要慢。如果可能,请找到对 json 数据的请求或任何使页面在没有 JS 的情况下无法解析的内容。为此,打开 chrome,右键单击,单击检查,转到网络选项卡,然后在表单中输入 ID,然后查看具有所需数据或下一个 url 的 JSON 的 XHR 或 JS 选项卡。大多数情况下,都会有一些通过添加ID制作的url,如果你能找到它,你可以将你的url连接起来直接调用它,而无需花费JS渲染的成本。有时它是随机的,或者不存在,但我已经取得了相当大的成功。然后,您还可以使用它同时产生许多请求,而不必担心 phantomJS 会尝试同时做两件事或必须初始化它的许多实例。您可以使用标签,但这很痛苦。

    另外,我会使用您的 ID 队列来确保线程安全。否则,您可能会在同一个 ID 上调用两次 processDetails,尽管在您的程序逻辑中,一切似乎都是线性的,这意味着您没有使用 Scrapy 的并发功能,您的程序会运行得更慢。要使用队列添加:

    import Queue
    #go inside class definition and add
    itemIDQueue = Queue.Queue()
    #within __init__ add
    [self.itemIDQueue.put(ID) for ID in self.itemID]
    #within processDetails replace  itemID = self.itemIDs[self.current_item_num] with
    itemID = self.itemIDQueue.get()
    

    然后就不需要增加计数器,你的程序是线程安全的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 2022-12-09
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 2016-11-21
      相关资源
      最近更新 更多