【问题标题】:Creating a generic scrapy spider创建一个通用的爬虫蜘蛛
【发布时间】:2012-04-06 13:47:06
【问题描述】:

我的问题实际上是如何做与上一个问题相同的事情,但在 Scrapy 0.14 中。

Using one Scrapy spider for several websites

基本上,我有一个 GUI,它接受域、关键字、标签名称等参数,我想创建一个通用爬虫来为这些标签中的关键字抓取这些域。我通过覆盖蜘蛛管理器类或动态创建蜘蛛来阅读冲突的东西,使用旧版本的scrapy。首选哪种方法以及如何实施和调用正确的解决方案?提前致谢。

这是我想要通用的代码。它还使用 BeautifulSoup。我把它配对了,所以希望没有删除任何对理解它至关重要的东西。

class MySpider(CrawlSpider):

name = 'MySpider'
allowed_domains = ['somedomain.com', 'sub.somedomain.com']
start_urls = ['http://www.somedomain.com']

rules = (
    Rule(SgmlLinkExtractor(allow=('/pages/', ), deny=('', ))),

    Rule(SgmlLinkExtractor(allow=('/2012/03/')), callback='parse_item'),
)

def parse_item(self, response):
    contentTags = []

    soup = BeautifulSoup(response.body)

    contentTags = soup.findAll('p', itemprop="myProp")

    for contentTag in contentTags:
        matchedResult = re.search('Keyword1|Keyword2', contentTag.text)
        if matchedResult:
            print('URL Found: ' + response.url)

    pass

【问题讨论】:

  • 能否展示用于域、关键字、标签的固定值的代码?
  • 已添加代码。它使用 BeautifulSoup 来解析 html。
  • 嘿,朋友别太懒了。

标签: python scrapy web-crawler


【解决方案1】:

我不确定哪种方式更受欢迎,但我会告诉你我过去做过什么。我不确定这是最好的(或正确的)方法,我很想了解其他人的想法。

我通常只是重写父类 (CrawlSpider) 并传入参数,然后在我自己的 init 函数中通过 super(MySpider, self).__init__() 初始化父类我拉入该数据从我保存了要附加到 start_urls 之前的链接列表的数据库中。

