python爬虫结构scrapy实例详解

生成项目

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 = (
        'http://example.com/page1',
        'http://example.com/page2',
        )
    def parse(self, response):
        # 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


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("//h1/text()").extract()
        torrent['description'] = x.select("//div[@id='description']").extract()
        torrent['size'] = x.select("//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

假如item不符合要求,那么就抛一个反常,这个item不会被输出到json文件中。

要运用pipelines,咱们还需求修正settings.py

增加一行

ITEM_PIPELINES = ['dirbot.pipelines.FilterWordsPipeline']

现在履行scrapy crawl dmoz -o items.json -t json,不符合要求的item就被过滤掉了


上一篇:Python 如何将一变量做为函数名?
下一篇:处理ImportError: libmysqlclient_r.so.16: cannot open shared object file

PythonTab微信大众号:

Python技能交流合作群 ( 请勿加多个群 ):

群1: 87464755

群2: 333646237

群3: 318130924

群4: 385100854