【问题标题】:a format breaker on the website stops my Scrapy spider网站上的格式破坏者阻止了我的 Scrapy 蜘蛛
【发布时间】:2020-06-25 16:46:57
【问题描述】:

我的 Scrapy Spider 在遇到网站格式异常值时停止工作。这是出错的网站元素(突出显示的部分):

正确的有一个“a”属性。它看起来像下面((突出显示的部分)):

这是我的蜘蛛:

import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider(scrapy.Spider):
  name = "name"
  allowed_domains = ["website domain"]
  start_urls = ['Mywebsite link']

  def parse(self, response):
    self.log('I just visited: ' + response.url)

    for i in response.css('tr.odd'):
        item = {
            'company_name': i.css('td > a::text')[0].extract(),
            'ABN': i.css('td > a::text')[1].extract(),
            'status': i.css('td::text')[2].extract(),
            'size': i.css('td::text')[3].extract(),
            'suburb/town': i.css('td::text')[4].extract(),
            'state': i.css('td::text')[5].extract(),
        }
        yield item

    for i in response.css('tr.even'):
        item = {
            'company_name': i.css('td > a::text')[0].extract(),
            **'ABN': i.css('td > a::text')[1].extract()**,     # this part stops working
            'status': i.css('td::text')[2].extract(),
            'size': i.css('td::text')[3].extract(),
            'suburb/town': i.css('td::text')[4].extract(),
            'state': i.css('td::text')[5].extract(),
        }
        yield item

    # follow pagination link
    next_page_url = response.css('li.next > a::attr(href)').extract_first()
    if next_page_url:
        next_page_url = response.urljoin(next_page_url)
        yield scrapy.Request(url=next_page_url, callback=self.parse)

在网站上是这样的: 该行中的“信息不可用”部分阻止了我的蜘蛛。请告诉我我该怎么做。谢谢!

【问题讨论】:

  • 您的代码需要 td 中的 6 个链接。您应该只有 1 的输入 HTML(而且它不是公司名称)。考虑从头开始重写行提取。
  • 谢谢你的回复,但我不明白你想说什么。
  • 您是否编写了您现在尝试修复的代码?鉴于您的屏幕截图显示的输入 HTML,您了解 i.css('td > a::text')[n].extract() 的作用吗? (ps:不鼓励截图代码)
  • 网址:acnc.gov.au/…
  • 顶部代码是实际的网站代码,我的印象是我的循环会抓取 td 类的文本并给我合法名称和 ABN 等信息

标签: python web-scraping scrapy


【解决方案1】:

让我们一次只关注一个问题:

不要直接输出值,而是使用临时变量。

    for i in response.css('tr.even'):
        abn = i.css('td > a::text')[1].extract()
        if not abn:
            abn = 'Unavailable' # Alternatively, you can use a fallback selector

        item = {
            'company_name': i.css('td > a::text')[0].extract(),
            'ABN': abn,
            #...
        }
        yield item

您可能还想检查extract() 和extract_first() 的使用情况。 documentation 实际上建议使用较新的 get() 和 getall(),因为这样更容易混淆。

现在使用 .get() 和 .getall() 方法编写 Scrapy 使用文档。 我们觉得这些新方法会导致更简洁易读 代码。

【讨论】:

  • 但是,您正在重现原始代码的主要问题,即滥用[n]。
  • 所以我应该尝试使用 tr 类作为 n 然后提取信息而不是单独浏览每个信息。
  • @Upendra 感谢您的回复,但是 if not 条件仍然会导致错误。
【解决方案2】:

您实现的主要问题是您的程序可以在不存在的元素上调用extract 方法。

for row in response.css('table.views-table tbody tr'):
    i = {}
    # field specific selectors
    i['company_name'], i['ABN'], i['status'], i['size'], i['suburb/town'], i['state'] = row.css('td')
    for key in i.keys():
        i[key] = i[key].css("*::text").get(default="") 
    yield i

我使用列表解包来填充项目字段 (partially based on this earlier answer)
因为表格的每一行都有严格的 6 列
根据docs - 方法get返回第一个匹配的结果
这意味着使用此方法您不需要在表格单元格选择器中显式使用a标签。

【讨论】:

  • 感谢您的回复,您的代码向我展示了一种使用 Scrapy 的简洁方法。但是,结果缺少 company_name 和 ABN。
【解决方案3】:

我通过重写代码解决了这个问题。感谢所有的答案贡献者。特别感谢@Gallaecio。这是我的解决方案:

import scrapy
from scrapy.crawler import CrawlerProcess

class AcncSpider(scrapy.Spider):
  name = "spider_name"
  allowed_domains = ["url"]
  start_urls = ['url']

  def parse(self, response):
    self.log('I just visited: ' + response.url)

    for i in response.css('div.view-content > div > table > tbody > tr.odd'):

        item = {
            'company_name': i.css('td.views-field.views-field-acnc-search-api-title-sort > a::text').extract(),
             
            'status': i.css('td.views-field.views-field-acnc-search-api-status-normalised::text').extract(),
            
            'size': i.css('td.views-field.views-field-field-charity-size.views-align-center::text').extract(),
             
            'suburb/town': i.css('td.views-field.views-field-acnc-search-api-address-locality::text').extract(),
            
            'state': i.css('td.views-field.views-field-acnc-search-api-address-administrative-area::text').extract(),
             
            'ABN': i.css('td.views-field.views-field-acnc-search-api-abn-sort > a::text').extract(),

            'URL': response.urljoin(i.css('td.views-field.views-field-acnc-search-api-title-sort > a::attr(href)').extract_first()),
                   
        }
        yield item
    
    for i in response.css('div.view-content > div > table > tbody > tr.even'):

        item = { 
            'company_name': i.css('td.views-field.views-field-acnc-search-api-title-sort > a::text').extract(),
             
            'status': i.css('td.views-field.views-field-acnc-search-api-status-normalised::text').extract(),
            
            'size': i.css('td.views-field.views-field-field-charity-size.views-align-center::text').extract(),
             
            'suburb/town': i.css('td.views-field.views-field-acnc-search-api-address-locality::text').extract(),
            
            'state': i.css('td.views-field.views-field-acnc-search-api-address-administrative-area::text').extract(),
             
            'ABN': i.css('td.views-field.views-field-acnc-search-api-abn-sort > a::text').extract(),

            'URL': response.urljoin(i.css('td.views-field.views-field-acnc-search-api-title-sort > a::attr(href)').extract_first())
        }
        yield item
    
    # follow pagination link
    next_page_url = response.css('li.next > a::attr(href)').extract_first()
    if next_page_url:
        next_page_url = response.urljoin(next_page_url)
        yield scrapy.Request(url=next_page_url, callback=self.parse)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-26
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多