2012-05-15 46 views
6

我正在使用Scrapy,特別是Scrapy的CrawlSpider類來刮取包含特定關鍵字的網頁鏈接。我有一個很長的start_urls列表,它從連接到Django項目的SQLite數據庫獲取條目。我想保存在這個數據庫中的網頁鏈接。如何訪問Scrapy CrawlSpider中的特定start_url?

我有兩個Django模型,一個用於起始網址,如http://example.com,一個用於刮網站鏈接,如http://example.com/website1http://example.com/website2等全部刮掉網站鏈接都在start_urls列表開始的一個URL的子網站。

網絡鏈接模型與起始網址模型具有多對一的關係,即網絡鏈接模型具有啓動網址模型的外鍵。爲了將我的網頁鏈接正確地保存到數據庫中,我需要告訴CrawlSpiderparse_item()方法,該方法啓動了抓取的網頁鏈接所屬的網址。我怎樣才能做到這一點? Scrapy的DjangoItem類在這方面沒有幫助,因爲我仍然需要明確定義使用的開始url。

換句話說,我如何將當前使用的啓動url傳遞給parse_item()方法,以便我可以將它與適當的網頁鏈接一起保存到數據庫中?有任何想法嗎?提前致謝!

+0

你可以在與weblinks表相同的表中使用start_url字段嗎(就像你正在使用的DjangoItem一樣)?當然,它會創建冗餘非規範化,但如果你想避免明確調用,這可能會有所幫助。 – zubinmehta

回答

8

默認情況下,您無法訪問原始啓動網址。

但是,您可以覆蓋make_requests_from_url方法並將起始網址放入meta。然後在解析中,你可以從那裏提取它(如果你在解析方法中產生後續請求,不要忘記在它們中轉發該起始url)。


我還沒有和CrawlSpider工作,說不定什麼馬克西姆暗示會爲你工作,但要記住,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和其他類和方法的期望。進入代碼有助於節省時間,儘管開始時可能看起來很困難。

+0

我想我粗略地理解你的意思,但我不知道如何實現它。你能否提供一個簡短的例子來演示如何使用'make_requests_from_url'方法以及它如何與'parse'方法相關?我還是個初學者。謝謝! – pemistahl

+1

謝謝warwaruk,這個作品很好。但是,我發現只有'make_requests_from_url'方法和請求的'meta'標記的配置才能正常工作。我不想最終保存起始網址,因此將它放入一個項目中是沒有意義的。無論如何,非常感謝你! :) – pemistahl

1

如果我理解正確的問題,您可以從response.url獲得url,然後寫入item['url']

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

而且在管道:url = item['url']

或者把response.url改成meta,就像warvariuc寫道的。

1

看起來像warvariuc的答案需要稍微修改Scrapy 1.3.3:您需要覆蓋_parse_response而不是parse。覆蓋make_requests_from_url不再需要。