多线程控制台应用中的 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);
    }

任何帮助将不胜感激。

我没有使用多线程,但用谷歌搜索了这个主题并得到了一些想法,只是不确定如何最好地设置我的场景。

多线程控制台应用中的 Web 爬网

不要使用线程。使用异步 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();
}