标签归档:浏览器,编程,自动化测试,webdriver

浏览器编程:WebDriver – ChromeDriver

浏览器编程有两个主要应用:
1 自动化测试
2 通过浏览器自动抓取内容(针对防抓的网站,模拟人工点击)

Selenium Server只是作为一个代理,它的作用是当要驱动远程浏览器(驱动一般只能监听本地端口),或需要驱动不同版本的浏览器时会有很大的作用。否则,应用程序直接面对具体的驱动即可(Selenium Server仅转发Json)。
注意:PhantomJS视乎是没有提供驱动,为了驱动这个无头浏览器,Selenium Server应该是把Json数据转换成了JS脚本让其执行(未证实)

目前主流浏览器(Chrome Firefox)都提供了WebDriver的实现,比如Chrome对应的是ChromeDriver,Firefox对应的FirefoxDriver。注:WebDriver只是一个规范标准(https://w3c.github.io/webdriver/webdriver-spec.html),而实现的方式可以不同,而https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol这里描述的就是一种实现方式,任何实现了这个规范的驱动,都具有相同的API。

WebDriver is an open source tool for automated testing of webapps across many browsers. It provides capabilities for navigating to web pages, user input, JavaScript execution, and more. ChromeDriver is a standalone server which implements WebDriver’s wire protocol for Chromium. ChromeDriver is available for Chrome on Android and Chrome on Desktop (Mac, Linux, Windows and ChromeOS).
ChromeDriver是一个实现了WebDriver无线协议的独立服务器,所以需要下载这个服务器(驱动,启动后在本地监听9515端口,所有的操作发送到9515端口,这个驱动负责解析数据并操作浏览器,所以它是一个中间件)。

下载地址:https://sites.google.com/a/chromium.org/chromedriver/downloads(有三个平台,需要注意的是不同的版本对应的Chrome版本是不同的)。

关于使用,ChromeDriver提供了一个文档:
https://sites.google.com/a/chromium.org/chromedriver/getting-started(关键点:Chrome需要安装在默认位置(否则需要指定),下载正确的ChromeDriver版本)

ChromeDriver作为一个独立的服务,可以手动启动并监控,也可以在使用SDK中提供的方法启动。

#####
@RunWith(BlockJUnit4ClassRunner.class)
public class ChromeTest extends TestCase {

  private static ChromeDriverService service;
  private WebDriver driver;

  @BeforeClass
  public static void createAndStartService() {
    service = new ChromeDriverService.Builder()
        .usingDriverExecutable(new File("path/to/my/chromedriver"))
        .usingAnyFreePort()
        .build();
    service.start();
  }

  @AfterClass
  public static void createAndStopService() {
    service.stop();
  }

  @Before
  public void createDriver() {
    driver = new RemoteWebDriver(service.getUrl(),
        DesiredCapabilities.chrome());
  }

  @After
  public void quitDriver() {
    driver.quit();
  }

  @Test
  public void testGoogleSearch() {
    driver.get("http://www.google.com");
    // rest of the test...
  }
}

####独立启动
$ ./chromedriver
Started ChromeDriver
port=9515
version=14.0.836.0

WebDriver driver = new RemoteWebDriver("http://127.0.0.1:9515", DesiredCapabilities.chrome());
driver.get("http://www.google.com");

####PHP
$service = new \Facebook\WebDriver\Chrome\ChromeDriverService(‘path/to/my/chromedriver’, 9515);
$service->start();
$service->stop();

$driver = RemoteWebDriver::create( $service->getURL(),[
                ChromeOptions::CAPABILITY => $options,
                WebDriverCapabilityType::PROXY => [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]]);

ChromeDriver实际释放的是一套RESTfull API,可以参考:https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md,所以只要按照规范发送数据即可,也可以使用SDK,比如PHP,Facebook提供了一套实现:

composer require facebook/webdriver

应用实例:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Laravel\Dusk\Chrome\ChromeProcess;
use Laravel\Dusk\Browser;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
use Facebook\WebDriver\Remote\DriverCommand;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;

class Test extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //$process = (new ChromeProcess())->toProcess();
        //$process->start();

        try {
            // 取回已经打开的SessionID
            $reuseSessionId = '51492cf202343defea198867e32a81e3';
            try {
                $driver = $this->driverBy($reuseSessionId);
            } catch (\Exception $e) {
                $driver = $this->driver();
            }

            $driver->execute(DriverCommand::CLICK);
        } catch (\Exception $e) {
            echo 'Browser can not start up: ' . $e->getMessage();
            return;
        }

        // 取回所有SESSION
        print_r($driver->getAllSessions('http://localhost:9515'));

        $sessionID = $driver->getSessionID();
        echo "\n";
        echo $sessionID;
        echo "\n";

        $browser = new Browser($driver);
        $browser->visit('https://www.baidu.com/')->type("#kw", 'ip')->press("#su");
        $browser->visit('https://www.amazon.com');

        $driver->close();
        $driver->get('http://blog.ifeeline.com');
    }

    protected function driverBy($sessionId)
    {
        $driver = RemoteWebDriver::createBySessionID($sessionId, 'http://localhost:9515');
        $driver->execute(DriverCommand::CLICK);

        return $driver;
    }

    protected function driver()
    {
        $options = (new ChromeOptions)->addArguments([
            //'--disable-gpu',
            //'--headless'
        ]);

        return RemoteWebDriver::create(
            'http://localhost:9515',
            [
                ChromeOptions::CAPABILITY => $options,
                WebDriverCapabilityType::PROXY => [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]
            ]
        );

        /*
        return RemoteWebDriver::create(
            'http://localhost:9515',
            DesiredCapabilities::chrome()->setCapability(
                ChromeOptions::CAPABILITY, $options
            )->setCapability(
                WebDriverCapabilityType::PROXY,
                [
                    'proxyType'=> 'manual',
                    'httpProxy' => 'SOCKS5://127.0.0.1:1086',
                    'sslProxy' => 'SOCKS5://127.0.0.1:1086',
                    'socksProxy' => 'SOCKS5://127.0.0.1:1086'
                ]
            )
        );
        */
    }
}

在控制浏览器上,可以应用了–disable-gpu和–headless,这样就是一个无头浏览器了(不显示具体的过程)。另外,在创建的浏览器时,可以指定代理。另外,如果一个SESSION没有正确退出,那么它还是活动的,但是它却无法重用。在知道SESSIONID的情况下,SESSION可以重用。 一般来说,进行一个任务就开启一个浏览器,完毕后正常退出记录。

Laravel中的Dusk程序包,封装的更加狠一些,连ChromeDriver二进制程序包都拉取回来,自动启动监听,对个Facebook SDK进行二次封装,使API更加友好。

如果模拟人工进行大量操作,就会频繁启动关闭浏览器,实际上,浏览器启动后对应一个SESSION,接下来只要重用这个SESSION即可,基本思路:如果当前有可重用的SESSION,就重用,没有就新建;在任务执行完后,判断SESSION是否超过了最大值,超过则关闭,否则,标记该SESSION可重用(配合定时重启脚本,防止意外)。