通过 PHP 控制浏览器 —— Selenium WebDriver

老牛浏览 1215评论 0发表于

1. 介绍

Selenium 项目在「爬虫」领域是相当有名的,大多教程都是针对 Python 语言编写的。PHP 也有对应的非官方的扩展包,那就是 php-webdriver,该项目之前由 Facebook 维护。

通过 Selenium 我们可以做些啥呢?

主要有以下两个方面:

  • 浏览器自动化测试

  • 数据爬虫

Laravel 所使用的的浏览器测试工具 Laravel Dusk 就是基于 ChromeDriver 实现的。通过它我们可以模拟用户操作,测试登录模块,点击操作等,确保应用功能正常。

传统的页面直接由服务端返回 HTML,很简单就可以抓取 HTML 源码并解析出我们需要的数据。现在,随着前端的多元化发展,很多页面内容都采用 JavaScript 动态生成,这时就需要执行 JavaScript 才能返回最终数据,Selenium 正好可以很方便的实现模拟操作和访问。

2. 应用示例

接下来我们实战演示如何编写代码,使用账号密码自动登录天猫。

2.1 安装 Chrome 和 ChromeDriver

首先,需要安装 Chrome 和 ChromeDriver。打开 https://www.google.cn/chrome/ 下载谷歌浏览器并安装。然后打开 https://chromedriver.chromium.org/ 下载和安装的 Chrome 版本对应的 ChromeDriver 可执行文件。

2.2 安装 php-webdriver

通过 Composer 安装 php-webdriver:

bash
composer require php-webdriver/webdriver

2.3 登录天猫

php
<?php

namespace App\Console\Commands;

use Facebook\WebDriver\Chrome\ChromeDevToolsDriver;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;

class TmallSpider
{
    protected $login_url = 'https://login.taobao.com/member/login.jhtml';
    protected $login_required_url = 'https://member1.taobao.com/member/fresh/web_wangwang.htm';
    protected $username = 'USERNAME';
    protected $password = 'PASSWORD';
    protected $host = 'http://localhost:4444';
    protected $cookie_file;

    public function __construct()
    {
        $this->cookie_file = storage_path('taobao_cookies.key');
    }

    /**
     * @var RemoteWebDriver
     */
    protected $driver;

    /**
     * @var ChromeOptions
     */
    protected $chromeOptions;

    /**
     * @var ChromeDevToolsDriver
     */
    protected $devTools;

    /**
     * 初始化浏览器
     */
    protected function initChromeDriver()
    {
        // ChromeOptions
        $chromeOptions = new ChromeOptions();
        $chromeOptions->addArguments([
            'window-position=0,0',
            'window-size=1200,800',
            'disable-blink-features',
            'disable-blink-features=AutomationControlled',
        ]);
        $chromeOptions->setExperimentalOption('excludeSwitches', ['enable-automation']);
        $chromeOptions->setExperimentalOption('profile.managed_default_content_settings.images', 2);
        $this->chromeOptions = $chromeOptions;

        // ChromeDriver
        $capabilities = DesiredCapabilities::chrome();
        $capabilities->setCapability(ChromeOptions::CAPABILITY_W3C, $this->chromeOptions);
        $driver = RemoteWebDriver::create($this->host, $capabilities);
        $this->driver = $driver;

        // ChromeDevTools
        # 通过浏览器的 dev_tool 在 get 页面前将 .webdriver 属性改为 "undefined"
        $devTools = new ChromeDevToolsDriver($this->driver);
        $devTools->execute('Page.addScriptToEvaluateOnNewDocument', ['source' => 'Object.defineProperty(navigator, \'webdriver\', {get: () => undefined})']);
        $this->devTools = $devTools;
    }

    /**
     * 判断是否已经登录
     *
     * @param bool $first_time 是否是第一次判断,初次用 get(),后续用 navigate()
     * @return bool
     */
    protected function isLogin($first_time = false)
    {
        if ($first_time) {
            $this->driver->get($this->login_required_url);
        } else {
            $this->driver->navigate()->to($this->login_required_url);
        }

        return $this->login_required_url === $this->driver->getCurrentURL();
    }

    /**
     * 自动登录
     */
    protected function autoLogin()
    {
        $this->initChromeDriver();

        $isLogin = $this->isLogin(true);
        if (!$isLogin) {
            // 检查是否存在 cookies
            if (!file_exists($this->cookie_file)) {
                $this->login();
            }

            // cookies 存在则解析
            $cookies = unserialize(file_get_contents($this->cookie_file));
            foreach ($cookies as $cookie) {
                $this->driver->manage()->addCookie($cookie);
            }

            // 添加 cookies 后判断是否登录
            $isLogin = $this->isLogin();
            if (!$isLogin) {
                $this->login();
            }
        }
    }

    /**
     * 使用用户名密码登录
     */
    protected function login()
    {
        $this->driver->navigate()->to($this->login_url . '?redirectURL=' . urlencode($this->login_required_url));

        $this->driver->findElement(WebDriverBy::name('fm-login-id'))->sendKeys($this->username);
        $this->driver->findElement(WebDriverBy::name('fm-login-password'))->sendKeys($this->password);
        $this->driver->findElement(WebDriverBy::className('password-login'))->click();

        sleep(10);

        if ($this->login_required_url === $this->driver->getCurrentURL()) {
            // 登录成功后保存 cookies
            $cookies = $this->driver->manage()->getCookies();
            file_put_contents($this->cookie_file, serialize($cookies));
        } else {
            // 登录失败
            exit('淘宝:使用账号密码登录失败。');
        }
    }
}

运行代码前,还需要启动服务,监听端口:

bash
./chromedriver --port=4444
点赞
收藏
暂无评论,快来发表评论吧~
私信
老牛@ilaoniu
老牛,俗称哞哞。单纯的九零后理工小青年。喜欢折腾,爱玩,爱音乐,爱游戏,爱电影,爱旅游...
最后活跃于