01.瀏覽器自動化webdriver源碼分析之啟動函數

日后,網絡爬蟲也好,數據采集也好,自動化必然是主流。因此,筆者未雨綢繆,在此研究各類自動化源碼,希望能夠趕上時代,做出一套實用的自動化框架。

這里先研究傳統的webdriver中轉來進行瀏覽器自動化的源碼。

webdriver官方位于這里:WebDriver

用過selenium的同學應該都知道,需要有selenium這個自動化庫來寫腳本,需要一個webdriver.exe,還需要一個瀏覽器。流程基本如下:

編寫腳本----->發送消息給webdriver------>webdriver發消息給瀏覽器

通過這樣一個流程,就完成了自動化。

腳本簡單,資料一大堆,瀏覽器也只是個執行者,所以關鍵在于webdriver如何接受、處理、發送消息,所以重點源碼webdriver源碼。

WebDriverMain.cpp:啟動函數


#include "config.h"#include "LogInitialization.h"
#include "WebDriverService.h"
#include <wtf/MainThread.h>
#include <wtf/Threading.h>#if OS(ANDROID)
__attribute__((visibility("default")))
int WebKit::WebDriverProcessMain(int argc, char** argv)
#else
int main(int argc, char** argv)
#endif
{WebDriver::WebDriverService::platformInit();WTF::initializeMainThread();
#if !LOG_DISABLED || !RELEASE_LOG_DISABLEDWebDriver::logChannels().initializeLogChannelsIfNecessary(WebDriver::logLevelString());
#endifWebDriver::WebDriverService service;return service.run(argc, argv);
}

步驟 1:平臺初始化:platformInit源碼里空白無實現,應該是等之后更新。

步驟 2:WTF(Web Template Framework)主線程初始化

步驟 3:日志通道初始化

步驟 4:創建并運行 WebDriver 服務

WebDriverService.cpp:解析命令參數,進入監聽loop循環。。

if (const char* targetEnvVar = getenv("WEBDRIVER_TARGET_ADDR"))targetString = String::fromLatin1(targetEnvVar);

先獲取一個名為WEBDRIVER_TARGET_ADDR的環境變量,這個變量是用于鏈接已經開啟的瀏覽器,是的,需要先把瀏覽器打開,然后webdriver回去鏈接,而不是先運行webdriver。

然后解析命令行參數,一大堆,沒什么可看的:

        if (equalSpans(arg, "-h"_span) || equalSpans(arg, "--help"_span)) {printUsageStatement(argv[0]);return EXIT_SUCCESS;}if (equalSpans(arg, "-p"_span) && portString.isNull()) {if (++i == argc) {printUsageStatement(argv[0]);return EXIT_FAILURE;}portString = String::fromLatin1(argv[i]);continue;}static constexpr auto portArgument = "--port="_span;if (spanHasPrefix(arg, portArgument) && portString.isNull()) {portString = arg.subspan(portArgument.size());continue;}static constexpr auto hostArgument = "--host="_span;if (spanHasPrefix(arg, hostArgument) && !host) {host = arg.subspan(hostArgument.size());continue;}#if ENABLE(WEBDRIVER_BIDI)static constexpr auto bidiPortArgument = "--bidi-port="_span;if (spanHasPrefix(arg, bidiPortArgument) && bidiPortString.isNull()) {bidiPortString = arg.subspan(bidiPortArgument.size());continue;}
