Scrapy框架实例:获取图书信息

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

其最初是为了页面抓取(更确切来说,网络抓取)所设计的, 也可以应用在获取API所返回的数据(例如Amazon Associates Web Services) 或者通用的网络爬虫。

Scrapy构架

下图显示Scrapy的结构和组件,箭头表示框架内数据流情况。

Scrapy框架结构和组件

Scrapy中的数据流由执行引擎控制,具体流程如下:

  1. 引擎获取初始请求从爬虫开始抓取数据。
  2. 引擎在调度器中调度请求,并要求抓取下一个请求。
  3. 调度器将下一个请求返回给引擎。
  4. 引擎将请求发送到下载器,通过下载器中间件。
  5. 一旦页面完成下载,下载器会生成响应并将其发送到引擎,通过下载中间件。
  6. 引擎从下载器接收响应并将其发送到爬虫进行处理,通过爬中间件。
  7. 爬虫处理响应,并将抓取的项目和新的请求返回到引擎,通过爬虫中间件。
  8. 引擎将处理过的项目发送到项目管道,然后将处理过的请求发送到调度器,并要求可能的下一个请求爬取。
  9. 重复以上流程直到调度器没有更多的请求。

接下来我们通过项目实例来看看它是如何运行的。

安装Scrapy

使用pip安装:

pip install scrapy

windows系统下安装会出现异常,需要到https://www.lfd.uci.edu/~gohlke/pythonlibs/下载Twisted库的whl文件,切换到文件目录后,在命令行下输入:

pip install Twisted-17.9.0-cp35-cp35m-win_amd64.whl

注:当前操作系统是64位的,环境是Python3.5,所以选择下载Twisted-17.9.0-cp35-cp35m-win_amd64.whl

根据系统及环境选择下载

安装完后再次运行:

pip install scrapy

创建项目

图灵社区:http://www.ituring.com.cn/book

图灵社区:主要以出版计算机、数学统计、科普等图书,并授权销售正版电子书的在线社区。是我比较喜欢的出版社之一,有兴趣的可以看看上面的书籍,质量都不错。

这里我们以爬取图灵社区图书信息作为项目先切换到要放置项目的目录文件夹,在命令行下输入:

scrapy startproject ituring
目录结构

该命令会创建包含下面内容的ituring目录:

  • scrapy.cfg:项目的配置文件
  • items.py:项目中的item文件
  • middlewares.py:项目中的中间件文件
  • pipelines.py:项目中的pipelines文件
  • settings.py:项目的设置文件

创建爬虫文件

进入项目文件夹ituring/ituring,使用如下命令:

cd ituring

创建爬虫程序并设定允许爬取的域名地址:

scrapy genspider ituring_spider ituring.com.cn

在spiders目录下会新创建ituring_spider.py文件,具体代码如下:

import scrapy


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'
    allowed_domains = ['ituring.com.cn']
    start_urls = ['http://ituring.com.cn/']

    def parse(self, response):
        pass

定义Item

Item 是保存爬取到的数据的容器,本质上就是Python字典数据类型,提供了额外保护机制来避免拼写错误导致的未定义字段错误。每个自定义的Item类都继承scrapy.item类,字段定义类型scrapy.Field类属性。

根据我们要爬取网站的数据进行设定字段,从http://www.ituring.com.cn/book主页点击进入书籍,可以看到书籍详情数据,这里我们需要选取的数据有,标题、链接地址、书籍图片、推荐人数、阅读人数及图书价格。

打开items.py文件开始在Item中定义相应字段,对IturingItem类进行修改:

import scrapy


class IturingItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()  # 标题
    link = scrapy.Field()  # 链接
    img = scrapy.Field()  # 图片
    up_count = scrapy.Field()  # 推荐数
    read_count = scrapy.Field()  # 阅读数
    price = scrapy.Field()  # 价格

编写爬虫

Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类。可以存在多个Spider类,但name属性的值(即爬虫名称)必须唯一,start_urls作为初始的URL,可以对其进行重写,一旦重写则start_urls立即失效。

在编写爬虫前我们先对网站进行分析,开始从http://www.ituring.com.cn/book主页获取每本书籍详情页链接,后面将要请求的request发送给调度器,接着使用编写的回调函数对详情页进行解析。

通过检查元素发现书籍在a标签下的href属性值里,并且因为是相对地址,还需要拼接成绝对地址。

检查元素

进入详情页http://www.ituring.com.cn/book/1927使用谷歌浏览器插件 XPath Helper提取需要的字段的XPath进行调试(这里也可以用检查元素进行Copy XPath)。

选择器

下面开始我们的第一个爬虫,打开ituring/ituring/spiders目录下的ituring_spider.py

import scrapy

from ituring.items import IturingItem


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'  # 爬虫名称
    allowed_domains = ['ituring.com.cn']  # 允许爬取的域名地址
    start_urls = ['http://www.ituring.com.cn/book']  # 初始URL

    def parse(self, response):
        """
        初始URl默认使用该解析方法
        """
        for book in response.xpath('//div[@class="book-img"]/a/@href'):
            url = response.urljoin(book.extract())  # 从列表页获取书籍URL链接
            yield scrapy.Request(url, callback=self.parse_book_info)  # 发送给调度器,回调函数使用parse_book_info

    def parse_book_info(self, response):
        """
        解析图书详情页信息
        """
        item = IturingItem()  # 定义Item
        item['title'] = response.xpath('//div[@class="book-title"]/h2/text()').extract_first()
        item['link'] = response.url
        item['img'] = response.xpath('//div[@class="book-img"]/a/img/@src').extract_first()
        item['up_count'] = response.xpath('//*[@id="toggle-vote"]/span[1]/text()').extract_first()
        item['read_count'] = response.xpath('//*[@id="book-fav-vote"]/div/span[1]/text()').extract_first()
        item['price'] = response.xpath('//dl/dd/span[@class="price"]/text()').extract_first()
        yield item

