乐者为王

Do one thing, and do it well.

使用Scrapy爬取小说(2)

接上文。

将抓取的小说内容保存成本地文件是通过在命令行指定-o选项实现的。虽然工作的很好,但是有两个缺点:一是把所有小说内容保存到单个文件会导致该文件太大,用文本编辑器打开随机浏览的速度非常慢;二是小说章节不是按照顺序保存的,导致阅读指定的章节内容很不方便。

再写个小工具按章节内容分割小说文件?无需如此麻烦。我们可以在Scrapy中直接将每个章节保存为单独的文本文件。Scrapy中的Item Pipeline就是干这类事情的。看下面的Scrapy架构图:

scrapy-architecture

当Item在Spider中被收集之后,它们会被传递到Item Pipeline,这些Pipeline组件按照一定的顺序执行对Item的处理,同时也决定此Item是否继续通过,或是被丢弃而不再进行处理。

以下是Item Pipeline的一些典型应用:

  • 清理HTML数据
  • 验证爬取的数据
  • 查重
  • 将爬取结果保存到数据库中

编写Item Pipeline

编写自己的Item Pipeline非常简单,每个Item Pipeline都是实现以下方法的Python类:

1
process_item(self, item, spider)

此外,下面的方法是可选实现的:

1
2
open_spider(self, spider)  # 该方法在Spider被开启时调用
close_spider(spider)       # 该方法在Spider被关闭时调用

明白原理后,我们就可以开始编写自己的Item Pipeline。以下就是将小说的每个章节写成单独文本文件的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
class TxtPipeline(object):
  def process_item(self, item, spider):
    # 标题和内容都是列表类型,必须先转换成字符串
    title = ''.join(item['title'])
    content = ''.join(item['content'])
    # 使用章节名来创建文件
    # 使用strip()来过滤非法字符r'\/:*?"<>|'
    filename = '{}.txt'.format(title.strip())
    f = open(filename, 'w')
    f.write(title)
    f.write(content)
    f.close()
    return item

启用Item Pipeline

要启用Pipeline组件,你必须将它添加到novel/settings.py的ITEM_PIPELINES配置中,就像下面这样:

1
2
3
ITEM_PIPELINES = {
  'novel.pipelines.TxtPipeline' : 300,
}

Pipeline后面的整数值确定它们的运行顺序,Item按数字从低到高通过每个Pipeline。通常将这些值定义在0-1000范围内。

运行Spider

在项目的根目录中执行如下的命令(因为不再把所有的小说内容保存为单个文件,所有不需要指定-o选项):

1
scrapy crawl novelspider

没有报错的话,等个几分钟,就能看到很多文本文件躺在自己的电脑上面。

novel-txt-list

Comments