鱼C论坛

 找回密码
 立即注册
查看: 455|回复: 8

[已解决]C# 多线程 爬虫 内存持续增加

[复制链接]
发表于 2023-6-27 13:42:40 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
while (true)
            {
                string ip = obj.ToString().Split('|')[1];
                string robsn = obj.ToString().Split('|')[0];
                ChromeOptions options1 = new ChromeOptions();
                // InternetExplorerOptions internetExplorerOptions = new InternetExplorerOptions();
                // 不显示浏览器
                options1.AddArgument("--headless");
                options1.AddArgument("--disable-gpu");
                options1.AddArgument("--disable-cache");
                options1.AddArgument("--incognito");
                options1.AddArgument("--first run");
                // internetExplorerOptions.
                IWebDriver driver = new ChromeDriver(options1);   //new EdgeDriver(options1);
                try
                {
                    string url = "http://" + ip;
                    string url2 = "http://" + ip + "/doc/XPOF.html";
                    bool online = false;

                    Ping ping = new Ping();
                    PingReply pingReply = ping.Send(ip);
                    if (pingReply.Status == IPStatus.Success)
                    {
                        online = true;
                        Console.WriteLine(ip + " | 当前在线,已ping通!");
                    }
                    else
                    {
                        online = false;
                        Console.WriteLine(ip + " | 在线,ping不通!");
                    }
                    if (online)
                    {
                        driver.Navigate().GoToUrl(url);
                        var element = driver.FindElement(By.Name("password"));
                        var _element = driver.FindElement(By.Name("goto"));

                        element.SendKeys("admin");
                        _element.Click();
                        driver.Navigate().GoToUrl(url2);
                        driver.SwitchTo().Frame("XFrameTablePage");
                        var element_ = driver.FindElement(By.Name("XForm"));
                        var tbody_element = element_.FindElements(By.TagName("table"))[0].FindElements(By.TagName("tbody"))[0].FindElements(By.TagName("tr"))[0].FindElements(By.TagName("td"))[1].FindElements(By.TagName("table"))[0].FindElements(By.TagName("tbody"))[0];
                        string POF1 = tbody_element.FindElements(By.TagName("tr"))[1].FindElements(By.TagName("td"))[1].Text;
                        string POF2 = tbody_element.FindElements(By.TagName("tr"))[2].FindElements(By.TagName("td"))[1].Text;
                        string POF3 = tbody_element.FindElements(By.TagName("tr"))[3].FindElements(By.TagName("td"))[1].Text;

                        string sql;
                        string sql2 = "select * from rapidio where robsn='" + ip + "' and name in ('port2','port3','port4') order by updatetime desc";
                        DataTable dt = SqlHelper.ExecuteQuery(sql2);
                        if (dt.Rows.Count > 0)
                        {
                            for (int i = 0; i < dt.Rows.Count; i++)
                            {
                                string port = dt.Rows[i]["name"].ToString();
                                string pof = string.Empty;
                                switch (port)
                                {
                                    case "port2":
                                        pof = POF1;
                                        break;
                                    case "port3":
                                        pof = POF2;
                                        break;
                                    case "port4":
                                        pof = POF3;
                                        break;
                                }
                                if (Convert.ToDateTime(dt.Rows[i]["updatetime"]).ToString("yyyy-MM-dd") != DateTime.Now.ToString("yyyy-MM-dd"))
                                {
                                    sql = "insert into rapidio(robsn,type,name,value,updatetime) values('" + ip + "','交换机数据','" + port + "','" + pof + "','" + DateTime.Now.ToString("yyyy-MM-dd") + "')";
                                    SqlHelper.ExecuteNonQuery(sql);
                                }
                                else
                                {
                                    sql = "update rapidio set value='" + pof + "' where name = '" + port + "' and updatetime ='" + DateTime.Now.ToString("yyyy-MM-dd") + "' and robsn='" + ip + "'";
                                    SqlHelper.ExecuteNonQuery(sql);
                                }
                            }
                        }
                        else
                        {
                            sql = "insert into rapidio(robsn,type,name,value,updatetime) values('" + ip + "','交换机数据','port2','" + POF1 + "','" + DateTime.Now.ToString("yyyy-MM-dd") + "'),('" + ip + "','交换机数据','port3','" + POF2 + "','" + DateTime.Now.ToString("yyyy-MM-dd") + "'),('" + ip + "','交换机数据','port4','" + POF3 + "','" + DateTime.Now.ToString("yyyy-MM-dd") + "')";
                            SqlHelper.ExecuteNonQuery(sql);
                        }
                        string sqlcmd = "insert into exchangedata (robsn,exchangeip,name,value,updatetime) values('" + robsn + "','" + ip + "','port2','" + POF1 + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "'),('" + robsn + "','" + ip + "','port3','" + POF2 + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "'),('" + robsn + "','" + ip + "','port4','" + POF3 + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                        SqlHelper.ExecuteNonQuery(sqlcmd);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "|" + e.ToString());
                }
                options1 = null;
                CloseChromeDriver(driver);
                Thread.Sleep(sleeptime);
            }