#endifif (equalSpans(arg, "-t"_span) && targetString.isNull()) {if (++i == argc) {printUsageStatement(argv[0]);return EXIT_FAILURE;}targetString = String::fromLatin1(argv[i]);continue;}static constexpr auto targetArgument = "--target="_span;if (spanHasPrefix(arg, targetArgument) && targetString.isNull()) {targetString = arg.subspan(targetArgument.size());continue;}if (equalSpans(arg, "--replace-on-new-session"_span)) {m_replaceOnNewSession = true;continue;}}if (portString.isNull()) {printUsageStatement(argv[0]);return EXIT_FAILURE;}if (!targetString.isEmpty()) {auto position = targetString.reverseFind(':');if (position != notFound) {m_targetAddress = targetString.left(position);m_targetPort = parseIntegerAllowingTrailingJunk<uint16_t>(StringView { targetString }.substring(position + 1)).value_or(0);}}auto port = parseInteger<uint16_t>(portString);if (!port) {fprintf(stderr, "Invalid port %s provided\n", portString.utf8().data());return EXIT_FAILURE;}#if ENABLE(WEBDRIVER_BIDI)auto bidiPort = parseInteger<uint16_t>(bidiPortString);if (!bidiPort) {const int16_t bidiPortIncrement = *port == std::numeric_limits<uint16_t>::max() ? -1 : 1;bidiPort = { *port + bidiPortIncrement };fprintf(stderr, "Invalid WebSocket BiDi port %s provided. Defaulting to %d.\n", bidiPortString.utf8().data(), *bidiPort);}
#endif

最后是進入主循環,這里先線程初始化,然后看是websockst還是http模式,前者雙向,后者單向。之后如果監聽listen成功,就進入loop的循環不斷接受消息:

    WTF::initializeMainThread();const char* hostStr = host && host->utf8().data() ? host->utf8().data() : "local";
#if ENABLE(WEBDRIVER_BIDI)if (!m_bidiServer.listen(host ? *host : nullString(), *bidiPort)) {fprintf(stderr, "FATAL: Unable to listen for WebSocket BiDi server at host %s and port %d.\n", hostStr, *bidiPort);return EXIT_FAILURE;}RELEASE_LOG(WebDriverBiDi, "Started WebSocket BiDi server with host %s and port %d", hostStr, *bidiPort);
#endif // ENABLE(WEBDRIVER_BIDI)if (!m_server.listen(host, *port)) {fprintf(stderr, "FATAL: Unable to listen for HTTP server at host %s and port %d.\n", hostStr, *port);return EXIT_FAILURE;}RELEASE_LOG(WebDriverClassic, "Started HTTP server with host %s and port %d", hostStr, *port);RunLoop::run();#if ENABLE(WEBDRIVER_BIDI)m_bidiServer.disconnect();
#endifm_server.disconnect();return EXIT_SUCCESS;

其中的監聽代碼如下:

bool HTTPServer::listen(const std::optional<String>& host, unsigned port)
{auto& endpoint = RemoteInspectorSocketEndpoint::singleton();if (auto id = endpoint.listenInet(host ? host.value().utf8().data() : "", port, *this)) {m_server = id;return true;}return false;
}

這里先用了個設計模式:單例模式,為了可復用利用同一個監聽端點。

然后是很常規的server模式,打開一個TCP端口監聽。

std::optional<ConnectionID> RemoteInspectorSocketEndpoint::listenInet(const char* address, uint16_t port, Listener& listener)
{Locker locker { m_connectionsLock };auto id = generateConnectionID();auto connection = makeUnique<ListenerConnection>(id, listener, address, port);if (!connection->isListening())return std::nullopt;m_listeners.add(id, WTFMove(connection));wakeupWorkerThread();return id;
}bool RemoteInspectorSocketEndpoint::isListening(ConnectionID id)
{Locker locker { m_connectionsLock };if (m_listeners.contains(id))return true;return false;
}

主循環代碼如下,典型的一個GUI的事件消息處理機制,熟悉win32的同學應該很懂,而且這里就是純粹的win32的函數接口,獲取消息,把消息轉化為字符消息,分發消息:

void RunLoop::run()
{MSG message;while (BOOL result = ::GetMessage(&message, nullptr, 0, 0)) {if (result == -1)break;::TranslateMessage(&message);::DispatchMessage(&message);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902408.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902408.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902408.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PLOG安裝

Plog可以通過以下命令安裝 cd ~ && git clone https://github.com/SergiusTheBest/plog.gitcd plog && mkdir buildcd build && cmake ..make && sudo make installcd ~ && sudo rm -rf ./plog若無法科學上網&#xff0c;可使用git cl…

Cyber SpaceGuidance網安學習指南見解

免責聲明 如有異議請在評論區友好交流&#xff0c;或者私信 內容純屬個人見解&#xff0c;僅供學習參考 如若從事非法行業請勿食用 如有雷同純屬巧合 版權問題請直接聯系本人進行刪改 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&#xff1a; 提示&#xff1a;以…

第十五屆藍橋杯 2024 C/C++組 下一次相遇

目錄 題目&#xff1a; 題目描述&#xff1a; 題目鏈接&#xff1a; 思路&#xff1a; 自己的思路詳解&#xff1a; 更好的思路詳解&#xff1a; 代碼&#xff1a; 自己的思路代碼詳解&#xff1a; 更好的思路代碼詳解&#xff1a; 題目&#xff1a; 題目描述&#xf…

Vue3中provide和inject數據修改規則

在 Vue3 中&#xff0c;通過 inject 接收到的數據是否可以直接修改&#xff0c;取決于 provide 提供的值的類型和響應式處理方式&#xff1a; 1. 若提供的是普通值&#xff08;非響應式數據&#xff09; javascript 復制 // 父組件 provide(staticValue, 123); 子組件修改行…

今日CSS筆記

原手寫筆記 ------------------------------------------------------------------------------------------------------- css選擇器的種類有很多種。這里只介紹幾種常用的選擇器。 1. 標簽選擇器標簽選擇器是最基本的選擇器&#xff0c;它可以選擇所有的標簽。例如&#xff…

健康生活新指南

在 “朋克養生” 與 “躺平焦慮” 并存的時代&#xff0c;真正的健康生活無需刻意 “內卷”。這幾個簡單又實用的養生妙招&#xff0c;能讓你在忙碌日常中悄悄升級健康狀態&#xff0c;輕松擁抱活力人生。? 一、飲食&#xff1a;吃對食物&#xff0c;給身體 “加 Buff”? 別…

輕量級景好鼠標錄制器

景好鼠標錄制器&#xff08;詳情請戳 官網&#xff09;是一款免費無廣的鍵鼠動作錄制/循環回放工具&#xff0c;輕松自動化應對一些重復繁瑣的操作任務&#xff0c;如來回切換窗口、文檔同一相對位置的復制粘貼等場景&#xff0c;兼容Win XP - 11 。畢竟此款本身主打簡約類型&a…

結構體與共用體-------C語言經典題目(3)

結構體 1.如何定義和使用結構體指針&#xff1f; 1.結構體指針的定義 首先需要定義結構體類型&#xff0c;例如表示學生信息的結構體&#xff1a; struct Student {char name[50];int age;float score; };接著&#xff0c;使用struct 關鍵字和指針符號* 聲明結構體指針&#x…

未來教育風向標 | 教育學頂流985高校,華東師范大學《AIGC技術賦能教育數字化轉型的機遇與挑戰》,13所大學deepseek

今天大師兄給大家推薦的是華東師范大學祝智庭教授的《AIGC技術賦能教育數字化轉型的機遇與挑戰》。華東師范大學是一所985學校&#xff0c;在最新的國家學科測評中&#xff0c;軟件工程為A級&#xff0c;教育學為A級。 可以說在AI和教育的結合上是國內top級別的存在。 此講義探…

Java常用正則表達式及使用方法

在 Java 中&#xff0c;Pattern 和 Matcher 類是 java.util.regex 包的核心&#xff0c;用于處理正則表達式。 Pattern 類 Pattern 類表示編譯后的正則表達式&#xff0c;它提供了一種將正則表達式字符串編譯成可執行對象的方式&#xff0c;以便后續用于匹配操作。 常用方法…

車載軟件架構 --- 駕駛員不感知的控制器軟件運行

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 周末洗了一個澡,換了一身衣服,出了門卻不知道去哪兒,不知道去找誰,漫無目的走著,大概這就是成年人最深的孤獨吧! 舊人不知我近況,新人不知我過…

深度學習3.5 圖像分類數據集

%matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import torch as d2l代碼執行流程圖 #mermaid-svg-WWhBmQvijswiICpI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-…

Kotlin集合全解析:List和Map高頻操作手冊

Kotlin 中 Map 和 List 常用功能總結 List 常用功能 創建 List val immutableList listOf(1, 2, 3) // 不可變列表 val mutableList mutableListOf("a", "b", "c") // 可變列表 val emptyList emptyList<String>() // 空列表基本…

Yocto項目實戰教程-第7章定制鏡像菜譜與內核菜譜-7.2小節-定制應用程序

&#x1f50d; B站相應的視頻教程&#xff1a; &#x1f4cc; Yocto項目實戰教程-第7章-定制鏡像菜譜與內核菜譜 記得三連&#xff0c;標為原始粉絲,感謝大神支持。 在嵌入式Linux系統開發中&#xff0c;定制專屬應用程序往往是最貼近產品交付的那一環。而Yocto項目&#xff0c…

【圖像輪廓特征查找】圖像處理(OpenCV) -part8

17 圖像輪廓特征查找 圖像輪廓特征查找其實就是他的外接輪廓。 應用&#xff1a; 圖像分割 形狀分析 物體檢測與識別 根據輪廓點進行&#xff0c;所以要先找到輪廓。 先灰度化、二值化。目標物體白色&#xff0c;非目標物體黑色&#xff0c;選擇合適的兒值化方式。 有了輪…

C# 的 字符串插值($) 和 逐字字符串(@) 功能

這段代碼使用了 C# 的 字符串插值&#xff08;$&#xff09; 和 逐字字符串&#xff08;&#xff09; 功能&#xff0c;并在 SQL 語句中動態拼接變量。下面詳細解釋它們的用法&#xff1a; 1. $&#xff08;字符串插值&#xff09; $ 是 C# 的 字符串插值 符號&#xff0c;允許…

mockMvc構建web單元測試學習筆記

web應用本來需要依靠tomcat這個環境運行 現在用mockMvc是為了模擬這個web環境&#xff0c;簡化測試 什么是mock(模擬) 模擬對象---mock object是以可控方式模擬真實對象行為的假對象&#xff0c;通過模擬輸入數據&#xff0c;驗證程序達到預期結果 為什么使用mock對象 因為…

6.7.圖的深度優先遍歷(英文縮寫DFS)

樹是特殊的圖&#xff0c;沒有回路的圖就是樹 BFS與DFS的區別在于&#xff0c;BFS使用了隊列&#xff0c;DFS使用了棧 一.深度優先遍歷&#xff1a; 1.樹的深度優先遍歷&#xff1a; 樹的深度優先遍歷分為先根遍歷和后根遍歷。 以樹的先根遍歷為例&#xff1a; 上述圖片里…

VOS3000內存滿了怎么刪除,錄音格式如何轉換呢

一、清理VOS3000內存&#xff08;刪除舊錄音文件&#xff09; 定位錄音存儲目錄 通常錄音文件存儲在以下路徑&#xff08;以實際配置為準&#xff09;&#xff1a; bash 復制 下載 /usr/local/vos/record # 默認錄音目錄 /var/log/vos/logs # 系統日志目錄&#xff08;…

【圖問答】DeepSeek-VL 論文閱讀筆記

《DeepSeek-VL: Towards Real-World Vision-Language Understanding》 1. 摘要/引言 基于圖片問答&#xff08;Visual Question Answering&#xff0c;VQA&#xff09;的任務 2. 模型結構 和 三段式訓練 1&#xff09;使用 SigLIP 和 SAM 作為混合的vision encoder&#xf…