第一个爬虫程序

51次阅读

共计 5479 个字符,预计需要花费 14 分钟才能阅读完成。

最近热播的电视剧《全职高手》是由小说改编而成的,爬取小说当做练习吧~~ 本文练习爬取第一章的章节标题和章节内容,并且保存到本地文件中。

创建一个 scrapy 项目

$ scrapy init first_scrapy

创建完成后目录结构跟下面???? 应该是一样的,在 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') as f:
            f.write(html_str)
        self.log('保存文件成功')
  • name 爬虫的名字,启动爬虫时需要用到它;
  • start_urls 要爬取哪个网页的源代码,填写网页网址;
  • parse Scrapy 帮你请求网址,拿到源代码后塞给 response 参数,接下来对 response 操作提取需要的数据。

运行爬虫

$ scrapy crawl novel    <= 这里的 novel 就是上面代码中的 name 值 

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

提取数据

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

调试环境

Scrapy 提供了调试环境,方便我们测试提取的数据是否正确。

命令行中输入???? 脚本,其中 scrapy shell 是固定写法,后面跟要爬取网页的网址。

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

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

2019-08-01 09:22:29 [scrapy.utils.log] INFO: Scrapy 1.5.1 started (bot: scrapybot)
2019-08-01 09:22:29 [scrapy.utils.log] INFO: Versions: lxml 4.2.5.0, libxml2 2.9.8, cssselect 1.0.3, parsel 1.5.0, w3lib 1.19.0, Twisted 18.7.0, Python 3.7.0 (default, Sep 18 2018, 18:47:22) - [Clang 9.1.0 (clang-902.0.39.2)], pyOpenSSL 18.0.0 (OpenSSL 1.1.0i  14 Aug 2018), cryptography 2.3.1, Platform Darwin-17.7.0-x86_64-i386-64bit
2019-08-01 09:22:29 [scrapy.crawler] INFO: Overridden settings: {'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter', 'LOGSTATS_INTERVAL': 0}
2019-08-01 09:22:29 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.memusage.MemoryUsage']
2019-08-01 09:22:29 [scrapy.middleware] INFO: Enabled downloader middlewares:
['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-01 09:22:29 [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-01 09:22:29 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2019-08-01 09:22:29 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2019-08-01 09:22:29 [scrapy.core.engine] INFO: Spider opened
2019-08-01 09:22:29 [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 0x10bc049e8>
[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 0x10bc04748>
[s]   spider     <DefaultSpider 'default' at 0x10bf0ac88>
[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 NovelSpider(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)

运行爬虫

$ scrapy crawl novel

可以看到分别打印了章节标题和标题内容。

保存为本地文件

在第一步 爬取网页 中已经用过一次保存为本地文件的操作了,当时是把网页源代码保存为 source.html。

现在把章节标题和章节内容保存为 novel.txt 文件。

import scrapy

class NovelSpider(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())
        
        # 保存为本地文件 novel.txt
        with open('novel.txt', 'w') as f:
            f.write(title)
            f.write('\n')
            f.write(content)
        self.log('保存文件成功')

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

正文完
 0