文章目錄
使用 C# 搭配 Selenium 來獲取網頁內容
最近團隊有個需求,需要從網頁上抓取一些資料,雖然普遍對於網路爬蟲的第一印象都是 python,但因為團隊中多數成員都是 C# developer,為了非常態性需求改使用 python,不僅開發效率變差,加上後續維護也會因為熟悉度不夠而多花不少時間在重新上手,因此還是決定以 C# 為主,只是爬網頁資料有不少套件可以利用,便想趁這個機會比較一下各家 library 的差異,以下是我比較的幾個 library 與筆記連結:
- HtmlAgilityPack:使用 C# 搭配 HtmlAgilityPack 來獲取網頁內容
- ScrapySharp:使用 C# 搭配 ScrapySharp 來獲取網頁內容
- AngleSharp:使用 C# 搭配 AngleSharp 來獲取網頁內容
- Puppeteer Sharp:使用 C# 搭配 Puppeteer Sharp 來獲取網頁內容
- Selenium:使用 C# 搭配 Selenium 來獲取網頁內容
- Playwright:使用 C# 搭配 Playwright 來獲取網頁內容
- C# Crawler 套件 Benchmark
Selenium 主要是用來做 web application 的自動化測試,提供 extension 來支援各種瀏覽器,可以透過 WebDriver 來控制瀏覽器,進行各種操作,如點擊、輸入、獲取元素等。
基本環境說明
- macOS Sequoia 15.0 (Apple M2 Pro)
- .NET SDK 8.0.401
- NuGet Libraries
- Selenium.WebDriver 4.25.0
- Selenium.WebDriver.ChromeDriver 129.0.6668.7000
使用方式
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using OpenQA.Selenium; | |
//不同瀏覽器有不同的 nuget library 與 namespace | |
using OpenQA.Selenium.Chrome; | |
string fullUrl = "https://tw.stock.yahoo.com/class-quote?sectorId=26&exchange=TAI"; | |
var options = new ChromeOptions(); | |
//使用 headless 模式 | |
options.AddArguments(new List<string>() { | |
"headless", | |
"disable-gpu" | |
}); | |
//使用 ChromeDriver | |
var browser = new ChromeDriver(options); | |
browser.Navigate().GoToUrl(fullUrl); | |
//使用 css selector 找到所有股票資訊 | |
var nodes = browser.FindElements(By.CssSelector("li[class='List(n)']")); | |
var stocks = nodes.Select(a => new Stock | |
{ | |
//使用 XPath 找到股票名稱、代號、價格、漲跌、漲跌幅、開盤、昨收、最高、最低、成交量 | |
Name = a.FindElement(By.XPath("./div/div[1]/div[2]/div/div[1]")).Text.Trim(), | |
Symbol = a.FindElement(By.XPath("./div/div[1]/div[2]/div/div[2]")).Text.Trim(), | |
Price = a.FindElement(By.XPath("./div/div[2]")).Text.Trim(), | |
Change = a.FindElement(By.XPath("./div/div[3]")).Text.Trim(), | |
PriceChange = a.FindElement(By.XPath("./div/div[4]")).Text.Trim(), | |
Open = a.FindElement(By.XPath("./div/div[5]")).Text.Trim(), | |
LastClose = a.FindElement(By.XPath("./div/div[6]")).Text.Trim(), | |
High = a.FindElement(By.XPath("./div/div[7]")).Text.Trim(), | |
Low = a.FindElement(By.XPath("./div/div[8]")).Text.Trim(), | |
Turnover = a.FindElement(By.XPath("./div/div[9]")).Text.Trim(), | |
UpDown = UpDownCheck(a.FindElement(By.XPath("./div/div[3]/span")).GetAttribute("class")) | |
}); | |
foreach(var stock in stocks) | |
{ | |
Console.WriteLine($"股票名稱: {stock.Name.PadRight(12)}\t 股票代號: {stock.Symbol}\t 股價: {stock.Price.PadRight(5)}\t 漲跌: {stock.UpDown} {stock.PriceChange.PadRight(8)}\t 漲跌幅: {stock.UpDown} {stock.Change.PadRight(8)}\t 開盤: {stock.Open}\t 昨收: {stock.LastClose}\t 最高: {stock.High}\t 最低: {stock.Low}\t 成交量(張): {stock.Turnover}"); | |
} | |
string UpDownCheck(string value) | |
{ | |
if (value.Contains("up")) | |
{ | |
return "上漲"; | |
} | |
if (value.Contains("down")) | |
{ | |
return "下跌"; | |
} | |
return string.Empty; | |
} | |
class Stock | |
{ | |
public string Name { get; set; } | |
public string Symbol { get; set; } | |
public string Price { get; set; } | |
public string Change { get; set; } | |
public string PriceChange { get; set; } | |
public string Open { get; set; } | |
public string LastClose { get; set; } | |
public string High { get; set; } | |
public string Low { get; set; } | |
public string Turnover { get; set; } | |
public string UpDown { get; set; } | |
} |
心得
- 語法有自己的特色,跟其他 library 原生用來當爬蟲的不同,但又不像 Playwright 那麼不直覺
只能使用標準寫法
li[class='List(n)']
,不允許li.List(n)
錯誤訊息
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersUnhandled exception. OpenQA.Selenium.InvalidSelectorException: invalid selector from javascript error: {"status":32,"value":"An invalid or illegal selector was specified"} (Session info: chrome=129.0.6668.59); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#invalid-selector-exception at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute) at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebDriver.FindElements(String mechanism, String value) at OpenQA.Selenium.By.<.ctor>b__11_1(ISearchContext context) at OpenQA.Selenium.By.FindElements(ISearchContext context) at OpenQA.Selenium.WebDriver.FindElements(By by) at Program.<Main>$(String[] args) in /Users/yowko.tsai/POCs/SeleniumTest/SeleniumTest/Program.cs:line 20 錯誤截圖
如何取得 xpath 或是 selector
完整程式碼請參考 GitHub - yowko/selenium-demo
參考資訊
- 使用 C# 搭配 HtmlAgilityPack 來獲取網頁內容
- 使用 C# 搭配 ScrapySharp 來獲取網頁內容
- 使用 C# 搭配 AngleSharp 來獲取網頁內容
- 使用 C# 搭配 Puppeteer Sharp 來獲取網頁內容
- 使用 C# 搭配 Selenium 來獲取網頁內容
- 使用 C# 搭配 Playwright 來獲取網頁內容
- C# Crawler 套件 Benchmark
- Selenium
- 7 Best C# Web Scraping Libraries in 2024
- Popular Scraping libraries in C#
- GitHub - yowko/selenium-demo
文章作者 Yowko Tsai
上次更新 2024-09-24
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。