如何在Scrapy CrawlSpider中访问特定的start_url?

时间:2012-05-15 10:22:42

标签: python django scrapy

我正在使用Scrapy,特别是Scrapy的CrawlSpider类来抓取包含某些关键字的网页链接。我有一个很长的start_urls列表,它从一个连接到Django项目的SQLite数据库中获取它的条目。我想在此数据库中保存已删除的Web链接。

我有两个Django模型,一个用于http://example.com等起始网址,另一个用于抓取的网络链接,例如http://example.com/website1http://example.com/website2等。所有抓取的网络链接都是子网站start_urls列表中的一个起始网址。

Web链接模型与起始URL模型具有多对一关系,即Web链接模型具有到开始URL模型的外键。为了将我的已删除的Web链接正确保存到数据库,我需要告诉CrawlSpider的{​​{1}}方法,该方法启动了已删除的Web链接所属的URL。我怎样才能做到这一点? Scrapy的parse_item()类在这方面没有帮助,因为我仍然需要明确定义使用的启动URL。

换句话说,如何将当前使用的起始网址传递给DjangoItem方法,以便我可以将其与适当的网页链接一起保存到数据库中?有任何想法吗?提前致谢!

4 个答案:

答案 0 :(得分:8)

默认情况下,您无法访问原始启动网址。

但您可以覆盖make_requests_from_url方法并将起始网址放入meta。然后在解析中你可以从那里提取它(如果你在后面的请求中产生了解析方法,不要忘记在它们中转发那个起始网址。)


我没有与CrawlSpider合作,也许Maxim建议对你有用,但请记住response.url在可能的重定向后有网址。

这是我将如何做的一个例子,但它只是一个例子(取自scrapy教程)并且没有经过测试:

class MySpider(CrawlSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com']

    rules = (
        # Extract links matching 'category.php' (but not matching 'subsection.php')
        # and follow links from them (since no callback means follow=True by default).
        Rule(SgmlLinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),

        # Extract links matching 'item.php' and parse them with the spider's method parse_item
        Rule(SgmlLinkExtractor(allow=('item\.php', )), callback='parse_item'),
    )

    def parse(self, response): # When writing crawl spider rules, avoid using parse as callback, since the CrawlSpider uses the parse method itself to implement its logic. So if you override the parse method, the crawl spider will no longer work.
        for request_or_item in CrawlSpider.parse(self, response):
            if isinstance(request_or_item, Request):
                request_or_item = request_or_item.replace(meta = {'start_url': response.meta['start_url']})
            yield request_or_item

    def make_requests_from_url(self, url):
        """A method that receives a URL and returns a Request object (or a list of Request objects) to scrape. 
        This method is used to construct the initial requests in the start_requests() method, 
        and is typically used to convert urls to requests.
        """
        return Request(url, dont_filter=True, meta = {'start_url': url})

    def parse_item(self, response):
        self.log('Hi, this is an item page! %s' % response.url)

        hxs = HtmlXPathSelector(response)
        item = Item()
        item['id'] = hxs.select('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
        item['name'] = hxs.select('//td[@id="item_name"]/text()').extract()
        item['description'] = hxs.select('//td[@id="item_description"]/text()').extract()
        item['start_url'] = response.meta['start_url']
        return item

询问您是否有任何疑问。顺便说一句,使用PyDev的“转到定义”功能,您可以看到scrapy源并了解Requestmake_requests_from_url和其他类和方法所期望的参数。进入代码可以帮助并节省您的时间,尽管一开始可能看起来很难。

答案 1 :(得分:1)

如果我正确理解了问题,您可以从response.url获取网址,然后写信给item['url']

在蜘蛛:item['url'] = response.url

在管道中:url = item['url']

或者将response.url放入meta,如warvariuc所写。

答案 2 :(得分:1)

看起来warvariuc的答案需要稍微修改一下Scrapy 1.3.3:你需要覆盖_parse_response而不是parse。不再需要覆盖make_requests_from_url

答案 3 :(得分:0)

按照 Stephan Seyboth 的建议,对于 Scrapy 2.5.0,我成功地覆盖了 _parse_response 类中的 CrawlSpider。查看 Scrapy's crawl.py 以获取需要修改的当前方法定义。我还在 make_requests_from_url 中将变量添加到 meta 中。

def _parse_response(self, response, callback, cb_kwargs, follow=True):
    if callback:
        cb_res = callback(response, **cb_kwargs) or ()
        cb_res = self.process_results(response, cb_res)
        for request_or_item in iterate_spider_output(cb_res):
            yield request_or_item
    if follow and self._follow_links:
        for request_or_item in self._requests_to_follow(response):
            request_or_item.meta['start_url'] = response.meta['start_url']
            yield request_or_item

def make_requests_from_url(self, url):
    return Request(url, dont_filter=True, meta = {'start_url': url})

然后我可以在 response.meta['start_url'] 方法中访问 parse_item。这将包含原始的 start_url。