【Python笔记】网络爬虫——常用框架介绍以及 Scrapy 框架使用


爬虫框架就是一些 爬虫项目的半成品,可以将一些爬虫常用的功能写好。然后留下一些接口,在不同的爬虫项目当中,调用合适自己项目的接口,再编写少量的代码实现自己需要的功能。因为 框架中已经实现了爬虫常用的功能,所以开发人员节省了很多精力和时间。

Scrapy 爬虫框架

Scrapy 框架是一套比较成熟的 Python 爬虫框架,简单轻巧,并且非常方便。可以高效率的爬取 Web 页面并从页面中提取结构化的数据。 Scrapy 是一套开源的框架,所以在使用时不需要担心收费问题。 Scrapy 的官网:http://scrapy.org

Crawley 爬虫框架

Crawley 也是 Python 开发出的爬虫框架,该框架致力于改变人们从互联网中提取数据的方式。Crawley 的具体特性如下:

  • 基于 Eventlet 构建的高速网络爬虫框架
  • 可以将数据存储在关系数据库中,例如,Postgres、Mysql、Oracle、Sqlite
  • 可以将爬取的数据导入为 Json、XML 格式
  • 支持非关系数据库,例如,Mongodb 和 Couchdb
  • 支持命令行工具
  • 可以使用喜欢的工具进行数据的提取,例如,XPath 或 Pyquery 工具
  • 支持使用 Cookie 登录或访问那些只有登录才可以访问的页面
  • 简单易学(可以参照实例)

Crawley 的官网地址为:http://project.crawley-cloud.com

PySpider 爬虫框架

相对于 Scrapy 框架而言,PySpider 框架是一支新秀。它采用 Python 语言编写,分布式架构,支持多种数据库后端,强大的 WebUI 支持脚本编辑器、任务监视器、项目管理器以及结果查看器。PySpider 的具体特性如下:

  • Python脚本控制,可以用任何你喜欢的 html 解析包(内置 pyquery)
  • Web 界面编写调试脚本、起停脚本、监控执行状态、查看活动历时、获取结果产出
  • 支持 MySQL、MongoDB、Redis、SQLite、Elasticsearch、PostgreSQL 与 SQLAIchemy
  • 支持 RabbitMQ、Beanstalk、Redis 和 Kombu 作为消息队列
  • 支持抓取 JavaScript 的页面
  • 强大的调度控制,支持超时重爬以及优先级设置
  • 组件可替换,支持单机/分布式架构,支持 Docker 部署

Scrapy 爬虫框架的使用

搭建 Scrapy 爬虫框架

由于 Scrapy 爬虫框架依赖的库比较多,尤其是 Windows 系统下,至少需要依赖的库有 Twisted、lxml、pyOpenSSL 以及 pywin32

1. 安装 Twisted 模块

