嗨,我在网页抓取或使用scrapy和硒方面没有太多经验。如果我的代码中有太多不良做法,请首先道歉。
我的代码的简要背景:我试图使用scrapy从多个网站上获取产品信息,并且我还使用了硒,因为我需要单击网页上的“查看更多”按钮和“不,谢谢”按钮。由于网站上有不同类别的href,因此我还需要请求这些“子链接”,以确保我不会错过根页面上未显示的任何项目。
问题是,我注意到在此for循环for l in product_links:
中,scrapy和selenium的行为似乎很奇怪。例如,我希望response.url == self.driver.current_url
永远是正确的。但是,它们在此for循环的中间变得不同。此外,self.driver
似乎捕获了products = self.driver.find_elements_by_xpath('//div[@data-url]')
当前URL中不存在的某些元素,然后在sub = self.driver.find_elements_by_xpath('//div[(@class="shelf-container") and (.//div/@data-url="' + l + '")]//h2')
非常感谢。我真的很困惑
from webScrape.items import ProductItem
from scrapy import Spider, Request
from selenium import webdriver
class MySpider(Spider):
name = 'name'
domain = 'https://uk.burberry.com'
def __init__(self):
super().__init__()
self.driver = webdriver.Chrome('path to driver')
self.start_urls = [self.domain + '/' + k for k in ('womens-clothing', 'womens-bags', 'womens-scarves',
'womens-accessories', 'womens-shoes', 'make-up', 'womens-fragrances')]
self.pool = set()
def parse(self, response):
sub_links = response.xpath('//h2[starts-with(@class, "shelf1-section-title")]/a/@href').extract()
if len(sub_links) > 0:
for l in sub_links:
yield Request(self.domain + l, callback = self.parse)
self.driver.get(response.url)
email_reg = self.driver.find_element_by_xpath('//button[@class="dc-reset dc-actions-btn js-data-capture-newsletter-block-cancel"]')
if email_reg.is_displayed():
email_reg.click()
# Make sure to click all the "load more" buttons
load_more_buttons = self.driver.find_elements_by_xpath('//div[@class="load-assets-button js-load-assets-button ga-shelf-load-assets-button"]')
for button in load_more_buttons:
if button.is_displayed():
button.click()
products = self.driver.find_elements_by_xpath('//div[@data-url]')
product_links = [item.get_attribute('data-url') for item in products if item.get_attribute('data-url').split('-')[-1][1:] not in self.pool]
for l in product_links:
sub = self.driver.find_elements_by_xpath('//div[(@class="shelf-container") and (.//div/@data-url="' + l + '")]//h2')
if len(sub) > 0:
sub_category = ', '.join(set([s.get_attribute('data-ga-shelf-title') for s in sub]))
else:
sub_category = ''
yield Request(self.domain + l, callback = self.parse_product, meta = {'sub_category': sub_category})
def parse_product(self, response):
item = ProductItem()
item['id'] = response.url.split('-')[-1][1:]
item['sub_category'] = response.meta['sub_category']
item['name'] = response.xpath('//h1[@class="product-title transaction-title ta-transaction-title"]/text()').extract()[0].strip()
self.pool.add(item['id'])
yield item
others = response.xpath('//input[@data-url]/@data-url').extract()
for l in others:
if l.split('-')[-1][1:] not in self.pool:
yield Request(self.domain + l, callback = self.parse_product, meta = response.meta)
答案 0 :(得分:0)
Scrapy是一个异步框架。 parse*()
方法中的代码并非总是线性运行。无论哪里有yield
,该代码的执行可能会在代码的其他部分运行时在那里停止一段时间。
因为循环中有一个yield
,这说明了为什么您会遇到这种意外行为。在yield
,程序的其他一些代码将恢复执行,并可能将Selenium驱动程序切换到其他URL,并且当代码恢复循环时,Selenium驱动程序的URL已更改。
说实话,就我所知,您并不需要Scrapy中的Selenium。在Scrapy中,诸如Splash或Selenium之类的东西仅在非常特定的情况下使用,诸如避免机器人检测之类的东西。
通过使用Web浏览器(检查,网络)中的开发人员工具,然后在Scrapy中再现它们,通常是找出页面HTML的结构和请求中使用的参数的更好的方法。