返回介绍

9.4 Selenium

发布于 2024-01-26 22:39:51 字数 10925 浏览 0 评论 0 收藏 0

上一节我们讲解了PhantomJS的用法,它只是一个没有界面的浏览器,运行的还是JavaScript脚本,这和Python爬虫开发有什么联系呢?本节介绍的Selenium能将Python和PhantomJS紧密地联系起来,从而实现爬虫的开发。

Selenium是一个自动化测试工具,支持各种浏览器,包括Chrome、Safari、Firefox等主流界面式浏览器,也包括PhantomJS等无界面浏览器,通俗来说Selenium支持浏览器驱动,可以对浏览器进行控制。而且Selenium支持多种语言开发,比如Java、C、Ruby,还有Python,因此Python+Selenium+PhantomJS的组合就诞生了。PhantomJS负责渲染解析JavaScript,Selenium负责驱动浏览器和与Python对接,Python负责做后期处理,三者构成了一个完整的爬虫结构。

9.4.1 安装Selenium

Selenium现在最新的版本为3.0.1,本书也是以此为标准进行讲解。Selenium官方地址为:http://www.seleniumhq.org/ ,其安装主要有两种方式:

·pip install Selenium==3.0.1

·从https://pypi.python.org/pypi/selenium 下载源代码解压后,运行python setup.py install。

·Selenium3.x和Selenium2.x版本有以下区别:

·Selenium2.x调用高版本浏览器会出现不兼容问题,调用低版本浏览器正常。

·Selenium3.x调用浏览器必须下载一个类似补丁的文件,比如Firefox的为geckodriver,Chrome的为chromedriver。

各种版本浏览器的补丁下载地址为:http://www.seleniumhq.org/download/ ,如图9-11所示。

图9-11 补丁下载地址

根据自己的操作系统,下载指定的geckodriver文件。下面以Firefox为例,对geckodriver进行配置。在ubuntu下,将文件下载下来之后解压到指定目录下,我把它解压到firefoxDriver目录下,如图9-12所示。

图9-12 补丁解压位置

接着配置环境变量,在shell中执行:export PATH=$PATH:/home/ubuntu/firefoxDr-iver,将geckodriver所在的目录配置到环境变量中,其他操作系统配置方式类似。

9.4.2 快速入门

安装和配置完成后,现在开始使用Selenium写一个小例子,功能是打开百度主页,在搜索框中输入网络爬虫,进行搜索。代码如下:

  from selenium import webdriver
  from selenium.webdriver.common.keys 
     import Keys
     import time
  
  driver = webdriver.Firefox()
  driver.get("http://www.baidu.com")
  assert u"百度" in driver.title
  elem = driver.find_element_by_name("wd")
  elem.clear()
  elem.send_keys(u"网络爬虫")
  elem.send_keys(Keys.RETURN)
  time.sleep(3)
  assert u"网络爬虫." not in driver.page_source
  driver.close()

效果如图9-13所示。

图9-13 搜索网络爬虫

代码分析:首先使用webdriver.Firefox()获取Firefox浏览器的驱动,调用get方法,打开百度首页,判断标题中是否包含百度字样,接着通过元素名称wd获取输入框,通过send_keys方法将网络爬虫填写其中,然后回车。延时3秒后,判断搜索页面是否有网络爬虫字样,最后关闭driver。

我相信即使是同样的代码,大家也会遇到各种各样的问题。下面将大家可能遇到的问题进行一下总结:

1)错误信息为:Exception AttributeError:“’Service‘object has no attribute’process‘”in...,可能是geckodriver环境变量有问题,重新将geckodriver所在目录配置到环境变量中。或者直接在代码中指定路径:

webdriver.Firefox(executable_path=’/home/ubuntu/firefoxDriver/geckodriver‘)

2)错误信息为:selenium.common.exceptions.WebDriverException:Message:Unsupported Marionette protocol version 2,required 3,可能是Firefox版本太低,使用Selenium3.x要求Firefox>=v47。

3)错误信息为:selenium.common.exceptions.WebDriverException:Message:Failed to start browser,可能是没找到Firefox浏览器,可以在代码中指定Firefox的位置:

  binary = FirefoxBinary(r'E:\Mozilla Firefox\firefox.exe')
  driver = webdriver.Firefox(firefox_binary=binary)