打开(https://www.lfd.uci.edu/~gohlke/pythonlibs/)Python 拓展包的非官方 Windows 二进制文件网站,根据自身情况进行下载,我是 64位系统,Python3.7,所以安装 “Twisted‑19.7.0‑cp37‑cp37m‑win_amd64.whl”。
在这里插入图片描述
下载完成后,以管理员身份运行命令提示符窗口,cd 命令进入下载文件所在路径,输入以下指令

pip install Twisted-19.7.0-cp37-cp37m-win_amd64.whl

2. 安装 Scrapy 框架

cmd 输入以下命令:

pip install Scrapy

注:Scrapy 框架安装的同时,会将 lxml 与 pyOpenSSL 模块也安装。

3. 安装 pywin32 模块

cmd 输入以下命令:

pip install pywin32

创建 Scrapy 项目

在任意路径下创建一个保存项目的文件夹,例如,在“F:\Python” 文件夹内运行 cmd,输入以下指令:

scrapy startproject scrapyDemo

即可创建一个名为 “scrapyDemo” 的项目。
在这里插入图片描述
在 PyCharm 中打开刚刚创建的 scrapyDemo 项目,可以看到目录结构:
在这里插入图片描述

创建爬虫

在 spiders 文件夹中创建一个爬虫模块的文件,例如,创建一个 novel.py 爬取网页源代码:

import scrapy

class NovelSpider(scrapy.Spider):

    # 启动爬虫时会用到这个名称
    name = "novel"

    # 爬取哪个网页的源码,这里是网址
    start_urls = ["https://www.biquge5200.cc/0_857/651708.html"]

    def parse(self, response):
        # 拿到 html 网页源代码
        html_str = response.css('html').extract_first()
        # 保存为本地文件 source.html
        with open('source.html', 'w', encoding='utf-8') as f:
            f.write(html_str)
        self.log('保存文件成功')

  • name 爬虫的名字,启动爬虫时需要用到它;
  • start_urls 要爬取哪个网页的源代码,填写网页网址;
  • parse Scrapy 帮你请求网址,拿到源代码后给 response 参数,对response 操作提取需要的数据。

运行爬虫项目,在 cmd 输入 scrapy crawl novel,其中 “novel” 为自定义的爬虫名称。由于使用了 PyCharm,在底部的 Terminal 窗口输入命令。
运行结果:只需要关注程序是否运行成功即可。

F:\Python\scrapyDemo>scrapy crawl novel
2019-08-21 17:38:00 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: scrapyDemo)
2019-08-21 17:38:00 [scrapy.utils.log] INFO: Versions: lxml 4.3.3.0, libxml2 2.9.5, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twis
ted 19.7.0, Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1
c  28 May 2019), cryptography 2.7, Platform Windows-10-10.0.17134-SP0
2019-08-21 17:38:00 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'scrapyDemo', 'NEWSPIDER_MODULE': 'scrapyDemo.spiders',
'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['scrapyDemo.spiders']}
2019-08-21 17:38:00 [scrapy.extensions.telnet] INFO: Telnet Password: 6e4380f0cd0a1af5
2019-08-21 17:38:00 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.logstats.LogStats']
2019-08-21 17:38:00 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware',
 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2019-08-21 17:38:00 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2019-08-21 17:38:00 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2019-08-21 17:38:00 [scrapy.core.engine] INFO: Spider opened
2019-08-21 17:38:00 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-08-21 17:38:00 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2019-08-21 17:38:00 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/robots.txt> (referer: None)
2019-08-21 17:38:00 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/0_857/651708.html> (referer: None)
2019-08-21 17:38:00 [novel] DEBUG: 保存文件成功
2019-08-21 17:38:00 [scrapy.core.engine] INFO: Closing spider (finished)
2019-08-21 17:38:00 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 459,
 'downloader/request_count': 2,
 'downloader/request_method_count/GET': 2,
 'downloader/response_bytes': 8505,
 'downloader/response_count': 2,
 'downloader/response_status_count/200': 2,
 'elapsed_time_seconds': 0.460619,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2019, 8, 21, 9, 38, 0, 907837),
 'log_count/DEBUG': 3,
 'log_count/INFO': 10,
 'response_received_count': 2,
 'robotstxt/request_count': 1,
 'robotstxt/response_count': 1,
 'robotstxt/response_status_count/200': 1,
 'scheduler/dequeued': 1,
 'scheduler/dequeued/memory': 1,
 'scheduler/enqueued': 1,
 'scheduler/enqueued/memory': 1,
 'start_time': datetime.datetime(2019, 8, 21, 9, 38, 0, 447218)}
2019-08-21 17:38:00 [scrapy.core.engine] INFO: Spider closed (finished)

执行完成后,在根目录下创建了 source.html 文件,打开可以看到网页的源代码已经被我们爬下来了。
在这里插入图片描述

获取数据

Scrapy 提供调试环境css 提取器,帮助我们快速准确的从 html 源代码中拿到我们需要的数据。

调试环境

Scrapy 提供了调试环境,方便我们测试提取的数据是否正确。
命令行中输入下面脚本,其中 scrapy shell 是固定写法,后面跟要爬取网页的网址。

