动态增加DataGridView行、批量获取ComboBox列值及索引越界排错
解决亚马逊爬取DataGridView的三个问题
问题1:第二页爬取时“索引超出范围”错误
这个错误大概率是因为你直接通过dataGridViewScrapingData.Rows[i]访问行,但第二次循环时,DataGridView里还没有足够的行。第一次循环后你没提前新增行就直接赋值,导致索引i对应的行根本不存在。另外也要检查cols变量是否超出了列的有效范围。
修复方案:
先提前创建足够的行,再执行赋值逻辑,同时加入元素等待避免页面未加载完成的问题:
// 先清空旧数据(按需选择) dataGridViewScrapingData.Rows.Clear(); // 提前创建对应数量的空行 for (int k = 0; k < dataGridViewASINs.Rows.Count -1; k++) { dataGridViewScrapingData.Rows.Add(); } // 执行标题赋值循环 for (int i = 0; i < dataGridViewASINs.Rows.Count - 1; i++) { // 等待标题元素加载完成,避免找不到元素 WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); IWebElement titleElement = wait.Until(ExpectedConditions.ElementIsVisible(By.Id("productTitle"))); string title = titleElement.GetAttribute("innerText"); // 检查列索引是否有效 if(cols >= 0 && cols < dataGridViewScrapingData.Columns.Count) { dataGridViewScrapingData.Rows[i].Cells[cols].Value = title; } }
额外提醒:爬取第二页后,务必确认driver已经跳转到目标页面,别还在操作第一页的元素。
问题2:循环第二次标题赋值出错+ComboBox列重复添加
你的代码里有两个致命问题:
- 每次循环都新增ComboBox列:
dataGridViewScrapingData.Columns.Add(comboState);放在循环里会导致每次迭代都加一列,第二次循环时列结构完全混乱,必然报错。 - ComboBox的Items没有清空:每次循环都往同一个
comboState里塞新图片URL,导致后续行的ComboBox会包含之前所有行的图片,数据完全串了。
修复方案:
把列创建移到循环外,每次循环清空当前行ComboBox的Items:
// 第一步:只创建一次ComboBox列,放在循环外面 DataGridViewComboBoxColumn comboCol = new DataGridViewComboBoxColumn(); comboCol.Name = "ProductImages"; comboCol.HeaderText = "产品图片"; dataGridViewScrapingData.Columns.Add(comboCol); // 执行循环逻辑 for (int i = 0; i < dataGridViewASINs.Rows.Count - 1; i++) { // 获取当前行的ComboBox单元格,先清空旧Items DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)dataGridViewScrapingData.Rows[i].Cells["ProductImages"]; comboCell.Items.Clear(); // 获取图片URL逻辑 List<IWebElement> imageCounts = driver.FindElements(By.XPath("//ul[@class='a-unordered-list a-nostyle a-button-list a-vertical a-spacing-top-extra-large']//li[@class='a-spacing-small item imageThumbnail a-declarative']//span[@class='a-button-text']//img")).ToList(); IWebElement element = driver.FindElement(By.Id("landingImage")); comboCell.Items.Add(element.GetAttribute("src")); // 遍历图片缩略图(这里不需要-1,否则会漏掉最后一个元素) for (int j = 0; j < imageCounts.Count; j++) { // 等待元素可点击,避免点击失败 WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5)); string GenricXpath = $"//ul[@class='a-unordered-list a-nostyle a-button-list a-vertical a-spacing-top-extra-large']//li[{j+5}]//span[1]//span[1]//span[1]//input[1]"; IWebElement clickElement = wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath(GenricXpath))); clickElement.Click(); // 等待选中的图片加载完成 string AnotherXpath = $"//li[@class='image item itemNo{j+1} maintain-height selected']//img"; IWebElement imgElement = wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(AnotherXpath))); comboCell.Items.Add(imgElement.GetAttribute("src")); } // 可选:设置默认选中第一个图片 if(comboCell.Items.Count >0) { comboCell.Value = comboCell.Items[0]; } }
标题赋值的问题和问题1同理,确保行已创建+元素加载完成即可解决。
问题3:从ComboBox列提取所有数据到List
分两种场景,看你需要提取的是每行选中的图片URL还是每行ComboBox里的所有选项:
场景1:提取每行选中的图片URL
List<string> selectedImageUrls = new List<string>(); foreach(DataGridViewRow row in dataGridViewScrapingData.Rows) { if(!row.IsNewRow) // 排除DataGridView默认的最后一行空行 { DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)row.Cells["ProductImages"]; if(comboCell.Value != null) { selectedImageUrls.Add(comboCell.Value.ToString()); } } }
场景2:提取每行ComboBox里的所有选项
// 嵌套List:每个子List对应一行的所有图片URL List<List<string>> allImageUrls = new List<List<string>>(); foreach(DataGridViewRow row in dataGridViewScrapingData.Rows) { if(!row.IsNewRow) { DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)row.Cells["ProductImages"]; List<string> rowUrls = new List<string>(); foreach(var item in comboCell.Items) { rowUrls.Add(item.ToString()); } allImageUrls.Add(rowUrls); } } // 如果要把所有URL合并成一个扁平的List List<string> flatAllUrls = allImageUrls.SelectMany(list => list).ToList();
内容的提问来源于stack exchange,提问作者Oxama Baig
相关产品推荐
相关产品推荐