【讨论】:

    【解决方案2】:

    不要将变量nameallowed_domainsstart_urlsrules 附加到类中,您应该编写一个MySpider.__init__,调用CrawlSpider.__init__,从中传递必要的参数,并设置@每个对象 987654327@、allowed_domains 等。 MyProp 和关键字也应该在你的__init__ 中设置。所以最后你应该有类似下面的东西。您不必将name 添加到参数中,因为name 是由BaseSpider 本身从kwargs 设置的:

    class MySpider(CrawlSpider):
    
        def __init__(self, allowed_domains=[], start_urls=[], 
                rules=[], findtag='', finditemprop='', keywords='', **kwargs):
            CrawlSpider.__init__(self, **kwargs)
            self.allowed_domains = allowed_domains
            self.start_urls = start_urls
            self.rules = rules
            self.findtag = findtag
            self.finditemprop = finditemprop
            self.keywords = keywords
    
        def parse_item(self, response):
            contentTags = []
    
            soup = BeautifulSoup(response.body)
    
            contentTags = soup.findAll(self.findtag, itemprop=self.finditemprop)
    
            for contentTag in contentTags:
                matchedResult = re.search(self.keywords, contentTag.text)
                if matchedResult:
                    print('URL Found: ' + response.url)
    

    【讨论】:

    • 你能解释一下如何调用这个类MySpider吗?我认为在最新版本中这种方法是不可能的
    • 不知道。好久没用了。它奏效了 - 6 年前。
    • 是的,我刚看到很久以前的帖子,谢谢
    【解决方案3】:

    您可以创建一个由解释器评估的运行时蜘蛛。这段代码可以像这样在运行时进行评估:

    a = open("test.py")
    from compiler import compile
    d = compile(a.read(), 'spider.py', 'exec')
    eval(d)
    
    MySpider
    <class '__main__.MySpider'>
    print MySpider.start_urls
    ['http://www.somedomain.com']
    

    【讨论】:

      【解决方案4】:

      我使用Scrapy Extensions 方法将Spider 类扩展为 到一个名为Masterspider 的包含通用解析器的类。

      下面是我的通用扩展解析器的非常“短”版本。请注意,一旦您开始使用 AJAX 处理页面,就需要使用 Javascript 引擎(例如 Selenium 或 BeautifulSoup)实现渲染器。还有很多额外的代码来管理站点之间的差异(基于列标题的报废、处理相对 URL 和长 URL、管理不同类型的数据容器等......)。

      Scrapy Extension 方法的有趣之处在于,如果某些东西不适合但我从来不需要这样做,您仍然可以覆盖通用解析器方法。 Masterspider 类检查是否在站点特定的蜘蛛类下创建了某些方法(例如 parser_start、next_url_parser...),以允许对特定内容进行管理:发送表单、从页面中的元素构造 next_url 请求等。

      由于我正在抓取非常不同的网站,因此总会有一些特殊性需要管理。这就是为什么我更喜欢为每个抓取的站点保留一个类,以便我可以编写一些特定的方法来处理它(预处理/后处理,除了 PipeLines、请求生成器......)。

      masterspider/sitespider/settings.py

      EXTENSIONS = {
          'masterspider.masterspider.MasterSpider': 500
      }
      

      masterspider/masterspdier/masterspider.py

      # -*- coding: utf8 -*-
      from scrapy.spider import Spider
      from scrapy.selector import Selector
      from scrapy.http import Request
      from sitespider.items import genspiderItem
      
      class MasterSpider(Spider):
      
          def start_requests(self):
              if hasattr(self,'parse_start'): # First page requiring a specific parser
                  fcallback = self.parse_start
              else:
                  fcallback = self.parse
              return [ Request(self.spd['start_url'],
                           callback=fcallback,
                           meta={'itemfields': {}}) ]
      
          def parse(self, response):
              sel = Selector(response)
              lines = sel.xpath(self.spd['xlines'])
              # ...
              for line in lines:
                  item = genspiderItem(response.meta['itemfields'])               
                  # ...
                  # Get request_url of detailed page and scrap basic item info
                  # ... 
                  yield  Request(request_url,
                         callback=self.parse_item,
                         meta={'item':item, 'itemfields':response.meta['itemfields']})
      
              for next_url in sel.xpath(self.spd['xnext_url']).extract():
                  if hasattr(self,'next_url_parser'): # Need to process the next page URL before?
                      yield self.next_url_parser(next_url, response)
                  else:
                      yield Request(
                          request_url,
                          callback=self.parse,
                          meta=response.meta)
      
          def parse_item(self, response):
              sel = Selector(response)
              item = response.meta['item']
              for itemname, xitemname in self.spd['x_ondetailpage'].iteritems():
                  item[itemname] = "\n".join(sel.xpath(xitemname).extract())
              return item
      

      masterspider/sitespider/spiders/somesite_spider.py

      # -*- coding: utf8 -*-
      from scrapy.spider import Spider
      from scrapy.selector import Selector
      from scrapy.http import Request
      from sitespider.items import genspiderItem
      from masterspider.masterspider import MasterSpider
      
      class targetsiteSpider(MasterSpider):
          name = "targetsite"
          allowed_domains = ["www.targetsite.com"]
          spd = {
              'start_url' : "http://www.targetsite.com/startpage", # Start page
              'xlines' : "//td[something...]",
              'xnext_url' : "//a[contains(@href,'something?page=')]/@href", # Next pages
              'x_ondetailpage' : {
                  "itemprop123" :      u"id('someid')//text()"
                  }
          }
      
      #     def next_url_parser(self, next_url, response): # OPTIONAL next_url regexp pre-processor
      #          ...
      

      【讨论】:

        【解决方案5】:

        就爬取作为参数传递的特定域而言,我只是覆盖Spider.__init__

        class MySpider(scrapy.Spider):
            """
            This spider will try to crawl whatever is passed in `start_urls` which
            should be a comma-separated string of fully qualified URIs.
        
            Example: start_urls=http://localhost,http://example.com
            """
            def __init__(self, name=None, **kwargs):
                if 'start_urls' in kwargs:
                    self.start_urls = kwargs.pop('start_urls').split(',')
                super(Spider, self).__init__(name, **kwargs)
        

        【讨论】:

          猜你喜欢
          • 2017-09-11
          • 1970-01-01
          • 2010-12-03
          • 2014-03-04
          • 1970-01-01
          • 2013-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多