scrapy shell https://www.biquge5200.cc/0_857/651708.html

输入完成后会打印一堆东东,我们只要关注最后一行 >>>

F:\Python\scrapyDemo>scrapy shell https://www.biquge5200.cc/0_857/651708.html
2019-08-21 17:44:43 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: scrapyDemo)
2019-08-21 17:44:43 [scrapy.utils.log] INFO: Versions: lxml 4.3.3.0, libxml2 2.9.5, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twis
ted 19.7.0, Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1
c  28 May 2019), cryptography 2.7, Platform Windows-10-10.0.17134-SP0
2019-08-21 17:44:43 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'scrapyDemo', 'DUPEFILTER_CLASS': 'scrapy.dupefilters.Ba
seDupeFilter', 'LOGSTATS_INTERVAL': 0, 'NEWSPIDER_MODULE': 'scrapyDemo.spiders', 'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['scrapyDe
mo.spiders']}
2019-08-21 17:44:43 [scrapy.extensions.telnet] INFO: Telnet Password: 45fef94917f2a5f6
2019-08-21 17:44:43 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole']
2019-08-21 17:44:43 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware',
 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2019-08-21 17:44:43 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2019-08-21 17:44:43 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2019-08-21 17:44:43 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2019-08-21 17:44:43 [scrapy.core.engine] INFO: Spider opened
2019-08-21 17:44:43 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/robots.txt> (referer: None)
2019-08-21 17:44:44 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/0_857/651708.html> (referer: None)
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x0000024CF2F64A90>
[s]   item       {}
[s]   request    <GET https://www.biquge5200.cc/0_857/651708.html>
[s]   response   <200 https://www.biquge5200.cc/0_857/651708.html>
[s]   settings   <scrapy.settings.Settings object at 0x0000024CF2F64B00>
[s]   spider     <DefaultSpider 'default' at 0x24cf32aa2e8>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
>>>

css 提取器

比如我们想提取 https://www.biquge5200.cc/0_857/651708.html 的 title 标签中的数据,在最后一行 >>> 后面输入 response.css(‘title’),然后回车。

>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>第一章 被驱逐的高手_全职高手_笔趣阁</title>'>]

你会发现,提取到的是个 Selector 数组,并不是我们想要的数据。Scrapy 给我们准备了一些函数来进一步提取,extract() 函数将 Selector 转换为我们熟悉的 html 标签

>>> response.css('title').extract()
['<title>第一章 被驱逐的高手_全职高手_笔趣阁</title>']

上面拿到的 html 标签仍然是一个数组,在后面加上 [0] 很方便拿到数组中第一个元素

>> response.css('title').extract()[0]
'<title>第一章 被驱逐的高手_全职高手_笔趣阁</title>'

Scrapy 提供了另一个函数 extract_first(),同样可以拿到数组中第一个元素,写法上更加简单。至此,成功提取到 title 标签,但是多了 title 标签,继续修改。

>>> response.css('title').extract_first()
'<title>第一章 被驱逐的高手_全职高手_笔趣阁</title>'

在 title 后面加上 ::text 即可提取 html 标签包裹的内容了。到这里已经成功提取到我们需要的数据了。

>>> response.css('title::text').extract_first()
'第一章 被驱逐的高手_全职高手_笔趣阁'

::text 可以提取 html 标签包裹的内容,如果要提取 html 标签自身的属性,比如下面 a 标签的 href 属性值,怎么办呢?

<a href="https://xxx.yyy.ccc">链接</a>

::attr(属性名) 可以提取 html 标签自身的属性。

>>> response.css('a::attr(href)').extract_first()

提取章节标题和章节内容

有了前面 css 提取器的学习,拿到章节标题和章节内容相信不是什么困难的事了

import scrapy

class NovelScrapy(scrapy.Spider):

    name = "novel"

    start_urls = ["https://www.biquge5200.cc/0_857/651708.html"]

    def parse(self, response):
        # 拿到章节标题
        title = response.css('div.bookname h1::text').extract_first()
        # 拿到章节内容
        content = '\n\n'.join(response.css('div#content p::text').extract())
        print("title ", title)
        print("content ", content)

运行爬虫:cmd里输入 scrapy crawl novel
运行结果:

F:\Python\scrapyDemo>scrapy crawl novel
2019-08-21 18:09:50 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: scrapyDemo)
2019-08-21 18:09:50 [scrapy.utils.log] INFO: Versions: lxml 4.3.3.0, libxml2 2.9.5, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twis
ted 19.7.0, Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1
c  28 May 2019), cryptography 2.7, Platform Windows-10-10.0.17134-SP0
2019-08-21 18:09:50 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'scrapyDemo', 'NEWSPIDER_MODULE': 'scrapyDemo.spiders',
'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['scrapyDemo.spiders']}
2019-08-21 18:09:50 [scrapy.extensions.telnet] INFO: Telnet Password: 8c4d3ffd167749cc
2019-08-21 18:09:50 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.logstats.LogStats']
2019-08-21 18:09:50 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware',
 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2019-08-21 18:09:50 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2019-08-21 18:09:50 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2019-08-21 18:09:50 [scrapy.core.engine] INFO: Spider opened
