User Tools

Site Tools


Action disabled: source
prog:csharp:250226-002:index

C#爬取動態網頁上的資訊:B站首頁 (2025-02-26)

Local Backup

簡介

  • 動態內容網站使用JavaScript 腳本動態檢索和渲染數據,爬取資訊時需要模擬瀏覽器行為,否則取得的原始碼基本上是空的。
  • 本文所使用的爬取步驟如下:
  • 使用Selenium取得渲染後的HTML 文檔
  • 使用HtmlAgilityPack解析HTML 文檔
  • 新建項目,安裝需要的庫:
    • Selenium.WebDriver
    • HtmlAgilityPack

取得HTML 文件

  • 需要注意的主要是以下2點:
    • 設定瀏覽器啟動參數:無頭模式、停用GPU加速、設定啟動時視窗大小
    • 等待頁面動態載入完成:等待5秒鐘,設定一個適當的時間即可
    • private static string GetHtml(string url)
      {
          ChromeOptions options = new ChromeOptions();
          // 不显示浏览器
          options.AddArgument("--headless");
          // GPU加速可能会导致Chrome出现黑屏及CPU占用率过高
          options.AddArgument("--nogpu");
          // 设置chrome启动时size大小
          options.AddArgument("--window-size=10,10");
      
          using (var driver = new ChromeDriver(options))
          {
              try
              {
                  driver.Manage().Window.Minimize();
                  driver.Navigate().GoToUrl(url);
                  // 等待页面动态加载完成
                  Thread.Sleep(5000);
                  // 返回页面源码
                  return driver.PageSource;
              }
              catch (NoSuchElementException)
              {
                  Console.WriteLine("找不到该元素");
                  return string.Empty;
              }
          }
      }

解析HTML 文件

  • 這裡以B站為例,爬取B站UP主頁上的影片訊息,如影片的標題、連結、封面。
  • 先定義一個類別來保存資訊:
  • class VideoInfo
    {
        public string Title { get; set; }
        public string Href { get; set; }
        public string ImgUrl { get; set; }
    }
  • 定義解析函數,返回視訊資訊列表:
  • private static List<VideoInfo> GetVideoInfos(string url)
    {
        List<VideoInfo> videoInfos = new List<VideoInfo>();
    
        // 加载文档
        var html = GetHtml(url);
        var htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(html);
    
        // 解析文档,先定位到视频列表标签
        var xpath = "/html/body/div[2]/div[4]/div/div/div[1]/div[2]/div/div";
        var htmlNodes = htmlDoc.DocumentNode.SelectNodes(xpath);
    
        // 循环解析它的子节点视频信息
        foreach (var node in htmlNodes)
        {
            var titleNode = node.SelectSingleNode("a[2]");
            var imgNode = node.SelectSingleNode("a[1]/div[1]/picture/source[1]");
    
            var title = titleNode.InnerText;
            var href = titleNode.Attributes["href"].Value.Trim('/');
            var imgUrl = imgNode.Attributes["srcset"].Value.Split('@')[0].Trim('/');
    
            videoInfos.Add(new VideoInfo
            {
                Title = title,
                Href = href,
                ImgUrl = imgUrl
            });
        }
        return videoInfos;
    }
  • 視訊清單標籤的XPath路徑是透過瀏覽器偵錯工具,在指定標籤上右鍵複製完整的XPath:
  • 分析程式碼中的node節點時,html文字格式可能很亂,可以透過線上HTML 程式碼格式化工具格式後再進行分析。

測試

  • 以B站UP主星瞳_Official為例,爬取影片訊息:
  • static void Main(string[] args)
    {
        var url = @"https://space.bilibili.com/401315430";
        var videoInfos = GetVideoInfos(url);
        foreach (var videoInfo in videoInfos)
        {
            Console.WriteLine(videoInfo.Title);
            Console.WriteLine(videoInfo.Href);
            Console.WriteLine(videoInfo.ImgUrl);
            Console.WriteLine();
        }
        Console.ReadKey();
    }
  • 結果如下:
  • 等一下,好妹妹
    www.bilibili.com/video/BV1uyxLeJEM9
    i0.hdslb.com/bfs/archive/46a15065d1b6722a04696ffaaa2235287ceaa452.jpg
    
    一口一个?你的超甜辣椒
    www.bilibili.com/video/BV1AQsDeiEn1
    i0.hdslb.com/bfs/archive/d93d47d67323ee284483e963ffed34fb9884cf61.jpg
  • 這裡只是示範爬取動態頁面的方法,如果想取得B站UP主的視訊訊息,建議直接使用API​​ 請求資料。

補充:使用CSS 選擇器

  • 實際使用時,動態頁面可能會根據ID不同產生不同的個性內容,這直接導致XPath 的層級發生變化、無法正常定位元素。我在實際使用過程中就遇到這種情況,例如替換上面的URL結果會直接異常:
  • //var url = @"https://space.bilibili.com/401315430";
    var url = @"https://space.bilibili.com/3494357793507869";<>
      * 建議使用CSS 選擇器來定位元素,一般來說樣式更具唯一性,使用CSS 選擇器來定位元素需要安裝庫:
        * HtmlAgilityPack.CssSelectors.NetCore
      * 偵測動態頁面上的原始碼可以發現,跟影片清單關聯的主要是.section.video、.small-item.fakeDanmu-item兩個選擇器:
        * {{:prog:csharp:250226-002:pasted:20250226-100152.png?500}}
      * 優化上面的GetVideoInfos 函數,改為CSS 選擇器:
        * <sxh css> // 解析文档,先定位到视频列表标签
     //var xpath = "/html/body/div[2]/div[4]/div/div/div[1]/div[2]/div/div";
     //var htmlNodes = htmlDoc.DocumentNode.SelectNodes(xpath);
    
    // 使用 CSS 选择器定位到 class="section video"
    var videoSectionNode = htmlDoc.DocumentNode.QuerySelector(".section.video");
    var htmlNodes = videoSectionNode.QuerySelectorAll(".small-item.fakeDanmu-item");
  • 測試結果如下:
  • 如果你不开心,可以尝试把手指放进圆圈里
    www.bilibili.com/video/BV1gExWeuEJK
    i1.hdslb.com/bfs/archive/127819dd1936b82b128576f1b8aacd95eccf58bc.jpg
    
    和室友亲嘴上舰长?不得不出卖色相了
    www.bilibili.com/video/BV1yfsReEEd3
    i2.hdslb.com/bfs/archive/1ec25178134980de1c78a761430b5deeefb9260f.jpg

參考文章

  • 5 person(s) visited this page until now.

prog/csharp/250226-002/index.txt · Last modified: 2025/02/26 10:03 (external edit)