【问题标题】:Yield both items and callback request in scrapy在 scrapy 中同时生成项目和回调请求
【发布时间】:2016-09-01 02:04:49
【问题描述】:

免责声明:我对 Python 和 Scrapy 都很陌生。

我正试图让我的蜘蛛从起始 url 收集 url,跟随这些收集的 url 和两者:

  1. 从下一页抓取特定项目(并最终退回)
  2. 从下一页收集更具体的网址并关注这些网址。

我希望能够继续这个产生项目和回调请求的过程,但我不太确定该怎么做。 目前我的代码只返回网址,没有项目。我显然做错了什么。任何反馈将不胜感激。

class VSSpider(scrapy.Spider):
    name = "vs5"
    allowed_domains = ["votesmart.org"]
    start_urls = [
                  "https://votesmart.org/officials/WA/L/washington-state-legislative#.V8M4p5MrKRv",
                  ]

    def parse(self, response):
        sel = Selector(response)
        #this gathers links to the individual legislator pages, it works
        for href in response.xpath('//h5/a/@href'): 
            url = response.urljoin(href.extract())
            yield scrapy.Request(url, callback=self.parse1)

    def parse1(self, response):
        sel = Selector(response)
        items = []
        #these xpaths are on the next page that the spider should follow, when it first visits an individual legislator page
        for sel in response.xpath('//*[@id="main"]/section/div/div/div'):
            item = LegislatorsItems()
            item['current_office'] = sel.xpath('//tr[1]/td/text()').extract()
            item['running_for'] = sel.xpath('//tr[2]/td/text()').extract()
            items.append(item)
        #this is the xpath to the biography of the legislator, which it should follow and scrape next
        for href in response.xpath('//*[@id="folder-bio"]/@href'):
            url = response.urljoin(href.extract())
            yield scrapy.Request(url, callback=self.parse2, meta={'items': items})

    def parse2(self, response):
        sel = Selector(response)
        items = response.meta['items']
        #this is an xpath on the biography page
        for sel in response.xpath('//*[@id="main"]/section/div[2]/div/div[3]/div/'):
            item = LegislatorsItems()
            item['tester'] = sel.xpath('//div[2]/div[2]/ul/li[3]').extract()
            items.append(item)
            return items

谢谢!

【问题讨论】:

  • 快速浏览您的代码后,我猜最后一行的return items 应该有不同的缩进级别。
  • 除了 starrify 提到的,parse2 是否可以访问?你能发布抓取日志吗?

标签: python callback scrapy


【解决方案1】:

您的问题有 2 个级别。

1. 禁用 JS 后,简历网址不可用。在浏览器中关闭 JS 并检查此页面: https://votesmart.org/candidate/126288/derek-stanford

您应该会看到带有空 href 的标签和隐藏在评论下的正确 url。

<a href="#" class="folder" id="folder-bio">
<!--<a href='/candidate/biography/126288/derek-stanford' itemprop="url" class='more'>
           See Full Biographical and Contact Information</a>-->

要提取bio url,您可以使用xpath 选择器“/comment()”获取此评论,然后使用regexp 提取url。

或者,如果 url 结构对所有页面都是通用的,则只需自己形成 url:将链接中的“/candidate/”替换为“/candidate/biography/”。

注意!如果您遇到意外问题,首先要采取的措施之一是禁用 JS,然后像 Scrapy 一样查看页面。测试所有选择器。


2.您对物品的使用非常复杂。如果“一个项目=一个人”,您应该只在“parse_person”中定义一个项目并将其传递给“parse_bio”。

查看更新后的代码。我在发现问题时重写了一些部分。备注:

  • 您不需要(在大多数情况下)创建“项目”列表并将项目附加到它。 Scrapy 自己管理项目。
  • “sel = Selector(response)”在你的代码中没有意义,你可以扔掉它。

此代码已使用 Scrapy 1.0 和 Python 3.5 进行测试,但早期版本也应该可以工作。

from scrapy import Spider, Request

class VSSpider(Spider):
    name = "vs5"
    allowed_domains = ["votesmart.org"]
    start_urls = ["https://votesmart.org/officials/WA/L/washington-state-legislative"]

    def parse(self, response):
        for href in response.css('h5 a::attr(href)').extract():
            person_url = response.urljoin(href)
            yield Request(person_url, callback=self.parse_person)

    def parse_person(self, response):  # former "parse1"
        # define item, one for both parse_person and bio function
        item = LegislatorsItems()

        # extract text from left menu table and populate to item
        desc_rows = response.css('.span-abbreviated td::text').extract()
        if desc_rows:
            item['current_office'] = desc_rows[0]
            item['running_for'] = desc_rows[1] if len(desc_rows) > 1 else None

        # create right bio url and pass item to it
        bio_url = response.url.replace('votesmart.org/candidate/', 
                                       'votesmart.org/candidate/biography/')
        return Request(bio_url, callback=self.parse_bio, meta={'item': item})

    def parse_bio(self, response):  # former "parse2"
        # get item from meta, add "tester" data and return
        item = response.meta['item']
        item['tester'] = response.css('.item.first').xpath('//li[3]').extract()
        print(item)   # for python 2: print item 
        return item

【讨论】:

  • 感谢关于 JS 的提示,这让事情变得容易多了。除了parse_bio 之外,您的代码中的所有内容对我来说都很好。它返回正确的 url(bios 的),但也只是 {'tester': []},而不是指定的 xpath,这让我认为这是 xpath 选择器的一个简单问题,但是当我在 Google Chrome 中检查它时控制台,它的工作原理。另外,您将其更改为css是否有特定原因?谢谢!
  • 没关系!我的 xpath 选择器肯定是错误的。
猜你喜欢
  • 2016-02-08
  • 1970-01-01
  • 2016-11-21
  • 2017-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
相关资源
最近更新 更多