创建一个通用的scrapy蜘蛛

时间:2012-03-22 00:24:40

标签: python scrapy web-crawler

我的问题是如何做与前一个问题相同的事情,但在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

5 个答案:

答案 0 :(得分:2)

您可以创建一个由解释器评估的运行时蜘蛛。这段代码可以在运行时进行评估,如下所示:

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']

答案 1 :(得分:2)

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

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

Scrapy Extension方法的另一个原因是,如果某些东西不适合我仍然可以覆盖通用的解析器方法,但我从来没有这样做过。 Masterspider类检查是否已在特定于站点的spider类下创建了一些方法(例如,parser_start,next_url_parser ...)以允许管理特定内容:发送表单,从页面中的元素构造next_url请求等。 / p>

由于我正在抓取截然不同的网站,因此总是需要管理。这就是为什么我更喜欢为每个被删除的站点保留一个类,以便我可以编写一些特定的方法来处理它(除了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 /蜘蛛/ 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
#          ...

答案 2 :(得分:1)

不要让变量nameallowed_domainsstart_urlsrules附加到班级,而应该写一个MySpider.__init__,调用{{1从那里传递必要的参数,并为每个对象设置CrawlSpider.__init__name等。 allowed_domains和关键字也应在MyProp内设置。所以最后你应该有类似下面的东西。您不必向参数添加__init__,因为namename设置为BaseSpider

kwargs

答案 3 :(得分:0)

我不确定哪种方式更受欢迎,但我会告诉你我过去所做的事情。我无法确定这是做到这一点的最佳(或正确)方式,我有兴趣了解其他人的想法。

我通常只是覆盖父类(CrawlSpider)并传入参数,然后从我自己的init-function 中通过super(MySpider, self).__init__()初始化父类从我保存了要附加到start_urls的链接列表的数据库中提取该数据。

答案 4 :(得分:0)

对于作为参数传递的特定域的爬网,我只是覆盖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)