【问题标题】:Web mining or scraping or crawling? What tool/library should I use? [closed]网络挖掘或抓取或爬行?我应该使用什么工具/库? [关闭]
【发布时间】:2011-12-05 02:11:15
【问题描述】:

我想抓取一些网页并将其保存为 HTML。比如说,爬入数百个受欢迎的网站,然后简单地保存它们的首页和“关于”页面。

我研究了很多问题,但没有从网络抓取或网络抓取问题中找到答案。

我应该使用什么库或工具来构建解决方案?或者是否有一些现有的工具可以处理这个问题?

【问题讨论】:

  • 如何识别“关于”页面?
  • 比方说,只需在 url 中匹配“about”的正则表达式。如果匹配,则保存它,如果不匹配,则保留它。假阴性没问题。
  • @Flake 为了安全起见,文本中带有 about 的 url 并且是内部链接的。当然,有些人可能只有“公司信息”之类的链接,而没有“关于”这个词。

标签: java python web-crawler web-scraping web-mining


【解决方案1】:

使用 Python 时,您可能会对 mechanize 和 BeautifulSoup 感兴趣。

Mechanize 有点模拟浏览器(包括代理选项、伪造浏览器标识、页面重定向等),并允许轻松获取表单、链接……文档有点粗糙/虽然稀疏。

一些示例代码(来自 mechanize 网站)给你一个想法:

import mechanize
br = mechanize.Browser()
br.open("http://www.example.com/")
# follow second link with element text matching regular expression
html_response = br.follow_link(text_regex=r"cheese\s*shop", nr=1)
print br.title()
print  html_response

BeautifulSoup 可以非常轻松地解析 html 内容(您可以使用 mechanize 获取),并且支持正则表达式。

一些示例代码:

from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup(html_response)

rows = soup.findAll('tr')
for r in rows[2:]:  #ignore first two rows
    cols = r.findAll('td')
    print cols[0].renderContents().strip()    #print content of first column

所以,上面这 10 行几乎是复制粘贴准备打印网站上每个表格行的第一列的内容。

