使用 C# 搭配 Selenium 來獲取網頁內容

最近團隊有個需求,需要從網頁上抓取一些資料,雖然普遍對於網路爬蟲的第一印象都是 python,但因為團隊中多數成員都是 C# developer,為了非常態性需求改使用 python,不僅開發效率變差,加上後續維護也會因為熟悉度不夠而多花不少時間在重新上手,因此還是決定以 C# 為主,只是爬網頁資料有不少套件可以利用,便想趁這個機會比較一下各家 library 的差異,以下是我比較的幾個 library 與筆記連結:

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

使用方式

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; }
}
view raw Program.cs hosted with ❤ by GitHub

心得

  • 語法有自己的特色,跟其他 library 原生用來當爬蟲的不同,但又不像 Playwright 那麼不直覺
  • 只能使用標準寫法 li[class='List(n)'],不允許 li.List(n)

    • 錯誤訊息

      Unhandled 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
      view raw brackets.err hosted with ❤ by GitHub
    • 錯誤截圖

      1error

  • 如何取得 xpath 或是 selector

    • 開啟瀏覽器的開發者工具並選取要取得的元素

      2developertool

    • 右鍵選擇 Copy –> Copy XPath 或是 Copy selector

      3copy

完整程式碼請參考 GitHub - yowko/selenium-demo

參考資訊

  1. 使用 C# 搭配 HtmlAgilityPack 來獲取網頁內容
  2. 使用 C# 搭配 ScrapySharp 來獲取網頁內容
  3. 使用 C# 搭配 AngleSharp 來獲取網頁內容
  4. 使用 C# 搭配 Puppeteer Sharp 來獲取網頁內容
  5. 使用 C# 搭配 Selenium 來獲取網頁內容
  6. 使用 C# 搭配 Playwright 來獲取網頁內容
  7. C# Crawler 套件 Benchmark
  8. Selenium
  9. 7 Best C# Web Scraping Libraries in 2024
  10. Popular Scraping libraries in C#
  11. GitHub - yowko/selenium-demo