9.4.3 元素选取

要想对页面进行操作,首先要做的是选中页面元素。元素选取方法如表9-5所示。

表9-5 定位方法

除了上面具有确定功能的方法,还有两个通用方法find_element和find_elements,可以通过传入参数来指定功能。示例如下:

  from selenium.webdriver.common.by import By
  driver.find_element(By.XPATH, '// button[text()="Some text"]')

这一个例子是通过xpath表达式来查找,方法中第一个参数是指定选取元素的方式,第二个参数是选取元素需要传入的值或表达式。第一个参数还可以传入By类中的以下值:

·By.ID

·By.XPATH

·By.LINK_TEXT

·By.PARTIAL_LINK_TEXT

·By.NAME

·By.TAG_NAME

·By.CLASS_NAME

·By.CSS_SELECTOR

下面通过一个HTML文档来讲解一下如何使用以上方法提取内容,HTML文档如下:

  <html>
     <body>
     <h1>Welcome</h1>
     <p class="content">用户登录</p>
       <form id="loginForm">
       <input name="username" type="text" />
       <input name="password" type="password" />
       <input name="continue" type="submit" value="Login" />
       <input name="continue" type="button" value="Clear" />
       </form>
     <a href="register.html">Register</a>
     </body>
  <html>

定位方法的使用如表9-6所示。

表9-6 定位方法示例

9.4.4 页面操作

以如下HTML文档为例介绍页面操作,login.html代码如下:

  <html>
  <head>
  <meta http-equiv="content-type" content="text/html;charset=gbk">
  </head>
     <body>
     <h1>Welcome</h1>
     <p class="content">用户登录</p>
       <form id="loginForm">
            <select name="loginways">
              <option value="email">邮箱</option>
              <option value="mobile">手机号</option>
              <option value="name">用户名</option>
       </select>
       <br/>
            <input name="username" type="text" />
            <br/>
            密码
            <br/>
            <input name="password" type="password" />
            <br/><br/>
            <input name="continue" type="submit" value="Login" />
            <input name="continue" type="button" value="Clear" />
       </form>
     <a href="register.html">Register</a>
     </body>
  </html>

效果如图9-14所示。

图9-14 登录页面

1.页面交互与填充表单

第一步: 初始化Firefox驱动,打开html文件,由于是本地文件,可以使用下面方式打开。

  driver = webdriver.Firefox()
  driver.get("file:// /e:/login.html")

第二步: 获取用户名和密码的输入框,和登录按钮。

  username = driver.find_element_by_name('username')
  password = driver.find_element_by_xpath(".// *[@id='loginForm']/input[2]")
  login_button = driver.find_element_by_xpath("// input[@type='submit']")

第三步: 使用send_keys方法输入用户名和密码,使用click方法模拟点击登录。

  username.send_keys("qiye")
  password.send_keys("qiye_pass")
  login_button.click()

如果想清除username和password输入框的内容,可以使用clear方法。

  username.clear()
  password.clear()

上面还有一个问题没解决,如何操作下拉选项卡选择登录方式呢?第一种方法代码如下:

  select = driver.find_element_by_xpath("// form/select")
  all_options = select.find_elements_by_tag_name("option")
  for option in all_options:
     print("Value is: %s" % option.get_attribute("value"))
     option.click()

在代码中首先获取select元素,也就是下拉选项卡。然后轮流设置了select选项卡中的每一个option选项。这并不是一个非常好的办法。官方提供了更好的实现方式,在WebDriver中提供了一个叫Select方法,也就是第二种操作方式。代码如下:

  from selenium.webdriver.support.ui import Select
  select = Select(driver.find_element_by_xpath('// form/select '))
  select.select_by_index(index)
  select.select_by_visible_text("text")
  select.select_by_value(value)

它可以根据索引、文字、value值来选择选项卡中的某一项。

如果select标记中multiple=“multiple”,也就是说这个select标记支持多选,Select对象提供了支持此功能的方法和属性。示例如下:

·取消所有的选项:select.deselect_all()

·获取所有的选项:select.options

·获取已选中的选项:select.all_selected_options

