将硒驱动程序传递给scrapy
Posted
技术标签:
【中文标题】将硒驱动程序传递给scrapy【英文标题】:Passing selenium driver to scrapy 【发布时间】:2018-08-27 22:45:55 【问题描述】:我花了很长时间试图弄清楚这一点无济于事。我已经阅读了很多关于传回 htmlResponse 和使用 selenium 中间件的内容,但一直难以理解如何构建代码并在我的解决方案中实现。
这是我的蜘蛛代码:
import scrapy
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
count = 0
class ContractSpider(scrapy.Spider):
name = "contracts"
def start_requests(self):
urls = [
'https://www.contractsfinder.service.gov.uk/Search/Results',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def __init__(self):
self.driver = webdriver.Firefox()
self.driver.get("https://www.contractsfinder.service.gov.uk/Search/Results")
elem2 = self.driver.find_element_by_name("open")
elem2.click()
sleep(5)
elem = self.driver.find_element_by_name("awarded")
elem.click()
sleep(5)
elem3 = self.driver.find_element_by_id("awarded_date")
elem3.click()
sleep(5)
elem4 = self.driver.find_element_by_name("awarded_from")
elem4.send_keys("01/03/2018")
elem4.send_keys(Keys.RETURN)
sleep(5)
elem5 = self.driver.find_element_by_name("awarded_to")
elem5.send_keys("16/03/2018")
elem5.send_keys(Keys.RETURN)
sleep(5)
elem6 = self.driver.find_element_by_name("adv_search")
self.driver.execute_script("arguments[0].scrollIntoView(true);", elem6)
elem6.send_keys(Keys.RETURN)
def parse(self, response):
global count
count += 1
strcount = str(count)
page = self.driver.get(response.url)
filename = strcount+'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
for a in response.css('a.standard-paginate-next'):
yield response.follow(a, callback=self.parse)
selenium 部分在调用 firefox 时工作,正在发生各种 java 交互并加载最后一页结果。
代码的scrapy部分似乎正在工作(因为它找到了加载硒的firefox webdriver的下一个按钮并点击 - 我可以通过观察webdriver firefox本身来看到这一点) - 但是,实际的抓取发生了(将 HTML 保存到我的 c:\ 驱动器上)正在单独抓取 URL 'https://www.contractsfinder.service.gov.uk/Search/Results',并且没有来自 firefox webdriver 的 selenium 诱导的 java 交互。
我想我理解为什么它不能按我想要的那样工作的一些原因,例如在 start_requests 我指的是原始 URL,这意味着硒加载的页面没有被蜘蛛,但是每次我尝试通过阅读***的各种不同方法从webdriver创建响应时,我都会遇到各种错误,因为我的理解还不够好——我想我会发布一个selenium 和 scrapy 元素正在做某事的版本,但请有人解释并向我展示将 2 个元素链接在一起的最佳方法,即,一旦 selenium 完成 - 使用 firefox webdriver 加载页面并将其传递给 scrapy 做它的东西?非常感谢任何反馈。
【问题讨论】:
【参考方案1】:正如您所说,scrapy 打开您的初始网址,而不是 Selenium 修改的页面。
如果你想从 Selenium 获取页面,你应该使用 driver.page_source.encode('utf-8') (编码不是强制性的)。你也可以将它与 scrapy Selector 一起使用:
response = Selector(text=driver.page_source.encode('utf-8'))
在它像以前一样使用响应之后。
编辑:
我会尝试这样的事情(注意,我还没有测试过代码):
import scrapy
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from time import sleep
count = 0
class ContractSpider(scrapy.Spider):
name = "contracts"
def start_requests(self):
urls = [
'https://www.contractsfinder.service.gov.uk/Search/Results',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def __init__(self):
driver = webdriver.Firefox()
# An implicit wait tells WebDriver to poll the DOM for a certain amount of time when trying to find any element
# (or elements) not immediately available.
driver.implicitly_wait(5)
@staticmethod
def get__response(url):
self.driver.get("url")
elem2 = self.driver.find_element_by_name("open")
elem2.click()
elem = self.driver.find_element_by_name("awarded")
elem.click()
elem3 = self.driver.find_element_by_id("awarded_date")
elem3.click()
elem4 = self.driver.find_element_by_name("awarded_from")
elem4.send_keys("01/03/2018")
elem4.send_keys(Keys.RETURN)
elem5 = self.driver.find_element_by_name("awarded_to")
elem5.send_keys("16/03/2018")
elem5.send_keys(Keys.RETURN)
elem6 = self.driver.find_element_by_name("adv_search")
self.driver.execute_script("arguments[0].scrollIntoView(true);", elem6)
elem6.send_keys(Keys.RETURN)
return self.driver.page_source.encode('utf-8')
def parse(self, response):
global count
count += 1
strcount = str(count)
# Here you got response from webdriver
# you can use selectors to extract data from it
selenium_response = Selector(text=self.get_selenium_response(response.url))
...
【讨论】:
感谢 Alex - 几个 q's - 1) 该代码在我当前的蜘蛛中应该放在哪里,2) 我的 start_requests 应该是什么样子/我是否还需要包含 start_requests? alex 说的是对的,我建议你在代码中使用 wait.until 而不是很多 sleep ;) Alex - 再次感谢您提供代码,经过数小时的编辑和尝试,我仍然无法让脚本正常工作。只是重申我正在尝试开始工作的过程-我想加载firefox webdriver,转到URL,应用所有java-加载最后一页-将其发送到scrapy并通过过滤(通过selenium)结果进行分页-不知道我是否说清楚了。我很难理解 - 我可以解析但不断收到错误,例如未从 selenium_response = Selector(..) 定义的 'url' 你能尝试运行你的代码来调试吗?谢谢【参考方案2】:结合@Alex K和其他人的解决方案,这是我测试过的代码:
import scrapy
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
...
def __init__(self, name=None, **kwargs):
super(MySpider, self).__init__(name, **kwargs)
self.driver = webdriver.Chrome()
@staticmethod
def get_selenium_response(driver, url):
driver.get(url)
# in case of explicit amount of time
# time.sleep(5)
# in case of wait until element been found
try:
def find(driver):
table_el = driver.find_element_by_xpath('//*[@id="table_el"]')
if table_el:
return table_el
else:
return False
element = WebDriverWait(driver, 5).until(find)
return driver.page_source.encode('utf-8')
except:
driver.quit()
def parse(self, response):
response = scrapy.Selector(
text=self.get_selenium_response(self.driver, response.url))
# ...parse response as normally
【讨论】:
以上是关于将硒驱动程序传递给scrapy的主要内容,如果未能解决你的问题,请参考以下文章