【问题标题】:How do I scrape only the <body> tag off of a website如何仅从网站上刮掉 <body> 标记
【发布时间】:2011-10-28 07:11:58
【问题描述】:

我正在开发一个网络爬虫。目前我抓取整个内容,然后使用正则表达式删除&lt;meta&gt;, &lt;script&gt;, &lt;style&gt; 和其他标签并获取正文的内容。

但是,我正在尝试优化性能,我想知道是否有一种方法可以只抓取页面的 &lt;body&gt;

namespace WebScraper
{
    public static class KrioScraper
    {    
        public static string scrapeIt(string siteToScrape)
        {
            string HTML = getHTML(siteToScrape);
            string text = stripCode(HTML);
            return text;
        }

        public static string getHTML(string siteToScrape)
        {
            string response = "";
            HttpWebResponse objResponse;
            HttpWebRequest objRequest = 
                (HttpWebRequest) WebRequest.Create(siteToScrape);
            objRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; " +
                "Windows NT 5.1; .NET CLR 1.0.3705)";
            objResponse = (HttpWebResponse) objRequest.GetResponse();
            using (StreamReader sr =
                new StreamReader(objResponse.GetResponseStream()))
            {
                response = sr.ReadToEnd();
                sr.Close();
            }
            return response;
        }

        public static string stripCode(string the_html)
        {
            // Remove google analytics code and other JS
            the_html = Regex.Replace(the_html, "<script.*?</script>", "", 
                RegexOptions.Singleline | RegexOptions.IgnoreCase);
            // Remove inline stylesheets
            the_html = Regex.Replace(the_html, "<style.*?</style>", "", 
                RegexOptions.Singleline | RegexOptions.IgnoreCase);
            // Remove HTML tags
            the_html = Regex.Replace(the_html, "</?[a-z][a-z0-9]*[^<>]*>", "");
            // Remove HTML comments
            the_html = Regex.Replace(the_html, "<!--(.|\\s)*?-->", "");
            // Remove Doctype
            the_html = Regex.Replace(the_html, "<!(.|\\s)*?>", "");
            // Remove excessive whitespace
            the_html = Regex.Replace(the_html, "[\t\r\n]", " ");

            return the_html;
        }
    }
}

我从Page_Load 调用scrapeIt() 方法,将我从页面文本框中获得的字符串传递给它。

【问题讨论】:

  • 当然可以,但我们需要查看您当前的抓取代码

标签: c# .net html-parsing web-scraping


【解决方案1】:

我建议利用 HTML Agility Pack 进行 HTML 解析/操作。

您可以像这样轻松选择主体:

var webGet = new HtmlWeb();
var document = webGet.Load(url);
document.DocumentNode.SelectSingleNode("//body")

【讨论】:

  • 嗨,Joel,感谢您抽出宝贵时间为您提供帮助。 HtmlAgilityPack 对我有什么帮助?我不是要先加载页面再解析字符串吗?
  • 敏捷包可以为您加载和解析页面。我已经更新了我的示例。自己解析 html 可能会很痛苦,尤其是如果它的格式不完美。敏捷包真的很擅长。
  • 敏捷包需要预先加载和解析页面,这会增加额外的开销。虽然这是一个简单而准确的解决方案,但它并不快速或高效。
  • 好点。您只需要对其进行测试,看看它是否对您的需求来说太慢了。
【解决方案2】:

仍然是最简单/最快(最不准确)的方法。

int start = response.IndexOf("<body", StringComparison.CurrentCultureIgnoreCase);
int end = response.LastIndexOf("</body>", StringComparison.CurrentCultureIgnoreCase);
return response.Substring(start, end-start + "</body>".Length);

显然,如果 HEAD 标记中有 javascript,例如...

document.write("<body>");

那么你最终会得到比你想要的更多的东西。

【讨论】:

  • +1 用于添加简单快速的答案以快速完成工作。不是每个人都想下载和部署框架,尤其是一次性使用。不知道为什么这被否决了。 . .