最佳答案
2023-6-27 13:47:52
这段代码看起来在执行过程中可能会产生内存泄露的问题。在多线程环境中执行Web爬虫任务时,如果没有妥善处理对象的创建和销毁,可能会导致内存持续增加。这种现象通常是由于某些资源没有被及时释放,比如未关闭的数据库连接、未关闭的网络连接、未关闭的文件流,或者一些大对象未能及时被垃圾回收等。

具体来看,我注意到你的代码中存在一些可能的内存泄露的风险点:

1. Selenium WebDriver的使用:每个线程都会创建一个新的WebDriver实例,这可能会导致大量的内存使用。你的代码中使用了 CloseChromeDriver(driver); 这个函数,但我无法看到这个函数的定义,不确定是否正确关闭了WebDriver。如果只是简单的调用 driver.Quit() ,还不足以清理所有资源,可能需要进一步手动清理。

2. 数据库连接:从你的代码中可以看出,有大量的对数据库的操作。你使用了SqlHelper类来执行查询和非查询操作,但是没有看到与数据库连接相关的代码。如果每个操作都创建一个新的数据库连接,并且在操作完成后未能正确关闭,那么可能会导致数据库连接资源的泄露,从而导致内存持续增加。

3. 多线程操作:在你的代码中,你的爬虫任务在一个无限循环中执行,这意味着线程可能永远不会结束。你需要确保这些线程在完成任务后能够正确结束,否则这些线程及其关联的资源可能永远不会被清理。

4. Ping类和PingReply对象:你的代码创建了Ping和PingReply对象,但没有显式地释放这些对象。在.Net中,虽然有垃圾回收机制,但是对于实现了IDisposable接口的对象,你仍需要手动调用Dispose方法来释放资源。

综上所述,你需要检查一下上述可能导致内存泄露的地方,并且需要注意及时释放不再使用的资源。如果问题依然存在,你可能需要使用一些诊断工具,比如Visual Studio的性能分析工具,来查看内存使用的详细信息,以帮助你定位问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-6-27 13:44:11 | 显示全部楼层
你的问题是什么
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-27 13:47:52 | 显示全部楼层    本楼为最佳答案   
这段代码看起来在执行过程中可能会产生内存泄露的问题。在多线程环境中执行Web爬虫任务时,如果没有妥善处理对象的创建和销毁,可能会导致内存持续增加。这种现象通常是由于某些资源没有被及时释放,比如未关闭的数据库连接、未关闭的网络连接、未关闭的文件流,或者一些大对象未能及时被垃圾回收等。

具体来看,我注意到你的代码中存在一些可能的内存泄露的风险点:

1. Selenium WebDriver的使用:每个线程都会创建一个新的WebDriver实例,这可能会导致大量的内存使用。你的代码中使用了 CloseChromeDriver(driver); 这个函数,但我无法看到这个函数的定义,不确定是否正确关闭了WebDriver。如果只是简单的调用 driver.Quit() ,还不足以清理所有资源,可能需要进一步手动清理。