【讨论】:

    【解决方案2】:

    这里确实没有好的解决方案。您是对的,因为您怀疑 Python 可能是最好的入门方式,因为它对正则表达式的支持非常强大。

    为了实现这样的事情,对 SEO(搜索引擎优化)的深入了解会有所帮助,因为有效地为搜索引擎优化网页会告诉您搜索引擎的行为方式。我会从SEOMoz 这样的网站开始。

    就识别“关于我们”页面而言,您只有两个选择:

    a) 对于每个页面,获取关于我们页面的链接并将其提供给您的爬虫。

    b) 解析页面的所有链接,查找某些关键字,例如“关于我们”、“关于”、“了解更多”等。

    在使用选项 b 时,请小心,因为您可能会陷入无限循环,因为网站会多次链接到同一个页面,特别是如果链接位于页眉或页脚中,则页面甚至可能链接回自身。为避免这种情况,您需要创建已访问链接的列表并确保不再访问它们。

    最后,我建议让您的爬虫尊重robot.txt 文件中的说明,并且最好不要关注标记为rel="nofollow" 的链接,因为这些链接主要用于外部链接。同样,通过阅读 SEO 来了解这一点以及更多信息。

    问候,

    【讨论】:

      【解决方案3】:

      试试scrapy。它是一个用于 python 的网络抓取库。 如果需要一个简单的 python 脚本,请在 python 中尝试urllib2。

      【讨论】:

        【解决方案4】:

        Python ==> Curl

        下面的代码可以在一个不错的服务器上在 300 秒内爬取 10,000 个页面。

        #! /usr/bin/env python
        # -*- coding: iso-8859-1 -*-
        # vi:ts=4:et
        # $Id: retriever-multi.py,v 1.29 2005/07/28 11:04:13 mfx Exp $
        
        #
        # Usage: python retriever-multi.py <file with URLs to fetch> [<# of
        #          concurrent connections>]
        #
        
        import sys
        import pycurl
        
        # We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
        # the libcurl tutorial for more info.
        try:
            import signal
            from signal import SIGPIPE, SIG_IGN
            signal.signal(signal.SIGPIPE, signal.SIG_IGN)
        except ImportError:
            pass
        
        
        # Get args
        num_conn = 10
        try:
            if sys.argv[1] == "-":
                urls = sys.stdin.readlines()
            else:
                urls = open(sys.argv[1]).readlines()
            if len(sys.argv) >= 3:
                num_conn = int(sys.argv[2])
        except:
            print "Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0]
            raise SystemExit
        
        
        # Make a queue with (url, filename) tuples
        queue = []
        for url in urls:
            url = url.strip()
            if not url or url[0] == "#":
                continue
            filename = "doc_%03d.dat" % (len(queue) + 1)
            queue.append((url, filename))
        
        
        # Check args
        assert queue, "no URLs given"
        num_urls = len(queue)
        num_conn = min(num_conn, num_urls)
        assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
        print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
        print "----- Getting", num_urls, "URLs using", num_conn, "connections -----"
        
        
        # Pre-allocate a list of curl objects
        m = pycurl.CurlMulti()
        m.handles = []
        for i in range(num_conn):
            c = pycurl.Curl()
            c.fp = None
            c.setopt(pycurl.FOLLOWLOCATION, 1)
            c.setopt(pycurl.MAXREDIRS, 5)
            c.setopt(pycurl.CONNECTTIMEOUT, 30)
            c.setopt(pycurl.TIMEOUT, 300)
            c.setopt(pycurl.NOSIGNAL, 1)
            m.handles.append(c)
        
        
        # Main loop
        freelist = m.handles[:]
        num_processed = 0
        while num_processed < num_urls:
            # If there is an url to process and a free curl object, add to multi stack
            while queue and freelist:
                url, filename = queue.pop(0)
                c = freelist.pop()
                c.fp = open(filename, "wb")
                c.setopt(pycurl.URL, url)
                c.setopt(pycurl.WRITEDATA, c.fp)
                m.add_handle(c)
                # store some info
                c.filename = filename
                c.url = url
            # Run the internal curl state machine for the multi stack
            while 1:
                ret, num_handles = m.perform()
                if ret != pycurl.E_CALL_MULTI_PERFORM:
                    break
            # Check for curl objects which have terminated, and add them to the freelist
            while 1:
                num_q, ok_list, err_list = m.info_read()
                for c in ok_list:
                    c.fp.close()
                    c.fp = None
                    m.remove_handle(c)
                    print "Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)
                    freelist.append(c)
                for c, errno, errmsg in err_list:
                    c.fp.close()
                    c.fp = None
                    m.remove_handle(c)
                    print "Failed: ", c.filename, c.url, errno, errmsg
                    freelist.append(c)
                num_processed = num_processed + len(ok_list) + len(err_list)
                if num_q == 0:
                    break
            # Currently no more I/O is pending, could do something in the meantime
            # (display a progress bar, etc.).
            # We just call select() to sleep until some more data is available.
            m.select(1.0)
        
        
        # Cleanup
        for c in m.handles:
            if c.fp is not None:
                c.fp.close()
                c.fp = None
            c.close()
        m.close()
        

        【讨论】:

          【解决方案5】:

          如果您要构建一个爬虫,您需要(Java 特定):

          1. 了解如何使用 java.net.URL 和 java.net.URLConnection 类或使用 HttpClient 库
          2. 了解 http 请求/响应标头
          3. 了解重定向(HTTP、HTML 和 Javascript)
          4. 了解内容编码(字符集)
          5. 使用优秀的库来解析格式错误的 HTML(例如,cyberNecko、Jericho、JSoup)
          6. 向不同主机发出并发 HTTP 请求,但确保每 ~ 5 秒向同一主机发出不超过一个
          7. 保留您已提取的页面,因此您无需每天重新提取它们 不要经常更改(HBase 很有用)。
          8. 一种从当前页面中提取链接到下一个抓取的方法
          9. 遵守 robots.txt

          还有一堆其他的东西。

          这并不难,但有很多复杂的边缘情况(例如重定向、检测编码(结帐 Tika))。

          对于更多基本要求,您可以使用 wget。 Heretrix 是另一种选择,但又是一个需要学习的框架。

          可以使用各种启发式方法来识别关于我们的页面:

          1. 入站链接文本
          2. 页面标题
          3. 页面内容
          4. 网址

          如果您想更量化地了解它,您可以使用机器学习和分类器(也许是贝叶斯)。

          保存首页显然更容易,但首页重定向(有时到不同的域,通常在 HTML 元重定向标签甚至 JS 中实现)非常常见,因此您需要处理此问题。

          【讨论】:

          • 嗨乔尔,感谢您的有趣回复。您能否通过我的个人资料与我联系以进一步讨论网络爬虫,我有兴趣与您进行更深入的对话(如果您感兴趣,最终会雇用您)。谢谢你:)
          【解决方案6】:

          Heritrix 的学习曲线有点陡峭,但可以配置为只抓取主页和“看起来像”(使用正则表达式过滤器)关于页面的页面。

          更多开源Java(网络)爬虫:http://java-source.net/open-source/crawlers

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-04
            • 1970-01-01
            • 2016-11-28
            相关资源
            最近更新 更多