【解决方案3】:

我认为您最好的选择是使用轻量级 HTML 解析器(something like Majestic 12,根据我的测试,它比 HTML Agility Pack 快大约 50-100%)并且只处理您感兴趣的节点( &lt;body&gt;&lt;/body&gt; 之间的任何内容)。 Majestic 12 比 HTML Agility Pack 更难使用,但如果您正在寻找性能,那么它肯定会对您有所帮助!

这将使您接近您所要求的内容,但您仍然需要下载整个页面。我不认为有办法解决这个问题。您保存的内容实际上是为所有其他内容(除了正文)生成 DOM 节点。您将不得不解析它们,但您可以跳过您对处理不感兴趣的节点的全部内容。

Here is a good example of how to use the M12 parser.

我没有现成的如何抓取正文的示例,但我确实有一个如何只抓取链接并且只需稍加修改即可到达那里的示例。这是粗略的版本:

GrabBody(ParserTools.OpenM12Parser(_response.BodyBytes));

您需要打开 M12 解析器(M12 附带的示例项目具有详细说明所有这些选项如何影响性能的 cmets,而且它们确实如此!!!):

public static HTMLparser OpenM12Parser(byte[] buffer)
{
    HTMLparser parser = new HTMLparser();
    parser.SetChunkHashMode(false);
    parser.bKeepRawHTML = false;
    parser.bDecodeEntities = true;
    parser.bDecodeMiniEntities = true;

    if (!parser.bDecodeEntities && parser.bDecodeMiniEntities)
        parser.InitMiniEntities();

    parser.bAutoExtractBetweenTagsOnly = true;
    parser.bAutoKeepScripts = true;
    parser.bAutoMarkClosedTagsWithParamsAsOpen = true;
    parser.CleanUp();
    parser.Init(buffer);
    return parser;
}

解析正文:

public void GrabBody(HTMLparser parser)
{

    // parser will return us tokens called HTMLchunk -- warning DO NOT destroy it until end of parsing
    // because HTMLparser re-uses this object
    HTMLchunk chunk = null;

    // we parse until returned oChunk is null indicating we reached end of parsing
    while ((chunk = parser.ParseNext()) != null)
    {
        switch (chunk.oType)
        {
            // matched open tag, ie <a href="">
            case HTMLchunkType.OpenTag:
                if (chunk.sTag == "body")
                {
                    // Start generating the DOM node (as shown in the previous example link)
                }
                break;

            // matched close tag, ie </a>
            case HTMLchunkType.CloseTag:
                break;

            // matched normal text
            case HTMLchunkType.Text:
                break;

            // matched HTML comment, that's stuff between <!-- and -->
            case HTMLchunkType.Comment:
                break;
        };
    }
}

生成 DOM 节点很棘手,但是 Majestic12ToXml class will help you do that. 就像我说的那样,这绝不等同于您在 HTML 敏捷包中看到的 3-liner,但是一旦您掌握了工具,您将能够获得正是您所需要的性能成本的一小部分,并且可能与代码行数一样多。

【讨论】:

  • +1:很好。我不知道 Majest 12。我得去看看。
  • @Lirik:我也想看看;你说它更难,你能指出它有什么不同吗?我看不到任何在线文档或示例。
  • 谢谢 Lirik。唯一的问题是我找不到使用这个库的文档或 API。你能给我一个链接吗?
  • 如果你有机会,你能用一个抓住身体的例子来更新你的答案吗?我很想知道它是如何工作的。
  • @Johancho,不幸的是文档有点稀疏,但它确实附带了一个示例项目,向您展示了基础知识是如何工作的。查看 M12 页面上的文件:majestic12.co.uk/projects/html_parser.php 我花了大约一天的时间才弄清楚如何使用它,这非常值得花时间(注意我以前没有 HTML/DOM 解析方面的经验)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-09
  • 2015-09-02
  • 2012-03-05
  • 2019-03-01
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
相关资源
最近更新 更多