2. 数据库连接:从你的代码中可以看出,有大量的对数据库的操作。你使用了SqlHelper类来执行查询和非查询操作,但是没有看到与数据库连接相关的代码。如果每个操作都创建一个新的数据库连接,并且在操作完成后未能正确关闭,那么可能会导致数据库连接资源的泄露,从而导致内存持续增加。

3. 多线程操作:在你的代码中,你的爬虫任务在一个无限循环中执行,这意味着线程可能永远不会结束。你需要确保这些线程在完成任务后能够正确结束,否则这些线程及其关联的资源可能永远不会被清理。

4. Ping类和PingReply对象:你的代码创建了Ping和PingReply对象,但没有显式地释放这些对象。在.Net中,虽然有垃圾回收机制,但是对于实现了IDisposable接口的对象,你仍需要手动调用Dispose方法来释放资源。

综上所述,你需要检查一下上述可能导致内存泄露的地方,并且需要注意及时释放不再使用的资源。如果问题依然存在,你可能需要使用一些诊断工具,比如Visual Studio的性能分析工具,来查看内存使用的详细信息,以帮助你定位问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-27 13:48:09 | 显示全部楼层
这段代码主要有以下几个问题:

  • 1. 硬编码的URL和XPATH,不够灵活。如果交换机界面变化,代码需要修改。
  • 2. 直接执行SQL语句,存在SQL注入风险。应使用参数化查询或者ORM框架。
  • 3. 定时循环可能会导致大量请求,需要控制频率。
  • 4. ChromeDriver存在内存泄漏的风险,需要及时关闭。
  • 5. 未处理各种异常,需要加强错误处理。
  • 6. 可以考虑将读取的数据封装为对象,而不是简单的字符串,便于以后扩展。
  • 7. 可以将浏览器驱动作为依赖注入,以便替换成其他浏览器,增加灵活性。

改进方法如下:

  • 1. 将URL和XPATH提取为配置,支持动态变化。
  • 2. 使用参数化查询或者ORM框架,避免SQL注入风险。
  • 3. 加入频率控制,如每5-10分钟采集一次数据。
  • 4. 使用using语句关闭ChromeDriver,确保及时释放资源。
  • 5. 增加try/catch语句,捕获各种异常并记录日志。
  • 6. 可以定义POFValue对象来封装端口数据。
  • 7. 可以定义IDriver接口,ChromeDriver实现该接口,以支持多种浏览器驱动的替换。

例如:
// 频率控制
while (true) 
{
    if (DateTime.Now - lastTime > TimeSpan.FromMinutes(5)) {
        lastTime = DateTime.Now;
        // 逻辑...
    }
    Thread.Sleep(sleeptime); 
}  

// ORM查询 
List<POFValue> list = DbContext.POFValue.Where(x => x.Robsn == robsn && x.Date == DateTime.Today).ToList();

// 中 using 语句关闭ChromeDriver
using (IWebDriver driver = new ChromeDriver(options)) 
{
    ...
}  

// 错误处理
try 
{
    // 逻辑...
}
catch (Exception ex)
{
    logger.Error(ex);
}
这些改进可以大大提高代码的健壮性和可维护性。让逻辑更加清晰合理。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-27 13:53:08 | 显示全部楼层
该段代码是一个简单的爬虫程序,用于抓取网页上的数据并存入数据库。根据代码逻辑分析,导致内存持续增加的原因可能是在循环中创建了多个WebDriver对象,但没有及时释放资源。

解决办法是在每次循环结束时通过调用driver.Quit()方法显式关闭WebDriver对象,释放相关资源。修改后的代码如下:
while (true)
{
    string ip = obj.ToString().Split('|')[1];
    string robsn = obj.ToString().Split('|')[0];
    ChromeOptions options1 = new ChromeOptions();
    // 不显示浏览器
    options1.AddArgument("--headless");
    options1.AddArgument("--disable-gpu");
    options1.AddArgument("--disable-cache");
    options1.AddArgument("--incognito");
    options1.AddArgument("--first run");

    using (IWebDriver driver = new ChromeDriver(options1))
    {
        try
        {
            // 省略其他代码
            // ...

            // 在这里进行数据抓取和处理

        }
        finally
        {
            driver.Quit(); // 释放WebDriver对象
        }
    }

    // 其他代码
    // ...
}
请注意,在使用完WebDriver对象后应始终调用Quit()方法来确保资源被正确释放,避免内存持续增加的问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-6-27 14:05:51 | 显示全部楼层
好的  谢谢  我现在就去测试一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-6-27 14:12:29 | 显示全部楼层
GL_YC 发表于 2023-6-27 14:05
好的  谢谢  我现在就去测试一下

