委托是一种表示对具有特定参数列表和返回类型的方法的引用的类型。可以使用委托将方法作为参数传递给其他方法,或者异步地调用方法。
线程是一个执行单元,它可以与进程中的其他线程并发运行。可以使用线程来同时执行多个任务,或者并行化计算密集型的工作。
委托和线程之间的区别在于,委托是一种引用方法的方式,而线程是一种执行方法的方式。可以使用委托在不同的线程上调用方法,要么使用委托的 BeginInvoke 和 EndInvoke 方法,要么使用 ThreadPool 或 Task 类。
在爬虫程序中,哪一种更合适取决于具体的设计和需求。一般来说,使用委托与 ThreadPool 或 Task 比创建和管理自己的线程更高效和方便。但是,如果想要更多地控制线程的优先级、身份或生命周期,可能需要使用线程。也可能想要考虑使用 C# 5 或更高版本中的 async/await 关键字,它们使异步编程变得更容易和清晰。例如采集每天采集TOP5的新闻网站,归纳整理当天的热点新闻为案例,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using HtmlAgilityPack;
namespace AsyncWebCrawler
{
class Program
{
static async Task Main(string[] args)
{
var newsSites = new List<string>()
{
"https://www.bbc.com/news",
"https://www.cnn.com/",
"https://www.nytimes.com/",
"https://www.theguardian.com/international",
"https://www.aljazeera.com/",
};
var httpClientHandler = new HttpClientHandler
{
// 亿牛云 动态转发代理IP
// 爬虫加强版 IP和端口
string proxyHost = "www.16yun.cn";
string proxyPort = "31111";
// 爬虫加强版 代理验证信息
string proxyUser = "16YUN";
string proxyPass = "16IP";
// 设置代理服务器
Proxy = new WebProxy(string.Format("{0}:{1}", proxyHost, proxyPort), true),
UseProxy = true,
Credentials = new NetworkCredential(proxyUser, proxyPass)
};
var httpClient = new HttpClient(httpClientHandler);
var tasks = new List<Task<NewsSite>>();
// 开始异步采集
foreach (var newsSite in newsSites)
{
tasks.Add(CollectNewsSiteAsync(httpClient, newsSite));
}
// 等待所有异步采集任务完成
var results = await Task.WhenAll(tasks);
// 处理采集结果,整理热点标题
var hotTopics = new List<string>();
foreach (var result in results)
{
hotTopics.AddRange(result.GetHotTopics());
}
// 输出结果
Console.WriteLine("Hot Topics:");
foreach (var hotTopic in hotTopics)
{
Console.WriteLine(hotTopic);
}
}
// 异步采集新闻网站首页
static async Task<NewsSite> CollectNewsSiteAsync(HttpClient httpClient, string newsSite)
{
var html = await httpClient.GetStringAsync(newsSite);
var document = new HtmlDocument();
document.LoadHtml(html);
return new NewsSite(newsSite, document);
}
// 新闻网站类
class NewsSite
{
private string _url;
private HtmlDocument _document;
public NewsSite(string url, HtmlDocument document)
{
_url = url;
_document = document;
}
// 获取热点标题
public List<string> GetHotTopics()
{
var hotTopics = new List<string>();
var headlineNodes = _document.DocumentNode.SelectNodes("//h2[contains(@class,'headline')]");
if (headlineNodes != null)
{
foreach (var headlineNode in headlineNodes)
{
hotTopics.Add($"{_url}: {headlineNode.InnerText}");
}
}
return hotTopics;
}
}
}
}
该示例代码会采集五个新闻网站的首页,并整理当天的热点标题。采集过程是异步的,使用 async/await 关键字实现,同时使用动态转发代理IP提高采集效率。在处理采集结果时,代码会等待所有异步采集任务完成后再进行处理,以保证异步任务全部完成。