python – 使用Celery时Scrapy spider不遵循链接

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

我从用户收到域名并在视图中调用爬网任务,并将域作为参数传递:

crawl.delay(域)

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

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,用WebSpider替换MySpider.

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

问题是爬网程序只跟踪start_urls并且在遵循此方案并使用Celery时不遵循链接(或调用回调函数).但是通过python manage.py shell调用run_spider工作正常!

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

最佳答案 因此,在检查Scrapy的代码并启用Celery日志记录之后,通过在web_spider.py中插入以下两行:

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

我找到了问题所在:
在WebSpider的初始化函数中:

super(WebSpider, self).__init__(**kw)

父CrawlSpider的__init__函数调用_compile_rules函数,该函数简单地将规则从self.rules复制到self._rules,同时进行一些更改. self._rules是蜘蛛在检查规则时使用的.在定义规则之前调用CrawlSpider的初始化函数会导致空self._rules,因此没有遵循链接.

将super(WebSpider,self).__ init __(** kw)行移动到WebSpider的__init__的最后一行修复了问题.

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

self.crawler.signals.connect(reactor.stop, signal=signals.spider_closed)

在if语句之外,正如那里的评论中所建议的那样.

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

在django项目的设置模块myproject / settings.py中设置环境变量SCRAPY_SETTINGS_MODULE:

import os
os.environ['SCRAPY_SETTINGS_MODULE'] = 'myapp.crawler.crawler.settings'

在Scrapy设置模块crawler / settings.py中,将您的Scrapy项目路径添加到sys.path,以便设置文件中的相对导入可以工作:

import sys
sys.path.append('/absolute/path/to/scrapy/project')

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

点赞