可以给我最佳答案呗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-6-27 14:14:29 | 显示全部楼层
#region 异常 退出chromedriver
        [DllImport("user32.dll", EntryPoint = "FindWindow")] private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", EntryPoint = "SendMessage")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        public const int SW_HIDE = 0; public const int SW_SHOW = 5;
        [DllImport("user32.dll", EntryPoint = "ShowWindow")] public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
        /// <summary>
        /// 获取窗口句柄
        /// </summary>
        /// <returns></returns>
        public static IntPtr GetWindowHandle()
        {
            string name = (Environment.CurrentDirectory + "\\chromedriver.exe");
            IntPtr hwd = FindWindow(null, name);
            return hwd;
        }
        /// <summary>
        /// 关闭chromedriver窗口
        /// </summary>
        public static void CloseWindow()
        {
            try
            {
                IntPtr hwd = GetWindowHandle();
                SendMessage(hwd, 0x10, 0, 0);
            }
            catch { }
        }
        /// <summary>
        /// 退出chromedriver
        /// </summary>
        /// <param name="driver"></param>
        public static void CloseChromeDriver(IWebDriver driver)
        {
            try
            {
                driver.Close();
                driver.Quit();
                driver.Dispose();
            }
            catch { }
            CloseWindow();
            FlushMemory();
        }

每个线程执行完  都会关闭webdriver
private static void FlushMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
        }

这是垃圾回收

using (MySqlConnection mysqlconn = new MySqlConnection(connstr))
            {
                mysqlconn.Open();
                MySqlCommand cmd = new MySqlCommand(sqlstr, mysqlconn);
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                mysqlconn.Close();
                mysqlconn.Dispose();
            }

这是执行sql语句的方法
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-6-27 14:20:39 | 显示全部楼层
#region 异常 退出chromedriver
        [DllImport("user32.dll", EntryPoint = "FindWindow")] private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", EntryPoint = "SendMessage")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        public const int SW_HIDE = 0; public const int SW_SHOW = 5;
        [DllImport("user32.dll", EntryPoint = "ShowWindow")] public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
        /// <summary>
        /// 获取窗口句柄
        /// </summary>
        /// <returns></returns>
        public static IntPtr GetWindowHandle()
        {
            string name = (Environment.CurrentDirectory + "\\chromedriver.exe");
            IntPtr hwd = FindWindow(null, name);
            return hwd;
        }
        /// <summary>
        /// 关闭chromedriver窗口
        /// </summary>
        public static void CloseWindow()
        {
            try
            {
                IntPtr hwd = GetWindowHandle();
                SendMessage(hwd, 0x10, 0, 0);
            }
            catch { }
        }
        /// <summary>
        /// 退出chromedriver
        /// </summary>
        /// <param name="driver"></param>
        public static void CloseChromeDriver(IWebDriver driver)
        {
            try
            {
                driver.Close();
                driver.Quit();
                driver.Dispose();
            }
            catch { }
            CloseWindow();
            FlushMemory();
        }

每个线程执行完  都会关闭webdriver
private static void FlushMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);
        }

这是垃圾回收

using (MySqlConnection mysqlconn = new MySqlConnection(connstr))
            {
                mysqlconn.Open();
                MySqlCommand cmd = new MySqlCommand(sqlstr, mysqlconn);
                cmd.ExecuteNonQuery();
                cmd.Dispose();
                mysqlconn.Close();
                mysqlconn.Dispose();
            }

这是执行sql语句的方法
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-6-26 14:01

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表