如何更改ItemLoader的选择器
我正在尝试使用 ItemLoader 从多个页面提取数据来填充项目。但是现在我发现,初始化 ItemLoader 时使用的选择器是不能更改的。而文档中提到选择器属性是这样的:
选择器
这是一个用来提取数据的选择器对象。它可以是构造函数中给定的选择器,也可以是从构造函数中给定的响应创建的,使用的是默认的选择器类。这个属性是只读的,不能修改。
以下是示例代码:
def parse(self, response):
sel = Selector(response)
videos = sel.xpath('//div[@class="video"]')
for video in videos:
loader = ItemLoader(VideoItem(), videos)
loader.add_xpath('original_title', './/u/text()')
loader.add_xpath('original_id', './/a[@class="hRotator"]/@href', re=r'movies/(\d+)/.+\.html')
try:
url = video.xpath('.//a[@class="hRotator"]/@href').extract()[0]
request = Request(url,
callback=self.parse_video_page)
except IndexError:
pass
request.meta['loader'] = loader
yield request
pages = sel.xpath('//div[@class="pager"]//a/@href').extract()
for page in pages:
url = urlparse.urljoin('http://www.mysite.com/', page)
request = Request(url, callback=self.parse)
yield request
def parse_video_page(self, response):
loader = response.meta['loader']
sel = Selector(response)
loader.add_xpath('original_description', '//*[@id="videoInfo"]//td[@class="desc"]/h2/text()')
loader.add_xpath('duration', '//*[@id="video-info"]/div[2]/text()')
loader.add_xpath('tags', '//*[@id="tags"]//a/text()')
item = loader.load_item()
return item
目前为止,我无法从第二页抓取信息。
1 个回答
8
直接回答你的问题:要更改ItemLoader的选择器,你可以把新的选择器对象设置到loader.selector这个属性上。
def parse_video_page(self, response):
loader = response.meta['loader']
sel = Selector(response)
loader.selector = sel
loader.add_xpath(
'original_description',
'//*[@id="videoInfo"]//td[@class="desc"]/h2/text()'
)
# ...
不过,这种使用loader对象的方式看起来有点意外,因此可能不被支持。库的更新可能会导致这个代码出问题或者出现意想不到的错误。而且,把loader传递给请求的元数据也是不太好的做法,因为loader对象会引用响应对象,这在某些情况下可能会导致内存问题。
更正确的收集项目字段的方法是通过几个回调来实现,具体做法如下(注意注释):
def parse(self, response):
sel = Selector(response)
videos = sel.xpath('//div[@class="video"]')
for video in videos:
try:
url = video.xpath('.//a[@class="hRotator"]/@href').extract()[0]
except IndexError:
continue
loader = ItemLoader(VideoItem(), videos)
loader.add_xpath('original_title', './/u/text()')
loader.add_xpath(
'original_id',
'.//a[@class="hRotator"]/@href',
re=r'movies/(\d+)/.+\.html'
)
item = loader.load_item()
yield Request(
urlparse.urljoin(response.url, url),
callback=self.parse_video_page,
# Note: item passed to the meta dict, not loader itself
meta={'item': item}
)
pages = sel.xpath('//div[@class="pager"]//a/@href').extract()
for page in pages:
url = urlparse.urljoin('http://www.mysite.com/', page)
yield Request(url, callback=self.parse)
def parse_video_page(self, response):
item = response.meta['item']
# Note: new loader object created,
# item from response.meta is passed to the constructor
loader = ItemLoader(item, response=response)
loader.add_xpath(
'original_description',
'//*[@id="videoInfo"]//td[@class="desc"]/h2/text()'
)
loader.add_xpath(
'duration',
'//*[@id="video-info"]/div[2]/text()'
)
loader.add_xpath('tags', '//*[@id="tags"]//a/text()')
return loader.load_item()