使用Celery时,Scrapy蜘蛛不遵循链接

时间:2014-06-15 18:27:41

标签: python django scrapy twisted celery

我在Python中编写一个爬虫程序,它抓取给定域中的所有页面,作为特定于域的搜索引擎的一部分。我正在使用Django,Scrapy和Celery实现这一目标。方案如下:

我从用户那里收到一个域名,并在视图中调用crawl任务,将域作为参数传递:

crawl.delay(domain)

任务本身只调用一个启动爬网过程的函数:

from .crawler.crawl import run_spider
from celery import shared_task

@shared_task
def crawl(domain):
    return run_spider(domain) 

run_spider启动抓取过程as in this SO answer,将MySpider替换为WebSpider

WebSpider继承自CrawlSpider,我现在只是为了测试功能而使用它。唯一定义的规则采用SgmlLinkExtractor实例和回调函数parse_page,它只提取响应URL和页面标题,用它们填充新的DjangoItem(HTMLPageItem)并将其保存到数据库(我知道效率不高)。

from urlparse import urlparse
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from ..items import HTMLPageItem
from scrapy.selector import Selector
from scrapy.contrib.spiders import Rule, CrawlSpider

class WebSpider(CrawlSpider):
    name = "web"

    def __init__(self, **kw):
        super(WebSpider, self).__init__(**kw)
        url = kw.get('domain') or kw.get('url')
        if not (url.startswith('http://') or url.startswith('https://')):
            url = "http://%s/" % url
        self.url = url
        self.allowed_domains = [urlparse(url).hostname.lstrip('www.')]
        self.start_urls = [url]
        self.rules = [
            Rule(SgmlLinkExtractor(
                allow_domains=self.allowed_domains,
                unique=True), callback='parse_page', follow=True)
        ]

    def parse_start_url(self, response):
        return self.parse_page(response)

    def parse_page(self, response):
        sel = Selector(response)
        item = HTMLPageItem()
        item['url'] = response.request.url
        item['title'] = sel.xpath('//title/text()').extract()[0]
        item.save()
        return item

问题是抓取工具在跟踪此方案并使用Celery时仅抓取start_urls并且不跟踪链接(或调用回调函数)。但是通过run_spider调用python manage.py shell可以正常工作!

另一个问题是项目管道和日志记录无法与Celery一起使用。这使得调试变得更加困难。我认为这些问题可能是相关的。

1 个答案:

答案 0 :(得分:2)

因此,在检查Scrapy的代码并启用Celery日志记录后,在web_spider.py中插入这两行:

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

我找到了问题所在: 在WebSpider

的初始化函数中
super(WebSpider, self).__init__(**kw)

__init__的{​​{1}}功能会调用_compile_rules函数,在执行某些更改时,会将规则从CrawlSpider复制到self.rulesself._rules是蜘蛛在检查规则时使用的内容。在定义规则之前调用self._rules的初始化函数导致空CrawlSpider,因此没有遵循链接。

self._rules行移至super(WebSpider, self).__init__(**kw)' WebSpider的最后一行解决了问题。

更新: the previously mentioned SO answer的代码存在一些错误。它会导致反应堆在第二次调用后挂起。修复很简单,在__init__' WebCrawlerScript方法中,只需移动此行:

__init__

出自if语句,如那里的评论所示。

更新2:我终于让管道工作了!这不是芹菜问题。我意识到设置模块没有被读取。这只是一个进口问题。解决它:

在django项目的设置模块self.crawler.signals.connect(reactor.stop, signal=signals.spider_closed) 中设置环境变量SCRAPY_SETTINGS_MODULE

myproject/settings.py

在Scrapy设置模块import os os.environ['SCRAPY_SETTINGS_MODULE'] = 'myapp.crawler.crawler.settings' 中,将您的Scrapy项目路径添加到crawler/settings.py,以便设置文件中的相对导入可用:

sys.path

更改路径以适合您的情况。