2019-08-21 18:09:50 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-08-21 18:09:50 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2019-08-21 18:09:51 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/robots.txt> (referer: None)
2019-08-21 18:09:51 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.biquge5200.cc/0_857/651708.html> (referer: None)
title   第一章 被驱逐的高手
content    ;

  “卡卡卡,嗒嗒……”

  一双灵巧的手飞舞着艹纵着键盘和鼠标,富有节奏的敲击声仿佛是一首轻快的乐章。屏幕中漫天的光华闪过,对手飞扬着血花倒了下去。

  “呵呵。”叶秋笑了笑,抬手取下了衔在嘴角的烟头。银白的烟灰已经结成了长长一串,但在叶秋挥舞着鼠标敲打着键盘展开艹作的过程中却没有
被震落分毫。摘下的烟头很快被掐灭在了桌上的一个形状古怪的烟灰缸里,叶秋的手飞快地回到了键盘,正准备对对手说点什么,房门却“咣”的一声
被人推开了。

  叶秋没有回头,像是早就在等着这一刻一样,只是问了一句:“来了?”

  “来了。”苏沐橙的回答也同样简单。

  “那就走吧!”叶秋拒绝了对手又一次的邀战,轻轻地从网游荣耀专用的登录器上摘下了一张卡片,起身走到门口时,顺手从一旁的衣架上摘下了
外套。

  夜已经挺深了,嘉世俱乐部却依旧灯火通明。叶秋和苏沐橙走出房间,一路走到了楼道的尽头。这里是一间很宽大的会议室,刚入门便可看到一面
几乎占满整面墙壁的电子显示屏,上面显示得是“荣耀职业联盟”的战绩排行和一些技术统计。

  战绩排行:嘉世战队总排名第十九位,倒数第二。

  对于曾经创造过联赛三连霸的王牌战队,这个成绩分外刺眼,此时却是明晃晃地挂在墙上,像是在无情地嘲笑着众人。

  然而屋里气氛却是一点不见沉闷,相反倒是有些热络。嘉世的队员们此时正众星捧月般地围绕着一个人,对于叶秋踏入会议室他们视而不见,能扫
上一眼的,眼神中也全是冷漠和嘲笑。

  “叶秋,俱乐部已经决定,由新转会来的孙翔接替你的队长职务,一叶之秋今后也由孙翔来艹控。”俱乐部经理看到叶秋进来,立刻回头说道。没
有事先的沟通,没有婉转的表达,一来便是如此开门见山的冰冷通知,无情地就像甩开一团用过的手纸。

  苏沐橙张口要说话,却被叶秋轻轻拉住,朝她微笑着摇了摇头,表示自己并不在意。

  “叶哥,不好意思啊,一来就占了你的位置。”会议桌左手边第一席——嘉世战队队长的专属座位,本该是属于叶秋的座位。孙翔大大咧咧地坐在