2.元素拖拽

元素的拖拽即将一个元素拖到另一个元素的位置,类似于拼图。首先要找到源元素和目的元素,然后使用ActionChains类可以实现。代码如下:

  element = driver.find_element_by_name("source")
  target = driver.find_element_by_name("target")
  
  from selenium.webdriver import ActionChains
  action_chains = ActionChains(driver)
  action_chains.drag_and_drop(element, target).perform()

3.窗口和页面frame的切换

一个浏览器一般都会开多个窗口,我们可以switch_to_window方法实现指定窗口的切换。示例如下:

  driver.switch_to_window("windowName")

也可以通过window handle来获取每个窗口的操作对象。示例如下:

  for handle in driver.window_handles:
  driver.switch_to_window(handle)

如需切换页面frame,可以使用switch_to_frame方法,示例如下:

  driver.switch_to_frame("frameName")
  driver.switch_to_frame("frameName.0.child")

4.弹窗处理

如果你在处理页面的过程中,触发了某个事件,跳出弹框。可以使用switch_to_alert获取弹框对象,从而进行关闭弹框、获取弹框信息等操作。示例如下:

  alert = driver.switch_to_alert()
  alert.dismiss()

5.历史记录

操作页面的前进和后退功能,示例如下:

  driver.forward()
  driver.back()

6.Cookie处理

可以使用get_cookies方法获取cookie,也可以使用add_cookie方法添加cookie信息。示例如下:

  driver.get("http://www.baidu.com")
  cookie = {'name': 'foo', 'value' : 'bar'}
  driver.add_cookie(cookie)
  driver.get_cookies()

7.设置phantomJS请求头中User-Agent

这个功能在爬虫中非常有用,一般针对phantomJS的反爬虫措施都会检测这个字段,默认的User-Agent中含有phantomJS内容,可以通过代码进行修改。代码如下:

  dcap = dict(DesiredCapabilities.PHANTOMJS)
  dcap["phantomjs.page.settings.userAgent"] = (
     "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 
     (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36"
  )
  driver = webdriver.PhantomJS()# desired_capabilities=dcap)
  driver.get("http://www.google.com")
  driver.quit()

9.4.5 等待

由于现在很多网站采用Ajax技术,不确定网页元素什么时候能被完全加载,所以网页元素的选取会比较困难,这时候就需要等待。Selenium有两种等待方式,一种是显式等待,一种是隐式等待。

1.显式等待

显式等待是一种条件触发式的等待方式,指定某一条件直到这个条件成立时才会继续执行,可以设置超时时间,如果超过这个时间元素依然没被加载,就会抛出异常。示例如下:

  from selenium import webdriver
  from selenium.webdriver.common.by import By
  from selenium.webdriver.support.ui import WebDriverWait
  from selenium.webdriver.support import expected_conditions as EC
  
  driver = webdriver.Firefox()
  driver.get("http://somedomain/url_that_delays_loading")
  try:
     element = WebDriverWait(driver, 10).until(
       EC.presence_of_element_located((By.ID, "myDynamicElement"))
     )
  finally:
     driver.quit()

以上代码加载http://somedomain/url_that_delays_loading 页面,并定位id为myDynamic Element的元素,设置超时时间为10s。WebDriverWait默认会500ms检测一下元素是否存在。

Selenium提供了一些内置的用于显式等待的方法,位于expected_conditions类中,方法名称如表9-7所示:

表9-7 内置方法

2.隐式等待

隐式等待是在尝试发现某个元素的时候,如果没能立刻发现,就等待固定长度的时间,类似于socket超时,默认设置是0秒。一旦设置了隐式等待时间,它的作用范围是Webdriver对象实例的整个生命周期,也就是说Webdriver执行每条命令的超时时间都是如此。如果大家感觉设置的时间过长,可以进行不断地修改。使用方法示例如下:

  from selenium import webdriver
  driver = webdriver.Firefox()
  driver.implicitly_wait(10) # seconds
  driver.get("http://somedomain/url_that_delays_loading")
  myDynamicElement = driver.find_element_by_id("myDynamicElement")

3.线程休眠

time.sleep(time),这是使用线程休眠延时的办法,也是比较常用的。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文