7

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

我有两个 Django 模型,一个用于起始 URL,例如http://example.com,一个用于抓取的 Web 链接,例如http://example.com/website1,等等。所有抓取的 Web 链接都是列表http://example.com/website2中起始 URL 之一的子站点。start_urls

网络链接模型与起始 url 模型具有多对一的关系,即网络链接模型具有起始 urls 模型的外键。为了将我抓取的网页链接正确保存到数据库,我需要告诉CrawlSpider'sparse_item()方法,抓取的网页链接属于哪个起始网址。我怎样才能做到这一点?Scrapy 的DjangoItem类在这方面没有帮助,因为我仍然必须明确定义使用的起始 url。

换句话说,如何将当前使用的起始 url 传递给该parse_item()方法,以便我可以将它与适当的抓取 Web 链接一起保存到数据库中?有任何想法吗?提前致谢!

4

4 回答 4

9

默认情况下,您无法访问原始起始 url。

但是您可以覆盖make_requests_from_url方法并将开始 url 放入meta. 然后在解析中,您可以从那里提取它(如果您在该解析方法中产生后续请求,请不要忘记在其中转发该起始 url)。


我没有合作过CrawlSpider,也许 Maxim 的建议对你有用,但请记住,response.url在可能的重定向之后有 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 源并了解参数Request以及make_requests_from_url其他类和方法所期望的。进入代码有助于并节省您的时间,尽管一开始可能看起来很困难。

于 2012-05-15T17:35:13.323 回答
3

如果我正确理解了这个问题,您可以从中获取 url response.url,然后写入item['url'].

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

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

或者像warvariuc写的那样投入response.urlmeta

于 2012-05-16T04:00:32.123 回答
1

从 Scrapy 1.3.3 开始,warvariuc 的答案似乎需要稍作修改:您需要覆盖_parse_response而不是parse. make_requests_from_url不再需要覆盖。

于 2017-05-06T05:53:29.900 回答
1

按照 Stephan Seyboth 的建议,对于 Scrapy 2.5.0,我_parse_responseCrawlSpider课堂上成功超越。查看Scrapy 的 crawl.py以了解需要修改的当前方法定义。我还将变量添加到 meta,在make_requests_from_url.

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。

于 2021-06-07T01:26:55.067 回答