Scrapy中Xpath获取元素无法逐条输出Item问题求助
Scrapy实现单商品对应独立Item的问题修复
问题描述
从特定EC站点提取展商订单数据,已将订单结果页URL存入CSV并在start_requests中遍历请求。每个页面含30个商品信息,尝试用XPath定位后通过ItemLoader存入Item并yield,但当前输出的每个Item包含多条商品数据,期望每个商品对应一个独立Item。
原代码:
class AllSaledataSpider(CrawlSpider): name = 'all_salesdata_copy2' allowed_domains = ['www.buyma.com'] def start_requests(self): with open('/Users/morni/researchtool/AllshoppersURL.csv', 'r', encoding='utf-8-sig') as f: reader = csv.reader(f) for row in reader: for n in range(1, 300): url = str((row[2])[:-5]+'/sales_'+str(n)+'.html') yield scrapy.Request( url=url, callback=self.parse_firstpage_item, dont_filter=True ) def parse_firstpage_item(self, response): loader = ItemLoader(item = ResearchtoolItem(), response = response) Conversion_date = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[3]/text()').getall() product_name = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[1]/a/text()').getall() product_URL = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[1]/a/@href').getall() for i in range(30): loader.add_value("Conversion_date", Conversion_date[i]) loader.add_value("product_name", product_name[i]) loader.add_value("product_URL", product_URL[i]) yield loader.load_item()
当前输出:
{"product_name": ["product1", "product2"], "Conversion_date":["Conversion_date1", "Conversion_date2" ], "product_URL":["product_URL1", "product_URL2"]}
理想输出:
[{"product_name": "product1", "Conversion_date": "Conversion_date1", "product_URL": "product_URL1"},{"product_name": "product2", "Conversion_date": "Conversion_date2", "product_URL": "product_URL2"}]
问题原因
ItemLoader实例在for循环外初始化,每次循环调用add_value时,会往同一个Item对象里追加数据,而非创建新的Item。因此每次yield的Item都是累积了之前所有商品的信息。
修复方案
将ItemLoader的创建移至for循环内部,确保每次循环都生成新的loader实例,对应单个商品的Item:
class AllSaledataSpider(CrawlSpider): name = 'all_salesdata_copy2' allowed_domains = ['www.buyma.com'] def start_requests(self): with open('/Users/morni/researchtool/AllshoppersURL.csv', 'r', encoding='utf-8-sig') as f: reader = csv.reader(f) for row in reader: for n in range(1, 300): url = str((row[2])[:-5]+'/sales_'+str(n)+'.html') yield scrapy.Request( url=url, callback=self.parse_firstpage_item, dont_filter=True ) def parse_firstpage_item(self, response): Conversion_date = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[3]/text()').getall() product_name = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[1]/a/text()').getall() product_URL = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]/p[1]/a/@href').getall() for i in range(30): # 每次循环创建新的ItemLoader实例 loader = ItemLoader(item = ResearchtoolItem(), response = response) loader.add_value("Conversion_date", Conversion_date[i]) loader.add_value("product_name", product_name[i]) loader.add_value("product_URL", product_URL[i]) yield loader.load_item()
额外优化建议
- 不要硬编码
range(30),可以通过len(Conversion_date)动态获取商品数量,避免页面商品数不足30时出现索引错误:for i in range(len(Conversion_date)): - 直接遍历商品节点更健壮(避免XPath定位的列表长度不一致问题):
def parse_firstpage_item(self, response): # 先定位每个商品的根节点 product_nodes = response.xpath('//*[@id="buyeritemtable"]/div/ul/li[2]') for node in product_nodes: loader = ItemLoader(item = ResearchtoolItem(), selector=node) loader.add_xpath("Conversion_date", './p[3]/text()') loader.add_xpath("product_name", './p[1]/a/text()') loader.add_xpath("product_URL", './p[1]/a/@href') yield loader.load_item()
这种方式直接针对每个商品节点提取数据,无需手动处理索引,可靠性更高。
内容的提问来源于stack exchange,提问作者K_MM
相关产品推荐
相关产品推荐

