Selenium 项目在「爬虫」领域是相当有名的,大多教程都是针对 Python 语言编写的。PHP 也有对应的非官方的扩展包,那就是 php-webdriver,该项目之前由 Facebook 维护。
通过 Selenium 我们可以做些啥呢?
主要有以下两个方面:
浏览器自动化测试
数据爬虫
Laravel 所使用的的浏览器测试工具 Laravel Dusk 就是基于 ChromeDriver 实现的。通过它我们可以模拟用户操作,测试登录模块,点击操作等,确保应用功能正常。
传统的页面直接由服务端返回 HTML,很简单就可以抓取 HTML 源码并解析出我们需要的数据。现在,随着前端的多元化发展,很多页面内容都采用 JavaScript 动态生成,这时就需要执行 JavaScript 才能返回最终数据,Selenium 正好可以很方便的实现模拟操作和访问。
接下来我们实战演示如何编写代码,使用账号密码自动登录天猫。
首先,需要安装 Chrome 和 ChromeDriver。打开 https://www.google.cn/chrome/ 下载谷歌浏览器并安装。然后打开 https://chromedriver.chromium.org/ 下载和安装的 Chrome 版本对应的 ChromeDriver 可执行文件。
通过 Composer 安装 php-webdriver:
composer require php-webdriver/webdriver
<?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('淘宝:使用账号密码登录失败。');
}
}
}
运行代码前,还需要启动服务,监听端口:
./chromedriver --port=4444