RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1413707
Accepted
Komend
Komend
Asked:2022-07-25 22:52:26 +0000 UTC2022-07-25 22:52:26 +0000 UTC 2022-07-25 22:52:26 +0000 UTC

c# httpwebrequest 大页面解析优化

  • 772

像往常一样,我会说可能有类似的主题。但是我没有找到它,或者我用谷歌搜索得很糟糕。我尝试了我找到的所有东西,但我需要专门的提示或帮助。

我需要优化页面的输出及其处理(例如,查找单词并记下数字或从页面中获取数据)。但是页面是非常大的屏幕: 在此处输入图像描述

我不知道,也许对于一个很小的人来说,我在 2 秒内处理它,有时在 5 或 10 秒内。但它可以更快吗?并在一般情况下优化输出和处理。将来,我想做一个多线程模式,即解析几个站点。但它们也一样大。而且我不知道如何快速处理页面,或者如何获得更小的响应,但是数据会更少,我将无法解析所有内容。

现在我有List<string>特定的单词,我检查给定页面上这些单词的数量。据我了解,如果我将这样的页面写入字符串,它就会存储在程序内存中。

这是代码:控制台应用程序。主类Program调用ServiesCheck构造函数,该构造函数接受设置(作为类的集合,1 个类 - 设置)。

new ServiesCheck(settingslist);

构造函数ServiesCheck遍历设置(引用)并调用方法在构造函数中,我调用方法:ControlSettingsServies()

    private bool ControllSettingsServies(ServiesSettings setting, CookieContainer containerCookies)
        {

            if (setting.oneChecked)
            {
                var onecheckerservies = OneCheckServies(setting.settingsCheck, containerCookies, setting.linkServis);
                if (onecheckerservies.isValid)
                {
                    Console.WriteLine("Нашел");
                    return true;
                }
                else
                {
                    Console.WriteLine("Не нашел");
                    return false;
                }
            }
            return false;
        }

在该方法中,我调用 OneCheckServies(它接受我准备好的 cookie、我将处理输出的设置以及要遵循的链接)

     private (bool isValid, int[] countSearch, string ResHtml) OneCheckServies(ServiesSettingCheck settingsCheck, CookieContainer containercookies, string link)s
        {
            var startTime = System.Diagnostics.Stopwatch.StartNew();

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(link);
            request.CookieContainer = containercookies;
            request.AutomaticDecompression = DecompressionMethods.GZip;
            //request.Proxy = null;

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            String setCookieHeader = response.Headers[HttpResponseHeader.SetCookie];

            bool isSerach = false;
            string responceTextHtml = "";
            int[] countSerchList = new int[settingsCheck.worldSerach.Count];

            using (Stream stream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    string line = "";
                    while ((line = reader.ReadLine()) != null) {
                        responceTextHtml += line + "\n";
                        foreach (string worldS in settingsCheck.worldSerach) {
                            if (countSerchList[settingsCheck.worldSerach.IndexOf(worldS)] <= settingsCheck.closeFoundNum) {
                                if (line.Contains(worldS)) {
                                    countSerchList[settingsCheck.worldSerach.IndexOf(worldS)] += 1;
                                }
                            }
                        }
                    }
                }
            }
            for (int i = 0; i < countSerchList.Length; i++) {
                if (countSerchList[i] > settingsCheck.closeFoundNum) {
                    isSerach = true;
                }else {
                    isSerach = false;
                    break;
                }
            }

            startTime.Stop();
            var resultTime = startTime.Elapsed;
            Console.WriteLine(String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
                resultTime.Hours,
                resultTime.Minutes,
                resultTime.Seconds,
                resultTime.Milliseconds));

            var result = (isSerach, countSerchList, responceTextHtml);
            response.Close();
            return result;
        }

代码对你来说可能很糟糕,但我不能这样做,而且我不知道。

  1. 如何优化输出?
  2. 如何优化搜索?
  3. 如果需要,还可以进行内存优化。 或者是边际速度(从 2 到 10 秒)?

将来,我需要使用这个找到的页面进行进一步的操作。

c# .net
  • 1 1 个回答
  • 84 Views