上边说了这么一句,眼睛却连瞟都没瞟叶秋一眼,这已经不是漠视,而是一种无视了。他的目光,更多的倒是落在了和叶秋共同进门的苏沐橙身上。

  公道来说,苏沐橙也的确比叶秋更吸引眼球,她可是被称作荣耀职业联盟的头号美人。就算是出了这个圈子,扔到美女如云的娱乐圈,她也扔是不
可多得的漂亮姑娘。

  哪怕嘉世这些天天都能见到苏沐橙的队友,此时看到苏沐橙进来也不由地有些发怔。不过他们倒是很快回过神来,因为此时他们觉得有更重要的焦
点人物值得他们去关注。

  “哈哈,翔哥这话说的,这位置您来坐正合适。”回过神的众人连忙接着之前的话语抢台词。

  “不错,某些人已经老了,过时喽!”

  “一叶之秋也正该由翔哥您来艹控,那才能真正发挥出斗神的实力。”

  这就是众人此时关注的焦点。孙翔,荣耀职业联盟中新一代的天才级选手。去年初入联盟便稳获新人王称号,个人数据相比同年的mvp(最有价值选
手)也不逞多让。本赛季孙翔率领实力平平的越云战队在联赛中排位第八,进军季后赛有望,却中途转会成绩一塌糊涂的嘉世俱乐部。全因为嘉世虽然
战绩糟糕,却拥有在网游荣耀中被誉为斗神的账号角色:战斗法师一叶之秋。

  这个入联盟不满两年的年轻人年纪不大,嘉世这些队员却厚着脸皮以哥相称,显然是已经看出孙翔将是他们嘉世战队未来的老大。孙翔很是舒服地
收下了这些奉承,总算是舍得扭头扫了叶秋一眼,目光中尽是不屑。

  “叶秋,把一叶之秋的账号卡交给孙翔吧!”俱乐部经理说道。

  叶秋纵然再洒脱,此时心中也免不了的有些刺痛。叶秋,一叶之秋,听名便可知道这账号与叶秋的关系,这本就是他初入荣耀这个游戏世界时所创
的账号。十年了,这个账号相伴了叶秋整整十年,当初的菜鸟成了被誉为荣耀教科书级的大高手,当初的小小战斗法师,也成了荣耀中威名远播的“斗
神”。然而七年前踏入职业圈,与俱乐部签订合同后,一叶之秋的所有权已转为俱乐部所有。叶秋早知道会有分离的一天,而这一天,终于来了。

  叶秋的手指微微有一些颤抖。对于一个职业高手来说,稳定的双手是必须的,但现在,颤抖却发生在了叶秋,这个心理素质已经硬得不能再硬的老
牌高手身上。苏沐橙扭过了头,她不想看到这一幕,却又无能为力。

  在众人幸灾乐祸的目光中,一叶之秋那张银白的账号卡被递到了孙翔面前。

  孙翔眼中闪现出兴奋和贪婪的目光,他甘愿转会加入近两年已经大不如前的嘉世俱乐部,为的就是这里拥有一叶之秋这个顶尖的账号。一叶之秋的
原艹纵者叶秋,近些年成绩不佳,与俱乐部不断爆发矛盾,孙翔有百分百的信心可以取而代之。

  “到手了!”接住账号卡的一瞬,孙翔一阵激动,结果却从卡片上感到了一丝抗力。

  孙翔感觉到了叶秋的不舍,傲然一笑说:“放手吧叶哥。看看你的手,居然抖成这个样子。这样的一双手又怎么能发挥出斗神的实力呢?还是让我
来吧!我会让斗神的名号再度响撤整个荣耀的。你?退休啦!”

  这话刚完,孙翔就见到叶秋那一直满不在乎,只在交出一叶之秋时有些不舍的眼中突得闪过一丝锐色,他骇然地发现,叶秋那本在微微颤抖着的双
