python爬蟲框架scrapy實例詳解

jopen 9年前發布 | 37K 次閱讀 網絡爬蟲 Scrapy

生成項目scrapy提供一個工具來生成項目,生成的項目中預置了一些文件,用戶需要在這些文件中添加自己的代碼。打開命令行,執行:scrapy st...

生成項目

scrapy提供一個工具來生成項目,生成的項目中預置了一些文件,用戶需要在這些文件中添加自己的代碼。

打開命令行,執行:scrapy startproject tutorial,生成的項目類似下面的結構

tutorial/

   scrapy.cfg

   tutorial/

       __init__.py

       items.py

       pipelines.py

       settings.py

       spiders/

           __init__.py

           ...

scrapy.cfg是項目的配置文件

用戶自己寫的spider要放在spiders目錄下面,一個spider類似

from scrapy.spider import BaseSpider
class DmozSpider(BaseSpider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]
    def parse(self, response):
        filename = response.url.split("/")[-2]
        open(filename, 'wb').write(response.body)

name屬性很重要,不同spider不能使用相同的name

start_urls是spider抓取網頁的起始點,可以包括多個url

parse方法是spider抓到一個網頁以后默認調用的callback,避免使用這個名字來定義自己的方法。

當spider拿到url的內容以后,會調用parse方法,并且傳遞一個response參數給它,response包含了抓到的網頁的內容,在parse方法里,你可以從抓到的網頁里面解析數據。上面的代碼只是簡單地把網頁內容保存到文件。


開始抓取

你可以打開命令行,進入生成的項目根目錄tutorial/,執行 scrapy crawl dmoz, dmoz是spider的name。


解析網頁內容

scrapy提供了方便的辦法從網頁中解析數據,這需要使用到HtmlXPathSelector

from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
class DmozSpider(BaseSpider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]
    def parse(self, response):
        hxs = HtmlXPathSelector(response)
        sites = hxs.select('//ul/li')
        for site in sites:
            title = site.select('a/text()').extract()
            link = site.select('a/@href').extract()
            desc = site.select('text()').extract()
            print title, link, desc

HtmlXPathSelector使用了Xpath來解析數據

//ul/li表示選擇所有的ul標簽下的li標簽

a/@href表示選擇所有a標簽的href屬性

a/text()表示選擇a標簽文本

a[@href="abc"]表示選擇所有href屬性是abc的a標簽

我們可以把解析出來的數據保存在一個scrapy可以使用的對象中,然后scrapy可以幫助我們把這些對象保存起來,而不用我們自己把這些數據存到文件中。我們需要在items.py中添加一些類,這些類用來描述我們要保存的數據

from scrapy.item import Item, Field

class DmozItem(Item):

   title = Field()

   link = Field()

   desc = Field()

然后在spider的parse方法中,我們把解析出來的數據保存在DomzItem對象中。

from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from tutorial.items import DmozItem
class DmozSpider(BaseSpider):
   name = "dmoz"
   allowed_domains = ["dmoz.org"]
   start_urls = [
       "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
       "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
   ]
   def parse(self, response):
       hxs = HtmlXPathSelector(response)
       sites = hxs.select('//ul/li')
       items = []
       for site in sites:
           item = DmozItem()
           item['title'] = site.select('a/text()').extract()
           item['link'] = site.select('a/@href').extract()
           item['desc'] = site.select('text()').extract()
           items.append(item)
       return items

在命令行執行scrapy的時候,我們可以加兩個參數,讓scrapy把parse方法返回的items輸出到json文件中

scrapy crawl dmoz -o items.json -t json

items.json會被放在項目的根目錄


讓scrapy自動抓取網頁上的所有鏈接

上面的示例中scrapy只抓取了start_urls里面的兩個url的內容,但是通常我們想實現的是scrapy自動發現一個網頁上的所有鏈接,然后再去抓取這些鏈接的內容。為了實現這一點我們可以在parse方法里面提取我們需要的鏈接,然后構造一些Request對象,并且把他們返回,scrapy會自動的去抓取這些鏈接。代碼類似:

class MySpider(BaseSpider):
    name = 'myspider'
    start_urls = (
        '

    # collect `item_urls`
    for item_url in item_urls:
        yield Request(url=item_url, callback=self.parse_item)
def parse_item(self, response):
    item = MyItem()
    # populate `item` fields
    yield Request(url=item_details_url, meta={'item': item},
        callback=self.parse_details)
def parse_details(self, response):
    item = response.meta['item']
    # populate more `item` fields
    return item</pre> <p><br />

</p>

parse是默認的callback, 它返回了一個Request列表,scrapy自動的根據這個列表抓取網頁,每當抓到一個網頁,就會調用parse_item,parse_item也會返回一個列表,scrapy又會根據這個列表去抓網頁,并且抓到后調用parse_details

為了讓這樣的工作更容易,scrapy提供了另一個spider基類,利用它我們可以方便的實現自動抓取鏈接. 我們要用到CrawlSpider

from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
class MininovaSpider(CrawlSpider):
    name = 'mininova.org'
    allowed_domains = ['mininova.org']
    start_urls = ['http://www.mininova.org/today']
    rules = [Rule(SgmlLinkExtractor(allow=['/tor/\d+'])),
             Rule(SgmlLinkExtractor(allow=['/abc/\d+']), 'parse_torrent')]
    def parse_torrent(self, response):
        x = HtmlXPathSelector(response)
        torrent = TorrentItem()
        torrent['url'] = response.url
        torrent['name'] = x.select("http://h1/text()").extract()
        torrent['description'] = x.select("http://div[@id='description']").extract()
        torrent['size'] = x.select("http://div[@id='info-left']/p[2]/text()[2]").extract()
        return torrent

相比BaseSpider,新的類多了一個rules屬性,這個屬性是一個列表,它可以包含多個Rule,每個Rule描述了哪些鏈接需要抓取,哪些不需要。這是Rule類的文檔http://doc.scrapy.org/en/latest/topics/spiders.html#scrapy.contrib.spiders.Rule

這些rule可以有callback,也可以沒有,當沒有callback的時候,scrapy簡單的follow所有這些鏈接.

pipelines.py的使用

在pipelines.py中我們可以添加一些類來過濾掉我們不想要的item,把item保存到數據庫。

from scrapy.exceptions import DropItem
class FilterWordsPipeline(object):
    """A pipeline for filtering out items which contain certain words in their
    description"""

# put all words in lowercase
words_to_filter = ['politics', 'religion']
def process_item(self, item, spider):
    for word in self.words_to_filter:
        if word in unicode(item['description']).lower():
            raise DropItem("Contains forbidden word: %s" % word)
    else:
        return item</pre> <p>如果item不符合要求,那么就拋一個異常,這個item不會被輸出到json文件中。 </p>

要使用pipelines,我們還需要修改settings.py

添加一行

ITEM_PIPELINES = ['dirbot.pipelines.FilterWordsPipeline']

現在執行scrapy crawl dmoz -o items.json -t json,不符合要求的item就被過濾掉了


</div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!