1 个回答

  • Voted
  1. Best Answer
    aepot
    2022-07-26T00:39:05Z2022-07-26T00:39:05Z

    为了使一切快速运行,您需要使用优化良好的 .NET 6 而HttpClient不是长期弃用的HttpWebRequest.

    我将编写一个基于HtmlAgilityPack. 解析器的意义将是通过引用统计页面上的字数,并在控制台中显示统计结果。

    class HttpParser : IDisposable
    {
        private readonly HttpClient client;
        private readonly char[] splitChars = new char[] { ' ', '\t', '\r', '\n' };
    
        public HttpParser(CookieContainer cookies = null, WebProxy proxy = null)
        {
            var handler = new HttpClientHandler
            {
                CookieContainer = cookies ?? new CookieContainer(),
                AutomaticDecompression = DecompressionMethods.All,
                Proxy = proxy
            };
            client = new HttpClient(handler)
            {
                DefaultRequestVersion = HttpVersion.Version20
            };
            client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0"); // пример добавления HTTP заголовка по умолчанию
        }
        public async Task<Dictionary<string, int>> ParseLink(string url, int minLength = 1, int maxLength = 30)
        {
            string html = await client.GetStringAsync(url);
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            Dictionary<string, int> words = new Dictionary<string, int>();
            // Descendants - полный список нод документа в виде единого списка
            // из них выбираю только текстовые ноды
            // и зачищаю каждую от эскейп-кодов и HTML-Entity, меня вполне устраивает чистый юникод
            foreach (string nodeText in doc.DocumentNode
                .Descendants()
                .Where(n => n.NodeType == HtmlNodeType.Text)
                .Select(n => HtmlEntity.DeEntitize(Regex.Unescape(n.InnerText))))
            {
                // бью текст на слова по пустым местам, можно еще Regex.Split использовать для этого
                // далее фильтрую все что не попадает под мои условия выбора
                foreach (string word in nodeText
                    .Split(splitChars, StringSplitOptions.RemoveEmptyEntries)
                    .Select(text => text.Trim().ToLower())
                    .Where(t => t.Length >= minLength && t.Length <= maxLength))
                {
                    // заношу в словарь
                    words[word] = words.TryGetValue(word, out int count) ? count + 1 : 1;
                }
            }
            return words;
        }
    
        public void Dispose()
        {
            client.Dispose();
        }
    }
    

    这就是我使用的方式

    static async Task Main(string[] args)
    {
        using HttpParser parser = new HttpParser();
        var sw = Stopwatch.StartNew();
        var words = await parser.ParseLink("https://ru.wikipedia.org/wiki/HTTP", 5);
        Console.WriteLine(sw.Elapsed.ToString(@"hh\:mm\:ss\.fff"));
        foreach (var pair in words.OrderByDescending(x => x.Value).Take(20)) // возьму топ-20 слов, чтобы вывод не был огромным
        {
            Console.WriteLine($"[{pair.Key}] = {pair.Value}");
        }
    }
    

    页面的重量是 278 KB,根据它在 0.335 秒内加载的浏览器。

    这就是我得到的

    00:00:00.387
    [править] = 70
    [может] = 31
    [сервер] = 31
    [сообщения] = 22
    [только] = 21
    [заголовки] = 19
    [заголовок] = 19
    [ответа] = 19
    [также] = 18
    [сервера] = 18
    [ответ] = 18
    [англ.] = 17
    [содержимое] = 17
    [клиент] = 17
    [запрос] = 17
    [http/1.1] = 16
    [могут] = 15
    [запроса] = 15
    [используется] = 14
    [версия] = 13
    

    也就是说,所有的解析工作大概花费了大约 50 毫秒。好吧,您的页面要大得多,但我相信它应该仍然可以在不到半秒的时间内完成。

    从结果字典中,您已经可以选择您需要的单词。当然,您可以修改方法本身并在采样阶段过滤掉不必要的内容。我只是想展示这项技术。

    当然,您可以使用它仅从文档中获取文本doc.DocumentNode.InnerText,但是任何动态对象(其文本可以隐藏在 JavaScript 块或 CSS 样式中的某处)都不会落入选择范围内。此外,部分处理文档可以让您不必在操作中创建大型对象,因此可以保存。但是您可以尝试删除外部foreach并替换nodeText为doc.DocumentNode.InnerText

    顺便说一下,让我们试试

    public async Task<Dictionary<string, int>> ParseLink(string url, int minLength = 1, int maxLength = 30)
    {
        string html = await client.GetStringAsync(url);
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(html);
        Dictionary<string, int> words = new Dictionary<string, int>();
    
        foreach (string word in HtmlEntity.DeEntitize(Regex.Unescape(doc.DocumentNode.InnerText))
            .Split(splitChars, StringSplitOptions.RemoveEmptyEntries)
            .Select(text => text.Trim().ToLower())
            .Where(t => t.Length >= minLength && t.Length <= maxLength))
        {
            words[word] = words.TryGetValue(word, out int count) ? count + 1 : 1;
        }
        return words;
    }
    

    控制台输出

    00:00:00.394
    [править] = 35
    [сервер] = 31
    [может] = 30
    [сообщения] = 21
    [только] = 21
    [заголовок] = 19
    [ответ] = 18
    [заголовки] = 17
    [также] = 17
    [клиент] = 17
    [сервера] = 17
    [ответа] = 17
    [запрос] = 17
    [содержимое] = 16
    [могут] = 15
    [запроса] = 15
    [используется] = 14
    [http/1.1] = 14
    [основные] = 13
    [метод] = 13
    

    数字略有不同,但您决定最适合您的数字。从逻辑上讲,我更喜欢最后一个选项,而且更简单。

    关于实例的创建HttpParser,您必须为每组 cookie 和每个代理创建自己的解析器。不要忘记在不再需要创建的实例时释放它们。

    要在没有代理和 cookie 的情况下工作,一个解析器就足以处理所有请求,即使您想多线程执行它们也是如此。顺便说一句,您可以在此处阅读有关异步多线程的信息。

    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5