手,突然间就稳稳地停住了。

  “你喜欢这个游戏吗?”叶秋忽然直视着孙翔问道。

  “什么?”孙翔愕然。

  “如果喜欢,就把这一切当作是荣耀,而不是炫耀。”

  “你说什么呢?这关你什么事了?”孙翔忽然有些失态,这一瞬,不知为何他觉得自己仿佛矮了叶秋一头,他不想这样在气势上输给对方。他来是
为了取代叶秋而来,他来是为了得到斗神一叶之秋而来。

  “收好它。”就在孙翔想重新鼓起气势时,叶秋却已经松开了账号卡,淡淡地说了一句,转身准备离开。

  “叶秋!”就在此时,经理突得出声唤住了他。

  叶秋停步,微微侧了侧头,听到经理已经在他身后说道:“目前俱乐部暂时没有合适的比赛账号给你,你就先在队里担任陪练吧!”

  陪练……一个在联盟中创造过王朝,拿尽所有个人荣誉的高手,此时竟然沦落到要当陪练。

  对于这个安排孙翔大感兴趣,立刻接过声哈哈笑道:“以叶哥你的水平,陪练一定没有问题的,荣耀职业联盟第一陪练非你莫属啊!”

  “呵呵。”受此侮辱,叶秋竟然还笑得出来,回过身来望向经理:“陪练?我看不必了,解约吧!”

  “解约?你是要主动提出解约吗?”经理的神情看起来很值得玩味。

  “不错,我要求解约。”

  “不要冲动啊!”苏沐橙连忙冲了过来阻拦。联盟有联盟的规定,合约期间除特殊原因,任何主动提出解约的一方都需要支付违约金,叶秋目前和
嘉世还有一年半的合约,如果强行要求解约,损失会很大。但对于苏沐橙来说,她更怕的是叶秋的离开。

  “老板还没有来,等老板来了再说吧!”苏沐橙希望叶秋能冷静下来。

  叶秋却早已经瞥到了经理嘴角的那丝讥诮,他苦笑着朝苏沐橙摇了摇头:“沐橙,你还不明白吗?要我走,这本身就是老板的目的。我的存在对于
俱乐部来说,已经没有任何价值了,只是一个薪水包袱。”

  “不会的,怎么会是包袱,你的实力绝不输给任何人。”苏沐橙说。

  “这不单单是实力的问题,这是生意,而我,从来都是没有什么商业价值的。”叶秋说。

  “你本该是有的,是你自己选择了放弃。”经理在此时突然冷冷地插口。

  “不错,这是我的选择。”叶秋说。荣耀职业联盟如今进行得是如火如荼,各路赞助商是层出不穷。联盟中的明星高手那自然是接广告、做代言的
抢手角色。但叶秋作为最为顶尖的选手,却从来都是拒绝任何广告和代言,甚至但连各类采访、新闻发布会都拒绝参加,他就像是个很古老的网民,小
心翼翼地在虚拟世界里藏匿着自己的真实身份。

  对于这一点俱乐部很是不满。他们眼看着身边就落着一座金山,却无法从他身上挖掘到丝毫利益。总算叶秋实力强横,帮俱乐部在联盟中打出了名
气,赚满了荣誉,才让他们可以一直容忍。但随着现在的成绩不佳,一切都已经不复往曰。

  “联盟的商业化让我们存活下来,但现在……”叶秋没能再说下去,他也不知道这样的发展是好是坏。如今的联盟充满了铜气,每一家俱乐部的老
板第一考虑的都是如何利用战队来赚取利益。怀着对游戏的爱,为了荣誉而不懈地努力经营奋斗,叶秋怀念过去那个刚刚发展起来的联盟。而现在,对
荣誉的追求,也不过是为了谋求更大的利益。

  苏沐橙不再说话了,她本就是和叶秋同路走来,也是见证了这一切的老牌高手。她眼中浸满了泪水,她知道,叶秋是真的要离开了,拦他,也只会
