반응형
HtmlAgilityPack은 HTML 문서를 쉽게 분석하고 조작할 수 있는 .NET용 라이브러리입니다. ASP.NET (ASPX) 환경에서도 사용할 수 있으며, 주로 웹 스크래핑, HTML 파싱, 특정 요소 추출 등에 활용됩니다.
1. HtmlAgilityPack 설치
HtmlAgilityPack은 NuGet 패키지로 제공됩니다. 다음 단계를 통해 프로젝트에 추가할 수 있습니다.
- Visual Studio에서 NuGet 패키지 관리자 콘솔 열기
- 도구 > NuGet 패키지 관리자 > 패키지 관리자 콘솔 클릭.
- NuGet 패키지 설치
Install-Package HtmlAgilityPack
- 설치가 완료되면 프로젝트에 HtmlAgilityPack 라이브러리가 추가됩니다.
2. HTML 파싱 기본 코드
using System;
using System.Net.Http;
using HtmlAgilityPack;
public partial class WebForm1 : System.Web.UI.Page
{
protected async void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string url = "https://example.com"; // 분석할 URL
string htmlContent = await GetHtmlFromUrlAsync(url);
// HtmlDocument로 로드
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// 예: <title> 태그 추출
var titleNode = htmlDoc.DocumentNode.SelectSingleNode("//title");
string title = titleNode != null ? titleNode.InnerText : "Title not found";
// 예: 특정 클래스의 div 요소 추출
var divNodes = htmlDoc.DocumentNode.SelectNodes("//div[@class='example-class']");
if (divNodes != null)
{
foreach (var div in divNodes)
{
Response.Write($"<p>{div.InnerText}</p>");
}
}
// 결과 출력
Response.Write($"<h1>Page Title: {title}</h1>");
}
}
private async Task<string> GetHtmlFromUrlAsync(string url)
{
using (HttpClient client = new HttpClient())
{
// URL에서 HTML 가져오기
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
3. 코드 설명
- HttpClient 사용:
- 지정한 URL에서 HTML 콘텐츠를 가져옵니다.
- HttpClient를 통해 비동기 방식으로 HTML을 다운로드합니다.
- HtmlAgilityPack.HtmlDocument:
- HTML을 파싱하고 DOM을 생성합니다.
- LoadHtml() 메서드를 사용하여 HTML 문자열을 로드합니다.
- XPath를 사용한 노드 검색:
- SelectSingleNode("//title"): HTML 문서에서 <title> 태그를 가져옵니다.
- SelectNodes("//div[@class='example-class']"): 특정 클래스를 가진 <div> 태그들을 가져옵니다.
- 결과 출력:
- Response.Write로 분석된 내용을 클라이언트에 출력합니다.
4. HTML 분석 시 주의사항
- URL의 HTML 구조가 자주 바뀌는 경우 XPath를 업데이트해야 할 수 있습니다.
- 웹사이트에 따라 robots.txt 파일을 확인하여 크롤링이 허용되는지 확인하세요.
- HTML 인코딩 문제를 처리하려면 HtmlAgilityPack의 OptionReadEncoding 속성을 사용할 수 있습니다:
htmlDoc.OptionReadEncoding = true;
더 자세하게 HtmlAgilityPack으로 노드를 찾는 법을 알아봅시다.
HtmlAgilityPack에서 노드를 찾는 방법은 XPath를 사용하여 HTML 문서의 특정 요소를 선택하는 방식으로 이루어집니다. XPath는 XML 및 HTML 문서에서 요소를 검색하기 위한 쿼리 언어로, 다양한 패턴을 지원합니다. 아래에 구체적인 예제를 통해 SelectSingleNode 및 SelectNodes를 사용하는 방법과 XPath 문법을 자세히 설명하겠습니다.
1. 기본 노드 찾기
HTML 문서에서 특정 태그를 직접 선택할 수 있습니다.
예제 HTML
<!DOCTYPE html>
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<h1>Welcome to the Sample Page</h1>
<p>This is a sample paragraph.</p>
<a href="https://example.com">Example Link</a>
</body>
</html>
XPath 예제
코드
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// 1. <title> 태그 가져오기
var titleNode = htmlDoc.DocumentNode.SelectSingleNode("//title");
Console.WriteLine(titleNode.InnerText); // Output: Sample Page
// 2. <h1> 태그 가져오기
var h1Node = htmlDoc.DocumentNode.SelectSingleNode("//h1");
Console.WriteLine(h1Node.InnerText); // Output: Welcome to the Sample Page
// 3. <p> 태그 가져오기
var pNode = htmlDoc.DocumentNode.SelectSingleNode("//p");
Console.WriteLine(pNode.InnerText); // Output: This is a sample paragraph.
2. 속성을 기준으로 노드 찾기
특정 속성을 가진 태그를 선택할 수 있습니다.
예제 HTML
<div class="header">Header Section</div>
<div class="content">Main Content</div>
<div id="footer" class="footer">Footer Section</div>
XPath 예제
// 1. class가 "header"인 <div> 가져오기
var headerNode = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='header']");
Console.WriteLine(headerNode.InnerText); // Output: Header Section
// 2. id가 "footer"인 <div> 가져오기
var footerNode = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='footer']");
Console.WriteLine(footerNode.InnerText); // Output: Footer Section
// 3. class가 "content"인 모든 <div> 찾기
var contentNodes = htmlDoc.DocumentNode.SelectNodes("//div[@class='content']");
foreach (var node in contentNodes)
{
Console.WriteLine(node.InnerText); // Output: Main Content
}
3. 여러 노드 선택
여러 노드를 선택하려면 SelectNodes를 사용합니다.
예제 HTML
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
XPath 예제
// <li> 태그들 모두 가져오기
var liNodes = htmlDoc.DocumentNode.SelectNodes("//li");
foreach (var node in liNodes)
{
Console.WriteLine(node.InnerText);
}
// Output:
// Apple
// Banana
// Cherry
4. 구체적인 XPath 패턴
XPath를 활용하면 더욱 세부적으로 노드를 선택할 수 있습니다.
예제 HTML
<div class="product">
<h2>Product 1</h2>
<span class="price">$10</span>
</div>
<div class="product">
<h2>Product 2</h2>
<span class="price">$20</span>
</div>
XPath 예제
// 첫 번째 제품의 이름과 가격 가져오기
var firstProductName = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='product']/h2").InnerText;
var firstProductPrice = htmlDoc.DocumentNode.SelectSingleNode("//div[@class='product']/span[@class='price']").InnerText;
Console.WriteLine($"{firstProductName} - {firstProductPrice}");
// Output: Product 1 - $10
// 모든 제품 정보 가져오기
var products = htmlDoc.DocumentNode.SelectNodes("//div[@class='product']");
foreach (var product in products)
{
var productName = product.SelectSingleNode("./h2").InnerText; // 현재 노드 기준 검색
var productPrice = product.SelectSingleNode("./span[@class='price']").InnerText;
Console.WriteLine($"{productName} - {productPrice}");
}
// Output:
// Product 1 - $10
// Product 2 - $20
5. XPath 문법 정리
- //tag
특정 태그를 문서 전체에서 찾음.
예: //div - 모든 <div> 태그. - ./tag
현재 노드의 하위 태그를 찾음.
예: ./span - 현재 노드 아래의 <span> 태그. - //tag[@attr='value']
특정 속성을 가진 태그를 찾음.
예: //div[@class='content'] - class가 content인 <div> 태그. - //tag[contains(@attr, 'value')]
속성 값이 특정 문자열을 포함하는 태그를 찾음.
예: //div[contains(@class, 'prod')] - class에 prod가 포함된 <div>. - //tag[text()='value']
특정 텍스트 값을 가진 태그를 찾음.
예: //span[text()='$10'] - 텍스트가 $10인 <span> 태그.
6. 전체 예제
아래는 다양한 XPath를 활용하여 HTML 문서를 분석하는 완전한 예제입니다.
using HtmlAgilityPack;
using System;
class Program
{
static void Main()
{
string htmlContent = @"
<html>
<body>
<div class='product'>
<h2>Product A</h2>
<span class='price'>$30</span>
</div>
<div class='product'>
<h2>Product B</h2>
<span class='price'>$50</span>
</div>
</body>
</html>";
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// 모든 제품 정보 가져오기
var products = htmlDoc.DocumentNode.SelectNodes("//div[@class='product']");
foreach (var product in products)
{
var productName = product.SelectSingleNode("./h2").InnerText;
var productPrice = product.SelectSingleNode("./span[@class='price']").InnerText;
Console.WriteLine($"{productName}: {productPrice}");
}
}
}
출력 결과:
Product A: $30
Product B: $50
반응형