多线程控制台应用中的 Web 爬网
本文关键字:Web 爬网 控制台 应用 多线程 | 更新日期: 2023-09-27 18:30:16
多线程应用程序的新手。
我正在尝试创建一个控制台应用程序来检查给定的 IP 地址列表(内部网)。任何给定 IP 地址的每个网页都包含一些统计信息,显示在 html 表中,我需要收集这些统计信息。我可以在单个线程中执行此操作:设置请求/响应序列,获取页面内容并解析它。
我现在正在努力的是使这个多线程,因为我必须处理 4000 个 IP 地址,而单线程需要一些时间。我在字符串列表或数组中有 IP 列表;您知道如何设置线程吗?
假设我有一个处理响应的函数,比如"ProcessResponse(string s)",并且想从 10 个线程开始,我可以从这样的东西开始吗:
public class PASSServer
{
private string _ip;
public string IPAddress
{
get;
set;
}
public PASSServer()
{
}
}
static void Main(string[] args)
{
int iNumThreads = 3;
Thread[] threads = new Thread[iNumThreads];
string[] sIPs = { "192.168.10.20", "192.168.10.21", "192.168.10.22" };
for (int i = 0; i < threads.Length; i++)
{
ParameterizedThreadStart start = new ParameterizedThreadStart(Start);
threads[i] = new Thread(start);
PASSServer pserver = new PASSServer();
pserver.IPAddress = sIPs[i];
threads[i].Start(pserver);
}
Console.WriteLine("DONE");
Console.ReadKey();
}
static void Start(object info)
{
PASSServer pserver = (PASSServer)info;
crawl(pserver.IPAddress);
}
private static void crawl(string sUrl)
{
PASSData cData = new PASSData();
string sRequestUrl = "http://" + sUrl.Trim() + "/cgi-bin/sysstat?";
string sEncodingType = "utf-8";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sRequestUrl);
request.KeepAlive = true;
request.Timeout = 15 * 1000;
System.Net.HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string sStatus = ((HttpWebResponse)response).StatusDescription;
sEncodingType = GetEncodingType(response);
System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream(), Encoding.GetEncoding(sEncodingType));
// Read the content.
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
}
任何帮助将不胜感激。
我没有使用多线程,但用谷歌搜索了这个主题并得到了一些想法,只是不确定如何最好地设置我的场景。
不要使用线程。使用异步 HTTP 请求。例如,使用 HttpWebRequest.BeginGetResponse 或 HttpWebRequest.GetResponseAsync。使用信号量限制并发请求数。
因此,如果您有一个 URL 列表(List<string>
),并且您最多需要 10 个并发请求:
List<string> _urls = GetListOfUrls();
Semaphore _requestSemaphore = new Semaphore(10, 10);
foreach (var url in _urls)
{
// wait for an available spot
_requestSemaphore.WaitOne();
// Now start an asynchronous request with this url
var request = (HttpWebRequest)WebRequest.Create(url);
request.BeginGetResponse(GetResponseCallback, request);
}
当您的列表为空时,您必须等待收到最终响应。您这样做的方法是在信号灯上等待 10 次。当您有 10 个时,则不会有任何未完成的请求:
for (int i = 0; i < 10; ++i)
{
_requestSemaphore.WaitOne();
}
以及收到响应时调用的回调:
void GetResponseCallback(IAsyncResult ar)
{
var request = (HttpWebRequest)ar.AsyncState;
var response = (HttpWebResponse)request.EndGetResponse(ar);
// process the response here.
// when you're done processing the response, release the semaphore
_requestSemaphore.Release();
}
我会遍历您的 IP 地址列表并启动 ThreadPool 工作项。
foreach(string addr in IpAddresses)
Threading.ThreadPool.QueueUserWorkItem(
(string ipaddr) =>
{
ResponseFromQuery resp = new ResponseFromQuery();
this.BeginInvoke(new MethodInvoker(() => { UpdateTable(resp); }));
}, addr);
*编辑:上面,您将需要调用BeginInvoke并创建一个methodinvoker,该调用程序在应用程序调用UpdateTable中回调新方法。您可以传入响应信息(无论它是什么类型,例如,我使用了一个编造的 ResponseFromQuery 类)。
您可以使用匿名函数,或者,如果有很多代码并且您可以在其他地方使用它,则可以创建一个处理类和方法,您可以将其作为要执行的方法传递。
如果要自己管理线程,可以创建一个字典或列表对象,并为集合中的每个项目添加一个线程:
Dictionary<string, Thread> _threads = new Dictionary<string, Thread>();
foreach (string addr in IpAddresses)
{
_threads.Add(addr, new System.Threading.Thread(
new System.Threading.ParameterizedThreadStart(
(object ip) =>
{
// process ip.
}, addr)));
_threads[addr].Start();
}