让他更痛苦。

  “既然这样,我……”

  “不用了。”叶秋笑着打断了苏沐橙,他知道她要说什么:“放心吧,我还没到绝望的时候,我会回来的。”

  “不错,不愧是我认识的叶秋,真是有志气,那么现在咱们就来谈谈这笔违约金的问题吧!老实说,你在嘉世这么多年,劳苦功高,我们不会这么
绝。你既然想走,大家就坐下来好好谈谈,协商解约怎么样?”

  “直说吧,你们什么条件?”叶秋问。

  “爽快,条件很简单,只要你宣布退役。”经理说。

  “退役。这条件你还说不绝?”苏沐橙大为恼火。叶秋现年是25岁,对于他们电子竞技的职业选手来说已算高龄,在这个年纪上退役并不奇怪。但
叶秋方才已经表明他还不想放弃,嘉世经理这里立刻说出的协商条件就是退役,摆明了是在针对。

  退役选手自然没资格再参加职业比赛。虽然有退役就会有复出,但荣耀职业联盟规定退役选手要满一年后才可以复出。避免有些人今天退役明天复
出这样换队玩。叶秋本就已是职业暮年,每一天都很宝贵,现在却要让他平白浪费掉一年。这一年之后,就算他再重新复出,高龄、没有一年的高水平
比赛来保持状态,单凭昔曰的名气,是否有战队愿意接收他还很成问题。要知道叶秋还有一个很致命的缺陷:他拒绝从事商业活动。

  这看起来根本就是一个无法接受的协商条件,叶秋却很干脆地一点头:“我同意。”

  “你疯了?”苏沐橙大惊。

  “累了这么多年了,休息一年有什么不好?”叶秋笑。

  “你……你到底在想什么啊?”苏沐橙百般不解。

  “没什么。”叶秋转过了头,这边经理早已将几份文书递了上来,叶秋接过一看,笑了笑。对方还真是早有准备啊!想着叶秋已经飞快落笔签上了
名字。

  就要离开了……叶秋最后看了一眼这个自己待了七年的地方,他没有再说什么客气的告别话,默默地转身就准备离开。

  “我送你。”苏沐橙是唯一一个跟在他身后的。
2019-08-21 18:09:51 [scrapy.core.engine] INFO: Closing spider (finished)
2019-08-21 18:09:51 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 459,
 'downloader/request_count': 2,
 'downloader/request_method_count/GET': 2,
 'downloader/response_bytes': 8505,
 'downloader/response_count': 2,
 'downloader/response_status_count/200': 2,
 'elapsed_time_seconds': 0.450203,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2019, 8, 21, 10, 9, 51, 351834),
 'log_count/DEBUG': 2,
 'log_count/INFO': 10,
 'response_received_count': 2,
 'robotstxt/request_count': 1,
 'robotstxt/response_count': 1,
 'robotstxt/response_status_count/200': 1,
 'scheduler/dequeued': 1,
 'scheduler/dequeued/memory': 1,
 'scheduler/enqueued': 1,
 'scheduler/enqueued/memory': 1,
 'start_time': datetime.datetime(2019, 8, 21, 10, 9, 50, 901631)}
2019-08-21 18:09:51 [scrapy.core.engine] INFO: Spider closed (finished)

保存为本地文件

在第一步 爬取网页 中已经用过一次保存为本地文件的操作了,当时是把网页源代码保存为 source.html。
现在把章节标题和章节内容保存为 novel.txt 文件。

import scrapy

class NovelScrapy(scrapy.Spider):

    name = "novel"

    start_urls = ["https://www.biquge5200.cc/0_857/651708.html"]

    def parse(self, response):
        # 拿到章节标题
        title = response.css('div.bookname h1::text').extract_first()
        # 拿到章节内容
        content = '\n'.join(response.css('div#content p::text').extract())

        with open("novel.txt",'w',encoding="utf-8") as f:
            f.write(title)
            f.write("\n")
            f.write(content)
        self.log("保存成功")

运行爬虫,可以看到根目录下输出了 novel.txt 文件,成功地将章节标题和章节内容保存到文件中。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌宅鹿同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值