現在大多數網站都是隨著滾動條的滑動載入頁面內容的,因此單純獲得靜態頁面的Html是無法獲得全部的頁面內容的。使用Selenium就可以模擬瀏覽器拉動滑動條來載入所有頁面內容。 ...
現在大多數網站都是隨著滾動條的滑動載入頁面內容的,因此單純獲得靜態頁面的Html是無法獲得全部的頁面內容的。使用Selenium就可以模擬瀏覽器拉動滑動條來載入所有頁面內容。
前情提要
Selenium簡介
Selenium是一個WEB自動化測試工具。Selenium測試直接運行在瀏覽器中,就像真正的用戶在操作一樣。支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。主要功能包括:測試與瀏覽器的相容性——測試你的應用程式看是否能夠很好得工作在不同瀏覽器和操作系統之上。測試系統功能——創建回歸測試檢驗軟體功能和用戶需求。支持自動錄製動作和自動生成 .Net、Java、Perl等不同語言的測試腳本。Selenium也是一款同樣使用Apache License 2.0協議發佈的開源框架。
C#安裝Selenium
本文僅僅是使用Selenium實現拉動滾動條的功能,所以不對Selenium進行過多的介紹。
通過Nuget包管理器搜索"Selenium",分別安裝:
- Selenium.WebDriver
- Selenium.Chrome.WebDriver
實例(獲取某網站主頁所有圖片)
普通獲取網頁Html
ChromeDriver driver = new ChromeDriver();
driver.Navigate().GoToUrl(url);
string title = driver.Title;//頁面title
string html = driver.PageSource;//頁面Html
不啟動Chrome視窗及關閉Chrome控制台獲取網頁
程式執行時會自動打開Chrome視窗和輸出控制臺中一些信息,我們不需要這些東西。
//不啟動chrome視窗
ChromeOptions options = new ChromeOptions();
options.AddArgument("headless");
//關閉ChromeDriver控制台
ChromeDriverService driverService = ChromeDriverService.CreateDefaultService();
driverService.HideCommandPromptWindow = true;
ChromeDriver driver = new ChromeDriver(driverService, options);
driver.Navigate().GoToUrl(url);
將頁面滾動到底部
如果使用scrollTo(0, document.body.scrollHeight)
,直接讓將頁面滾動到底部會導致頁面中間部分讀取失敗,所以需要分幾次滑動並且給頁面足夠的時間載入
for (int i = 1; i <= 10; i++)
{
string jsCode = "window.scrollTo({top: document.body.scrollHeight / 10 * " + i + ", behavior: \"smooth\"});";
//使用IJavaScriptExecutor介面運行js代碼
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript(jsCode);
//暫停滾動
Thread.Sleep(1000);
}
使用HtmlAgilityPack解析讀取到的Html
以下內容與上一篇文章基本相同
string title = driver.Title;//頁面title
string html = driver.PageSource;//頁面Html
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);//解析Html字元串
string imgPath = "//img";//選擇img
//獲取img標簽中的圖片
foreach (HtmlNode node in doc.DocumentNode.SelectNodes(imgPath))
{
······
}
完整代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using HtmlAgilityPack;
using System.Text.RegularExpressions;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System.Threading;
namespace WebCrawlerDemo
{
class Program
{
static void Main(string[] args)
{
WebClient wc = new WebClient();
int imgNum = 0;//圖片編號
string url = "https://www.bilibili.com";
string html = FinalHtml.GetFinalHtml(url, 10);
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
string imgPath = "//img";//選擇img
//HtmlNode nodes = hd.DocumentNode.SelectSingleNode(path);
//獲取img標簽中的圖片
foreach (HtmlNode node in doc.DocumentNode.SelectNodes(imgPath))
{
if (node.Attributes["src"] != null)
{
string imgUrl = node.Attributes["src"].Value.ToString();
if (imgUrl != "" && imgUrl != " ")
{
imgNum++;
//生成文件名,自動獲取尾碼
string fileName = GetImgName(imgUrl, imgNum);
//Console.WriteLine(fileName);
//Console.WriteLine(imgUrl);
ImgDownloader.DownloadImg(wc, imgUrl, "images/", fileName);
}
}
}
//獲取背景圖
string bgImgPath = "//*[@style]";//選擇具有style屬性的節點
foreach (HtmlNode node in doc.DocumentNode.SelectNodes(bgImgPath))
{
if (node.Attributes["style"].Value.Contains("background-image:url"))
{
imgNum++;
string bgImgUrl = node.Attributes["style"].Value;
bgImgUrl = Regex.Match(bgImgUrl, @"(?<=\().+?(?=\))").Value;//讀取url()的內容
//Console.WriteLine(bgImgUrl);
//生成文件名,自動獲取尾碼
string fileName = GetImgName(bgImgUrl, imgNum);
ImgDownloader.DownloadImg(wc, bgImgUrl, "images/bgcImg/", fileName);
}
}
Console.WriteLine("----------END----------");
Console.WriteLine($"一共獲得: {imgNum}張圖");
Console.ReadKey();
}
}
/// <summary>
/// 圖片下載器
/// </summary>
public class ImgDownloader
{
/// <summary>
/// 下載圖片
/// </summary>
/// <param name="webClient"></param>
/// <param name="url">圖片url</param>
/// <param name="folderPath">文件夾路徑</param>
/// <param name="fileName">圖片名</param>
public static void DownloadImg(WebClient webClient, string url, string folderPath, string fileName)
{
//如果文件夾不存在,則創建一個
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
//判斷路徑是否完整,補全不完整的路徑
if (url.IndexOf("https:") == -1 && url.IndexOf("http:") == -1)
{
url = "https:" + url;
}
//下載圖片
try
{
webClient.DownloadFile(url, folderPath + fileName);
Console.WriteLine(fileName + "下載成功");
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.WriteLine(url);
}
}
/// <summary>
/// 生成圖片名稱
/// </summary>
/// <param name="imageUrl">圖片地址</param>
/// <param name="imageNum">圖片編號</param>
/// <returns></returns>
public static string GetImgName(string imageUrl, int imageNum)
{
string imgExtension;
if (imageUrl.LastIndexOf(".") != -1)
{
imgExtension = imageUrl.Substring(imageUrl.LastIndexOf("."));
}
else
{
imgExtension = ".jpg";
}
return imageNum + imgExtension;
}
}
/// <summary>
/// 獲得執行過js的網址
/// </summary>
public class FinalHtml
{
/// <summary>
/// 獲得拉動滾動條後的頁面
/// </summary>
/// <param name="url">網址</param>
/// <param name="sectionNum">滾動幾次</param>
/// <returns>html字元串</returns>
public static string GetFinalHtml(string url, int sectionNum)
{
//不啟動chrome視窗
ChromeOptions options = new ChromeOptions();
options.AddArgument("headless");
//關閉ChromeDriver控制台
ChromeDriverService driverService = ChromeDriverService.CreateDefaultService();
driverService.HideCommandPromptWindow = true;
ChromeDriver driver = new ChromeDriver(driverService, options);
driver.Navigate().GoToUrl(url);
string title = driver.Title;
Console.WriteLine($"Title: {title}");
//將頁面滾動到底部
Console.Write("頁面滾動中,請稍後");
for (int i = 1; i <= sectionNum; i++)
{
string jsCode = "window.scrollTo({top: document.body.scrollHeight / " + sectionNum + " * " + i + ", behavior: \"smooth\"});";
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript(jsCode);
Console.Write(".");
Thread.Sleep(1000);
}
Console.WriteLine();
string html = driver.PageSource;
driver.Quit();
return html;
}
}
}