Scrapy 爬行列表绕过 for 循环或 str 格式
Scrapy crawl list bypass with for loop or str formatting
我正在寻找一种解决方案,让我的代码只抓取每个项目一次。
自从我添加了最后一个循环以来,我收到了每件物品三遍。
我怎样才能只执行一次我的最后一个循环,或者是否有可能确定所有的双倍爬行?
import scrapy
from ..items import TopartItem
class LinkSpider(scrapy.Spider):
name = "link"
allow_domains = ['topart-online.com']
start_urls = ['https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg=1']
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status', 'Attribute', 'Values'] }
def parse(self, response):
card = response.xpath('//a[@class="clearfix productlink"]')
for a in card:
items = TopartItem()
link = a.xpath('@href')
items['title'] = a.xpath('.//div[@class="sn_p01_desc h4 col-12 pl-0 pl-sm-3 pull-left"]/text()').get().strip()
items['links'] = link.get()
items['ItemSKU'] = a.xpath('.//span[@class="sn_p01_pno"]/text()').get().strip()
items['Delivery_Status'] = a.xpath('.//div[@class="availabilitydeliverytime"]/text()').get().strip().replace('/','')
yield response.follow(url=link.get(),callback=self.parse_item, meta={'items':items})
last_pagination_link = response.xpath('//a[@class="page-link"]/@href')[-1].get()
last_page_number = int(last_pagination_link.split('=')[-1])
for i in range(2,last_page_number+1):
url = f'https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg={i}'
yield response.follow(url=url, callback=self.parse)
def parse_item(self,response):
table = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]')
for a in table:
items = TopartItem()
items = response.meta['items']
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['Attribute'] = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = response.xpath('//div[@class="col-6"]/text()').getall()
yield items
我只希望有 51 个元素,但我收到了 153 个。
你每件物品都得到 3 个的原因是你在 table 周围做一个 for 循环,我认为这不是必需的。如果数据没有意义,尽管很高兴是错误的。
加法
顶部代码的一个小补充。我之所以放入这个是因为要指定在创建 CSV 文件时应如何显示列。通常对于项目,您不会按照您想要的方式获得列的顺序。在这里,我们通过使 scrapy 包含这些设置来指定它们。我们必须将 attribute
和 value
添加到该列表以在您创建 CSV 文档时包含它。
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status','Attribute','Values'] }
代码更正
def parse_item(self,response):
items = response.meta['items']
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['Attribute'] = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = response.xpath('//div[@class="col-6"]/text()').getall()
yield items
解释
- 无需在
parse_item
中实例化 TopArtItem(),因为它已在 parse
函数中实例化。
- 无需使用 for 循环,只需使用 response 获取详细信息。
提示
- 如果您确实需要围绕 table 或任何提供列表的 XPATH 选择器执行 for 循环,请记住您的 xpath 选择器应该是
a.xpath('.//div etc....)
而不是 response('//)
。这是因为您想使用 a
而不是 response
或 table
,并且您必须使用 .//
因为您希望搜索的相对 XPATH NOT //
整个文档。
相对路径我的意思是你想告诉 scrapy,你假设 table
中的 xpath 选择器是在 .//
XPATH 选择器中给定的,并且使用 .//XPATH_SELECTOR
,您是在告诉 scrapy 将 table XPATH 选择器添加到 .//
XPATH 选择器中的任何内容。这是一种不必使用非常大的字符串 XPATH 选择器的简洁方法。但是,如果您围绕已创建选择器列表的 XPATH 选择器执行 for 循环,则必须使用它。
例如
不是要包含的代码,而是作为 table XPATH 选择器给您列表时如何使用 for 循环的示例。
table = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]')
for a in table:
items = response.meta['items']
items['ItemEAN'] = a.xpath('.//div[@class="productean"]/text()').get().strip()
items['Attribute'] = a.xpath('.//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = a.xpath('.//div[@class="col-6"]/text()').getall()
yield items
我们使用了 a
而不是 table
或 response
并且我们特别使用了 .//
而不是 //
根据评论更新
所以对于下一个问题,它需要一些字符串和列表操作。
代码更改
为了让下面的代码正常工作,您需要更改 custom_settings
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status','Values'] }
您还需要在items.py
中删除
Attributes = scrapy.Field()
更新了 parse_items 代码
def parse_item(self,response):
items = response.meta['items']
attribute = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
values = response.xpath('//div[@class="col-6"]/text()').getall()
combined = []
for i,j in zip(attribute,values):
combined.append(i.strip().replace('.','').replace(':',': ') + j.strip().replace('\'',''))
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['values'] = ', '.join(combined)
yield items
解释
我们定义变量attributes
和values
。我们不将这些添加到项目字典中,因为我们想先进行一些操作。
组合变量很长,但很容易理解。
我们有两个列表,attributes
和 values
,我们想将两个列表中的每个项目组合在一起。第一项来自属性,第一项来自值。这可以通过 zip 函数来完成。
举一个抽象的例子来理解zip是干什么的
如果我们有一个名为 num = ['1','2','3']
和 letter = [a,b,c]
的列表。 zip(num,letter)
将创建 [('1',a),('2',b),('3',c)]
。 Zip 为每个相应的列表项创建元组并将它们放入列表中。
现在我们要将这个列表的所有项组合成一个字符串作为目标。
我们可以像这样循环zip(num,letter)
的每个列表项
combined = []
for i,j zip(num,letter):
combined.append(i + j)
这将创建 combined = ['1 + a','2 + b','3 + c']
然后我们使用 ''.join(combined)
,这是将列表转换为字符串以将所有这些组合成字符串的标准方法。
所以我们用这段代码来做这个,除了我使用 strip() 方法并为每个 i 或 j 替换一些字母只是为了整理它。
我正在寻找一种解决方案,让我的代码只抓取每个项目一次。 自从我添加了最后一个循环以来,我收到了每件物品三遍。 我怎样才能只执行一次我的最后一个循环,或者是否有可能确定所有的双倍爬行?
import scrapy
from ..items import TopartItem
class LinkSpider(scrapy.Spider):
name = "link"
allow_domains = ['topart-online.com']
start_urls = ['https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg=1']
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status', 'Attribute', 'Values'] }
def parse(self, response):
card = response.xpath('//a[@class="clearfix productlink"]')
for a in card:
items = TopartItem()
link = a.xpath('@href')
items['title'] = a.xpath('.//div[@class="sn_p01_desc h4 col-12 pl-0 pl-sm-3 pull-left"]/text()').get().strip()
items['links'] = link.get()
items['ItemSKU'] = a.xpath('.//span[@class="sn_p01_pno"]/text()').get().strip()
items['Delivery_Status'] = a.xpath('.//div[@class="availabilitydeliverytime"]/text()').get().strip().replace('/','')
yield response.follow(url=link.get(),callback=self.parse_item, meta={'items':items})
last_pagination_link = response.xpath('//a[@class="page-link"]/@href')[-1].get()
last_page_number = int(last_pagination_link.split('=')[-1])
for i in range(2,last_page_number+1):
url = f'https://www.topart-online.com/de/Blattzweige-Blatt-und-Bluetenzweige/l-KAT282?seg={i}'
yield response.follow(url=url, callback=self.parse)
def parse_item(self,response):
table = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]')
for a in table:
items = TopartItem()
items = response.meta['items']
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['Attribute'] = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = response.xpath('//div[@class="col-6"]/text()').getall()
yield items
我只希望有 51 个元素,但我收到了 153 个。
你每件物品都得到 3 个的原因是你在 table 周围做一个 for 循环,我认为这不是必需的。如果数据没有意义,尽管很高兴是错误的。
加法
顶部代码的一个小补充。我之所以放入这个是因为要指定在创建 CSV 文件时应如何显示列。通常对于项目,您不会按照您想要的方式获得列的顺序。在这里,我们通过使 scrapy 包含这些设置来指定它们。我们必须将 attribute
和 value
添加到该列表以在您创建 CSV 文档时包含它。
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status','Attribute','Values'] }
代码更正
def parse_item(self,response):
items = response.meta['items']
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['Attribute'] = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = response.xpath('//div[@class="col-6"]/text()').getall()
yield items
解释
- 无需在
parse_item
中实例化 TopArtItem(),因为它已在parse
函数中实例化。 - 无需使用 for 循环,只需使用 response 获取详细信息。
提示
- 如果您确实需要围绕 table 或任何提供列表的 XPATH 选择器执行 for 循环,请记住您的 xpath 选择器应该是
a.xpath('.//div etc....)
而不是response('//)
。这是因为您想使用a
而不是response
或table
,并且您必须使用.//
因为您希望搜索的相对 XPATH NOT//
整个文档。
相对路径我的意思是你想告诉 scrapy,你假设 table
中的 xpath 选择器是在 .//
XPATH 选择器中给定的,并且使用 .//XPATH_SELECTOR
,您是在告诉 scrapy 将 table XPATH 选择器添加到 .//
XPATH 选择器中的任何内容。这是一种不必使用非常大的字符串 XPATH 选择器的简洁方法。但是,如果您围绕已创建选择器列表的 XPATH 选择器执行 for 循环,则必须使用它。
例如
不是要包含的代码,而是作为 table XPATH 选择器给您列表时如何使用 for 循环的示例。
table = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]')
for a in table:
items = response.meta['items']
items['ItemEAN'] = a.xpath('.//div[@class="productean"]/text()').get().strip()
items['Attribute'] = a.xpath('.//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
items['Values'] = a.xpath('.//div[@class="col-6"]/text()').getall()
yield items
我们使用了 a
而不是 table
或 response
并且我们特别使用了 .//
而不是 //
根据评论更新
所以对于下一个问题,它需要一些字符串和列表操作。
代码更改
为了让下面的代码正常工作,您需要更改 custom_settings
custom_settings = {'FEED_EXPORT_FIELDS': ['title','links','ItemSKU','ItemEAN','Delivery_Status','Values'] }
您还需要在items.py
中删除Attributes = scrapy.Field()
更新了 parse_items 代码
def parse_item(self,response):
items = response.meta['items']
attribute = response.xpath('//div[@class="productcustomattrdesc word-break col-6"]/text()').getall()
values = response.xpath('//div[@class="col-6"]/text()').getall()
combined = []
for i,j in zip(attribute,values):
combined.append(i.strip().replace('.','').replace(':',': ') + j.strip().replace('\'',''))
items['ItemEAN'] = response.xpath('//div[@class="productean"]/text()').get().strip()
items['values'] = ', '.join(combined)
yield items
解释
我们定义变量attributes
和values
。我们不将这些添加到项目字典中,因为我们想先进行一些操作。
组合变量很长,但很容易理解。
我们有两个列表,attributes
和 values
,我们想将两个列表中的每个项目组合在一起。第一项来自属性,第一项来自值。这可以通过 zip 函数来完成。
举一个抽象的例子来理解zip是干什么的
如果我们有一个名为 num = ['1','2','3']
和 letter = [a,b,c]
的列表。 zip(num,letter)
将创建 [('1',a),('2',b),('3',c)]
。 Zip 为每个相应的列表项创建元组并将它们放入列表中。
现在我们要将这个列表的所有项组合成一个字符串作为目标。
我们可以像这样循环zip(num,letter)
的每个列表项
combined = []
for i,j zip(num,letter):
combined.append(i + j)
这将创建 combined = ['1 + a','2 + b','3 + c']
然后我们使用 ''.join(combined)
,这是将列表转换为字符串以将所有这些组合成字符串的标准方法。
所以我们用这段代码来做这个,除了我使用 strip() 方法并为每个 i 或 j 替换一些字母只是为了整理它。