【问题标题】:Anyone know of a good Python based web crawler that I could use?有人知道我可以使用的基于 Python 的优秀网络爬虫吗?
【发布时间】:2010-09-29 23:21:46
【问题描述】:

我有点想自己写,但我现在真的没有足够的时间。我已经看过open source crawlers 的维基百科列表,但我更喜欢用 Python 编写的东西。我意识到我可能只使用 Wikipedia 页面上的一种工具并将其包装在 Python 中。我最终可能会这样做 - 如果有人对这些工具有任何建议,我愿意听取他们的意见。我通过它的网络界面使用了 Heritrix,我发现它非常麻烦。我绝对不会在即将到来的项目中使用浏览器 API。

提前致谢。另外,这是我的第一个 SO 问题!

【问题讨论】:

标签: python web-crawler


【解决方案1】:
  • Mechanize 是我的最爱;强大的高级浏览功能(超级简单的表单填写和提交)。
  • Twill 是一种建立在 Mechanize 之上的简单脚本语言
  • BeautifulSoup + urllib2 也很好用。
  • Scrapy 看起来是一个非常有前途的项目;这是新的。

【讨论】:

  • 将 urrlib2 添加到 Beautiful Soup 中,您就有了很好的工具组合。
  • 那些库可以用来爬虫,但它们本身不是爬虫
  • 例如,使用scrapy,为抓取创建一套规则真的很简单。其他的没试过,但是 Scrapy 确实是一段不错的代码。
  • @RexE,关于如何使用 Mechanize 从特定网页收集数据的任何建议,或者关于如何使用 Mechanize 做一些实际工作的任何示例,而不仅仅是演示?提前致谢。
【解决方案2】:

使用Scrapy

它是一个基于twisted的网络爬虫框架。仍在大力开发中,但它已经可以工作了。有很多好东西:

  • 内置支持解析 HTML、XML、CSV 和 Javascript
  • 一种媒体管道,用于使用图像(或任何其他媒体)抓取项目并下载图像文件
  • 支持通过使用中间件、扩展和管道插入您自己的功能来扩展 Scrapy
  • 广泛的内置中间件和扩展,用于处理压缩、缓存、cookie、身份验证、用户代理欺骗、robots.txt 处理、统计、抓取深度限制等
  • 交互式抓取shell控制台,对开发和调试非常有用
  • 用于监视和控制机器人的 Web 管理控制台
  • 用于对 Scrapy 进程进行低级访问的 Telnet 控制台

通过在返回的 HTML 上使用 XPath 选择器来提取有关今天添加到 mininova torrent 站点中的所有 torrent 文件信息的示例代码:

class Torrent(ScrapedItem):
    pass

class MininovaSpider(CrawlSpider):
    domain_name = 'mininova.org'
    start_urls = ['http://www.mininova.org/today']
    rules = [Rule(RegexLinkExtractor(allow=['/tor/\d+']), 'parse_torrent')]

    def parse_torrent(self, response):
        x = HtmlXPathSelector(response)
        torrent = Torrent()

        torrent.url = response.url
        torrent.name = x.x("//h1/text()").extract()
        torrent.description = x.x("//div[@id='description']").extract()
        torrent.size = x.x("//div[@id='info-left']/p[2]/text()[2]").extract()
        return [torrent]

【讨论】:

    【解决方案3】:

    查看HarvestMan,一个用Python编写的多线程网络爬虫,也看看spider.py模块。

    here 你可以找到代码示例来构建一个简单的网络爬虫。

    【讨论】:

      【解决方案4】:

      我用过Ruya,感觉还不错。

      【讨论】:

      • Ruya好像不能下载了?我在任何地方都找不到他们的压缩包。
      【解决方案5】:

      我破解了上面的脚本以包含一个登录页面,因为我需要它来访问一个 drupal 站点。不漂亮,但可以帮助那里的人。

      #!/usr/bin/python
      
      import httplib2
      import urllib
      import urllib2
      from cookielib import CookieJar
      import sys
      import re
      from HTMLParser import HTMLParser
      
      class miniHTMLParser( HTMLParser ):
      
        viewedQueue = []
        instQueue = []
        headers = {}
        opener = ""
      
        def get_next_link( self ):
          if self.instQueue == []:
            return ''
          else:
            return self.instQueue.pop(0)
      
      
        def gethtmlfile( self, site, page ):
          try:
              url = 'http://'+site+''+page
              response = self.opener.open(url)
              return response.read()
          except Exception, err:
              print " Error retrieving: "+page
              sys.stderr.write('ERROR: %s\n' % str(err))
          return "" 
      
          return resppage
      
        def loginSite( self, site_url ):
          try:
          cj = CookieJar()
          self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
      
          url = 'http://'+site_url 
              params = {'name': 'customer_admin', 'pass': 'customer_admin123', 'opt': 'Log in', 'form_build_id': 'form-3560fb42948a06b01d063de48aa216ab', 'form_id':'user_login_block'}
          user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
          self.headers = { 'User-Agent' : user_agent }
      
          data = urllib.urlencode(params)
          response = self.opener.open(url, data)
          print "Logged in"
          return response.read() 
      
          except Exception, err:
          print " Error logging in"
          sys.stderr.write('ERROR: %s\n' % str(err))
      
          return 1
      
        def handle_starttag( self, tag, attrs ):
          if tag == 'a':
            newstr = str(attrs[0][1])
            print newstr
            if re.search('http', newstr) == None:
              if re.search('mailto', newstr) == None:
                if re.search('#', newstr) == None:
                  if (newstr in self.viewedQueue) == False:
                    print "  adding", newstr
                    self.instQueue.append( newstr )
                    self.viewedQueue.append( newstr )
                else:
                  print "  ignoring", newstr
              else:
                print "  ignoring", newstr
            else:
              print "  ignoring", newstr
      
      
      def main():
      
        if len(sys.argv)!=3:
          print "usage is ./minispider.py site link"
          sys.exit(2)
      
        mySpider = miniHTMLParser()
      
        site = sys.argv[1]
        link = sys.argv[2]
      
        url_login_link = site+"/node?destination=node"
        print "\nLogging in", url_login_link
        x = mySpider.loginSite( url_login_link )
      
        while link != '':
      
          print "\nChecking link ", link
      
          # Get the file from the site and link
          retfile = mySpider.gethtmlfile( site, link )
      
          # Feed the file into the HTML parser
          mySpider.feed(retfile)
      
          # Search the retfile here
      
          # Get the next link in level traversal order
          link = mySpider.get_next_link()
      
        mySpider.close()
      
        print "\ndone\n"
      
      if __name__ == "__main__":
        main()
      

      【讨论】:

        【解决方案6】:

        相信我,没有什么比 curl 更好的了……以下代码可以在不到 300 秒的时间内在 Amazon EC2 上并行抓取 10,000 个 url

        注意: 不要以如此高的速度访问同一个域.. .

        #! /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()
        

        【讨论】:

          【解决方案7】:

          另一个simple spider 使用 BeautifulSoup 和 urllib2。没什么太复杂的,只是读取所有的 href 构建一个列表并通过它。

          【讨论】:

            【解决方案8】:

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-03-29
              • 1970-01-01
              相关资源
              最近更新 更多