启动爬虫,抓取数据

到这里,我们的爬虫程序基本雏形已经完成,现在可以运行我们的爬虫程序,在命令行下输入:

scrapy crawl ituring_spider

启动爬虫后,将得到类似以下的输出信息:

终端输出信息

可以看到我们的爬虫程序已经爬取到详情页信息数据,但仍然有问题数据里多了很多无用的字符例如\r\n

项目管道(Item Pipeline)

当Spider最终处理的Item之后,会被传递到项目管道,管道按顺序进行执行处理,在这里我们可以定义处理清除无用字符串的Pipeline。另外需要判断进入管道的Item是否有需要处理的字段,因为我们也许有很多的Item进入到管道里。通过管道将Item里字段多余的无用字符删除掉,达到清理数据的效果。

打开pipelines.py文件,看到默认的管道类:

class IturingPipeline(object):
    def process_item(self, item, spider):
        return item

处理后的Item最终需要return,下面开始自定义我们的管道吧!

class StripPipeline(object):
    """
    清除无用字符
    """

    def process_item(self, item, spider):
        if item['price']:
            item['price'] = item['price'].replace(' ', '').replace('\r', '').replace('\n', '').replace('¥', '')
        if item['title']:
            item['title'] = item['title'].replace(' ', '').replace('\r', '').replace('\n', '')
        return item

翻页爬取

在这之前我们还只是爬取单页的URL链接,而爬取多页则需要分析网页的翻页。通过点击下一页,发现原来的URL跳转到http://www.ituring.com.cn/book?tab=book&sort=hot&page=1,看出page参数是翻页页码,起始页是0。使用构造页码的方式可以遍历所有的页码页面,当没有获取到对应数据则停止。进一步分析发现最有一页没有下一页,而下一页则正是我们接下来要爬取的页面。所以可以通过判断当前页面是否有下一页,如果有则从“下一页”标签中的链接开始爬取,如果没有下一页则爬取完后停止程序。

第一页
最后一页

打开ituring_spider.py文件,添加翻页代码:

import scrapy

from ituring.items import IturingItem


class IturingSpiderSpider(scrapy.Spider):
    name = 'ituring_spider'  # 爬虫名称
    allowed_domains = ['ituring.com.cn']  # 允许爬取的域名地址
    start_urls = ['http://www.ituring.com.cn/book']  # 初始URL

    def parse(self, response):
        """
        初始URl默认使用该解析方法
        """
        for book in response.xpath('//div[@class="book-img"]/a/@href'):
            url = response.urljoin(book.extract())  # 从列表页获取书籍URL链接
            yield scrapy.Request(url, callback=self.parse_book_info)  # 发送给调度器,回调函数使用parse_book_info

        next_page = response.xpath('//div/ul/li[@class="PagedList-skipToNext"]/a/@href')

        if next_page:
            url = response.urljoin(next_page.extract_first())  # 下一页的链接
            yield scrapy.Request(url, callback=self.parse)

    def parse_book_info(self, response):
        """
        解析图书详情页信息
        """
        item = IturingItem()  # 定义Item
        item['title'] = response.xpath('//div[@class="book-title"]/h2/text()').extract_first()
        item['link'] = response.url
        item['img'] = response.xpath('//div[@class="book-img"]/a/img/@src').extract_first()
        item['up_count'] = response.xpath('//*[@id="toggle-vote"]/span[1]/text()').extract_first()
        item['read_count'] = response.xpath('//*[@id="book-fav-vote"]/div/span[1]/text()').extract_first()
        item['price'] = response.xpath('//dl/dd/span[@class="price"]/text()').extract_first()
        yield item

配置settings

不管是管道还是中间件,都需要到setting文件里面进行设置启用,这步可以可以在编写完管道或中间件后进行。
设置存放在settings.py文件,打开后编辑添加配置信息:

  1. 激活管道
    管道对应的值越大则越后通过,这里可以看到两个管道,默认管道和处理无用字符的管道。
ITEM_PIPELINES = {
    'ituring.pipelines.IturingPipeline': 300,
    'ituring.pipelines.StripPipeline': 400,
}
  1. 设置延时
    因为爬虫程序设计爬取到多页面,为防止对服务器造成影响和可能被kill掉,需要添加每次请求延时时间。
DOWNLOAD_DELAY = 3

保存数据

最简单存储爬取的数据的方式是使用Feed exports,其自带的类型有:

  • JSON
  • JSON lines
  • CSV
  • XML
    你也可以通过FEED_EXPORTERS设置扩展支持的属性,也可以存储到数据库里,数据库推荐使用MongoDB。

在启动爬虫的命令后面加上-o file_name.json将对爬取的数据进行序列化并采用JSON格式存储,生成文件file_name.json文件。
scrapy crawl ituring_spider -o items.json

items.json

最后打开items.json文件查看数据发现title标题数据中文乱码,打开settings.py文件配置FEED_EXPORTERS导出的编码方式:

# 设置输出格式
# FEED_EXPORT_ENCODING = 'utf-8'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容