Python3网络爬虫小说内容 - Go语言中文社区

Python3网络爬虫小说内容


转载作者:

http://blog.csdn.net/c406495762

转载文章:

https://blog.csdn.net/c406495762/article/details/78123502#1-requests%E5%AE%89%E8%A3%85

因转载作者所使用的小说网打不开。

    本文在以上文章的基础上自己爬虫其他网站小说,并对其中代码做了进一步解释,转载文章中的代码如果看不懂的可以参考本篇,新手学习,不足之处,请多多指教!

 

爬虫小说实战

语言:python3

软件:Pycharm2018

模块:BeautifulSoup、requests

requests库

基础方法如下: 

                      
官方中文教程地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

requests库的开发者为我们提供了详细的中文教程,查询起来很方便。本文不会对其所有内容进行讲解,摘取其部分使用到的内容,进行实战说明。

首先,让我们看下requests.get()方法,它用于向服务器发起GET请求,不了解GET请求没有关系。我们可以这样理解:get的中文意思是得到、抓住,那这个requests.get()方法就是从服务器得到、抓住数据,也就是获取数据。让我们看一个例子(以 http://www.zongheng.com/为例)来加深理解:

# -*- coding=UTF-8 -*-
import requests

if __name__ == '__main__':
    target = 'http://www.zongheng.com//'
    req = requests.get(url=target)
    print(req.text)

requests.get()方法必须设置的一个参数就是url,因为我们得告诉GET请求,我们的目标是谁,我们要获取谁的信息。运行程序看下结果:


左侧是我们程序获得的结果,右侧是我们在www.gitbook.cn网站审查元素获得的信息。我们可以看到,我们已经顺利获得了该网页的HTML信息。这就是一个最简单的爬虫实例,可能你会问,我只是爬取了这个网页的HTML信息,有什么用呢?客官稍安勿躁,接下来进入我们的实战正文。


(1) 实战背景


小说网站-纵横中文网:URL:http://www.zongheng.com/

本人基本不看小说,随意选了一个,自行练习请换其他小说网站

(2) 小试牛刀


我们先看下《大国良臣》小说的第一章内容,URL:http://book.zongheng.com/showchapter/912802.html


我们先用已经学到的知识获取HTML信息试一试,编写代码如下:

# -*- coding=UTF-8 -*-
import requests

if __name__ == '__main__':
    target = 'http://book.zongheng.com/chapter/912802/59031605.html'
    req = requests.get(url=target)
    print(req.text)


运行代码,可以看到如下结果:


可以看到,我们很轻松地获取了HTML信息。但是,很显然,很多信息是我们不想看到的,我们只想获得如右侧所示的正文内容,我们不关心div、br这些html标签。如何把正文内容从这些众多的html标签中提取出来呢?这就是本次实战的主要内容。

(3)Beautiful Soup

**爬虫的第一步,获取整个网页的HTML信息,我们已经完成。接下来就是爬虫的第二步,解析HTML信息,提取我们感兴趣的内容。**对于本小节的实战,我们感兴趣的内容就是文章的正文。提取的方法有很多,例如使用正则表达式、Xpath、Beautiful Soup等。对于初学者而言,最容易理解,并且使用简单的方法就是使用Beautiful Soup提取感兴趣内容。


一个强大的第三方库,都会有一个详细的官方文档。我们很幸运,Beautiful Soup也是有中文的官方文档。

URL:http://beautifulsoup.readthedocs.io/zh_CN/latest/

同理,我会根据实战需求,讲解Beautiful Soup库的部分使用方法,更详细的内容,请查看官方文档。

现在,我们使用已经掌握的审查元素方法,查看一下我们的目标页面,你会看到如下内容:


不难发现,文章的所有内容都放在了一个名为div的“东西下面”,这个"东西"就是html标签。HTML标签是HTML语言中最基本的单位,HTML标签是HTML最重要的组成部分。不理解,没关系,我们再举个简单的例子:

一个女人的包包里,会有很多东西,她们会根据自己的习惯将自己的东西进行分类放好。镜子和口红这些会经常用到的东西,会归放到容易拿到的外侧口袋里。那些不经常用到,需要注意安全存放的证件会放到不容易拿到的里侧口袋里。

html标签就像一个个“口袋”,每个“口袋”都有自己的特定功能,负责存放不同的内容。显然,上述例子中的div标签下存放了我们关心的正文内容。这个div标签是这样的:

<div class="content" itemprop="acticleBody">
1
细心的朋友可能已经发现,除了div字样外,还有itemprop和class。itemprop和class就是div标签的属性,content和acticleBody是属性值,一个属性对应一个属性值。这东西有什么用?它是用来区分不同的div标签的,因为div标签可以有很多,我们怎么加以区分不同的div标签呢?就是通过不同的属性值。

仔细观察目标网站一番,我们会发现这样一个事实:itemprop属性为acticleBody的div标签,独一份!这个标签里面存放的内容,是我们关心的正文部分。

知道这个信息,我们就可以使用Beautiful Soup提取我们想要的内容了,编写代码如下:

# -*- coding:UTF-8 -*-
import requests
from  bs4 import BeautifulSoup

if __name__ == "__main__":
    target = 'http://book.zongheng.com/chapter/912802/59031605.html'
    req = requests.get(url=target)
    html = req.text
    bf = BeautifulSoup(html)
    texts = bf.find_all('div', itemprop="acticleBody")
    print(texts)


在解析html之前,我们需要创建一个Beautiful Soup对象。BeautifulSoup函数里的参数就是我们已经获得的html信息。然后我们使用find_all方法,获得html信息中所有itemprop属性为acticleBody的div标签。find_all方法的第一个参数是获取的标签名,第二个参数class_是标签的属性,为什么不是class,而带了一个下划线呢?因为python中class是关键字,为了防止冲突,这里使用class_表示标签的class属性,class_后面跟着的acticleBody就是属性值了。看下我们要匹配的标签格式:

<div class="content", itemprop="acticleBody">

这样对应的看一下,是不是就懂了?可能有人会问了,为什么不是find_all(‘div’,class="content", itemprop="acticleBody")?这样其实也是可以的,属性是作为查询时候的约束条件,添加一个 itemprop="acticleBody"条件,我们就已经能够准确匹配到我们想要的标签了,所以我们就不必再添加class这个属性了。运行代码查看我们匹配的结果:


我们可以看到,我们已经顺利匹配到我们关心的正文内容,但是还有一些我们不想要的东西。比如div标签名,p标签,以及各种空格。怎么去除这些东西呢?我们继续编写代码:

# -*- coding:UTF-8 -*-
import requests
from  bs4 import BeautifulSoup

if __name__ == "__main__":
    target = 'http://book.zongheng.com/chapter/912802/59031605.html'
    req = requests.get(url=target)
    html = req.text
    bf = BeautifulSoup(html)
    texts = bf.find_all('div', itemprop="acticleBody")
    print(texts[0].text.replace('xa0', 'nn'))


find_all匹配的返回的结果是一个列表。提取匹配结果后,使用text属性,提取文本内容,滤除p标签。随后使用replace方法,剔除空格,替换为回车进行分段。 在html中是用来表示空格的。replace(’xa0’,’nn’)就是去掉下图的空格符号,并用回车代替:


程序运行结果如下:


可以看到,我们很自然的匹配到了所有正文内容,并进行了分段。我们已经顺利获得了一个章节的内容,要想下载正本小说,我们就要获取每个章节的链接。我们先分析下小说目录:

URL:http://book.zongheng.com/showchapter/912802.html


通过审查元素,我们发现可以发现,这些章节都存放在了class属性为chapter-list clearfix的ul标签下,选取部分html代码如下:

<ul class="chapter-list clearfix">
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59031605.html" target="_blank" title="第一章 抛诱饵钓上大鱼,贪婪心愿者上钩 字数:2333 更新时间:2019-12-17 22:29 ">第一章 抛诱饵钓上大鱼,贪婪心愿者上钩</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59048094.html" target="_blank" title="第二章 设圈套滴水不漏,大意者幡然醒悟 字数:2241 更新时间:2019-12-19 16:54 ">第二章 设圈套滴水不漏,大意者幡然醒悟</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59056665.html" target="_blank" title="第三章  父母官衣冠楚楚,小百姓发迹人生 字数:2324 更新时间:2019-12-20 15:15 ">第三章  父母官衣冠楚楚,小百姓发迹人生</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59069686.html" target="_blank" title="第四章  掌柜的犒劳伙计,摔破烂重建家园 字数:2461 更新时间:2019-12-21 23:33 ">第四章  掌柜的犒劳伙计,摔破烂重建家园</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59102389.html" target="_blank" title="第五章  下山来夜宿小院,初听闻世间人家 字数:3010 更新时间:2019-12-25 14:42 ">第五章  下山来夜宿小院,初听闻世间人家</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59102398.html" target="_blank" title="第六章  清风客栈识文博,京城之貌似繁华 字数:2838 更新时间:2019-12-25 14:43 ">第六章  清风客栈识文博,京城之貌似繁华</a>
</li>
<li class="col-4">
<a href="http://book.zongheng.com/chapter/912802/59112264.html" target="_blank" title="第七章  李凡一路见不平,显身手偶遇佳人 字数:2771 更新时间:2019-12-26 16:39 ">第七章  李凡一路见不平,显身手偶遇佳人</a>
</li>
</ul>


在分析之前,让我们先介绍一个概念:父节点、子节点、孙节点。<ul></ul>限定了<ul>标签的开始和结束的位置,他们是成对出现的,有开始位置,就有结束位置。我们可以看到,在<ul>标签包含<li>标签,那这个<li>标签就是<ul>标签的子节点,<li>标签又包含<a>标签,那么<dt>标签和<a>标签就是<ul>标签的孙节点。有点绕?那你记住这句话:谁包含谁,谁就是谁儿子!

**他们之间的关系都是相对的。**比如对于<li>标签,它的子节点是<a>标签,它的父节点是<ul>标签。这跟我们人是一样的,上有老下有小。

看到这里可能有人会问,这有好多<li>标签和<a>标签啊!不同的<li>标签,它们是什么关系啊?显然,兄弟姐妹喽!我们称它们为兄弟结点。
好了,概念明确清楚,接下来,让我们分析一下问题。我们看到每个章节的名字存放在了<a>标签里面。<a>标签还有一个href属性。这里就不得不提一下<a>标签的定义了,<a>标签定义了一个超链接,用于从一张页面链接到另一张页面。<a>标签最重要的属性是 href 属性,它指示链接的目标。

我们将之前获得的第一章节的URL和<a> 标签对比看一下:

http://book.zongheng.com/chapter/912802/59031605.html
<a href="http://book.zongheng.com/chapter/912802/59031605.html" target="_blank" title="第一章 抛诱饵钓上大鱼,贪婪心愿者上钩 字数:2333 更新时间:2019-12-17 22:29 ">第一章 抛诱饵钓上大鱼,贪婪心愿者上钩</a>


不难发现,<a> 标签中href属性存放的属性值http://book.zongheng.com/chapter/912802/59031605.html就是章节URLhttp://book.zongheng.com/chapter/912802/59031605.html。其他章节也是如此!那这样,我们就可以根据<a> 标签的href属性值获得每个章节的链接和名称了。

总结一下:小说每章的链接放在了class属性为chapter-list clearfix的<div>标签下的<a>标签中。先匹配class属性为chapter-list clearfix的<div>标签,再匹配<a>标签。编写代码如下:

if __name__ == "__main__":
    target = 'http://book.zongheng.com/showchapter/912802.html'
    req = requests.get(url = target)
    html = req.text
    div_bf = BeautifulSoup(html)
    div = div_bf.find_all('ul', class_ = 'chapter-list clearfix')
    print(div[0])


还是使用find_all方法,运行结果如下:


很顺利,接下来再匹配每一个<a>标签,并提取章节名和章节文章。如果我们使用Beautiful Soup匹配到了下面这个<a>标签,如何提取它的href属性和<a>标签里存放的章节名呢?

<a href="http://book.zongheng.com/chapter/912802/59031605.html" target="_blank" title="第一章 抛诱饵钓上大鱼,贪婪心愿者上钩 字数:2333 更新时间:2019-12-17 22:29 ">第一章 抛诱饵钓上大鱼,贪婪心愿者上钩</a>

方法很简单,对Beautiful Soup返回的匹配结果a,使用a.get(‘href’)方法就能获取href的属性值,使用a.string就能获取章节名,编写代码如下:

# -*- coding=UTF-8 -*-
import requests
from bs4 import BeautifulSoup

if __name__ == "__main__":
    target = 'http://book.zongheng.com/showchapter/912802.html'
    req = requests.get(url = target)
    html = req.text
    div_bf = BeautifulSoup(html)
    div = div_bf.find_all('ul', class_ = 'chapter-list clearfix')
    a_bf = BeautifulSoup(str(div[0]))
    a = a_bf.find_all('a')
    for each in a:
        print(each.string, each.get('href'))


因为find_all返回的是一个列表,里边存放了很多的<a>标签,所以使用for循环遍历每个<a>标签并打印出来,运行结果如下。

(4)整合代码

每个章节的链接、章节名、章节内容都有了。接下来就是整合代码,将获得内容写入文本文件存储就好了。编写代码如下:

# -*- coding:UTF-8 -*-
'''
爬取小说内容思路步骤:
    1.获取小说各个章节的下载链接
    2.获取章节内容
    3.将章节内容写入到磁盘文件中

'''
'''
from module_name import function_name as fn  从module_name模块中导入function_name函数并利用as指定函数的别名为fn
import module_name 导入module_name模块
BeautifulSoup 是一个可以从 HTML 或 XML 文件中提取数据的 Python 库
'''
from bs4 import BeautifulSoup
import requests, sys

"""
类说明:下载<纵横中华网>小说《大国良臣》
class class_name():     #class:来标识创建一个类
"""
class downloader():

    def __init__(self):
        '''
    def: 定义一个函数(方法)
    __init__: python中的特殊方法,当根据类创建一个实例时python会自动运行此方法(根据实例中传入的实参给类中的属性赋值)
    self:类中的方法必须存在此形参,并且位于多个形参最前面,是一个指向实例本身的引用,让实例可以访问类中的属性和方法
        '''
        self.server = 'http://book.zongheng.com/'
        self.target = 'http://book.zongheng.com/showchapter/912802.html'
        self.names = []            #存放章节名
        self.urls = []            #存放章节链接
        self.nums = 0            #章节数

    """
    函数说明:获取下载链接
    """
    def get_download_url(self):
        '''通过requests模块的get方法中的text方法可以获取链接的HTML代码'''
        req = requests.get(url = self.target)
        html = req.text
        div_bf = BeautifulSoup(html)
        '''通过变量div_bf的find_all可以找出所有标签为ul 属性为class="chapter-list clearfix"的所有内容'''
        div = div_bf.find_all('ul', class_="chapter-list clearfix")
        '''获取标签为ul 属性为class="chapter-list clearfix"的第一组'''
        a_bf = BeautifulSoup(str(div[0]))
        """获取a_bf变量内容中标签为a的全部html标签内容存放于a列表中"""
        a = a_bf.find_all('a')
        """获取a列表中的元素的数目即a标签的数目即章节的数目"""
        self.nums = len(a)

        for each in a:
            """
            each中的每一个元素例如:<a class="new" href="http://book.zongheng.com/chapter/912802/59678502.html" target="_blank" title="六十章 假大人煽动贼人,小无赖举报立功 字数:3518 更新时间:2020-02-17 09:20 ">六十章 假大人煽动贼人,小无赖举报立功</a>
            将a标签中的字符串追加到names属性中
            将each中a标签的href属性中的信息获取追加到urls中
            """
            self.names.append(each.string)
            self.urls.append(each.get('href'))

    """
    函数说明:获取章节内容
    Parameters:
        target - 下载连接(string)
    """
    def get_contents(self, target):
        req = requests.get(url = target)
        html = req.text
        bf = BeautifulSoup(html)
        texts = bf.find_all('div', itemprop="acticleBody")
        texts = texts[0].text.replace('xa0','n')
        return texts

    """
    函数说明:将爬取的文章内容写入文件
    Parameters:
        name - 章节名称(string)
        path - 当前路径下,小说保存名称(string)
        text - 章节内容(string)
    """
    def writer(self, name, path, text):
        write_flag = True
        """
        with:不再需要打开文件时自动关闭文件
        open:打开文件,若不存再则创建
            path:文件目录,相对路径去当前.py的工作目录下面找,绝对路径去对应目录找文件
            第二个参数:r/w/a/r+ 
                r:只读取文件没有就新建 
                w:只写入文件没有就新建 
                a:在原有文件内容的基础上追加内容没有就新建 
                r+:可读取可写入没有就新建 
            encoding='utf-8':编码格式,大部分为UTF-8,也有GBK等
        f   文件的别名
        """
        with open(path, 'a', encoding='utf-8') as f:
            f.write(name + 'n')
            f.writelines(text)
            f.write('nn')

"""
if __name__ == "__main__":只是一个虚拟标识,标识主函数入口位置(没有此标识代码依然可以执行不受影响)
    __name__”是Python的内置变量,用于指代当前模块
"""
if __name__ == "__main__":
    """实例化downloader类为dl"""
    dl = downloader()
    dl.get_download_url()
    print('《大国良臣》开始下载:')
    print(dl.nums)
    for i in range(dl.nums):
        dl.writer(dl.names[i], 'E:大国良臣.txt', dl.get_contents(dl.urls[i]))
        sys.stdout.write("  已下载:%.3f%%" %  float(i/dl.nums) + 'r')
        sys.stdout.flush()
        # print(dl.names[i], '大国良臣.txt', dl.get_contents(dl.urls[i]))
    print('《大国良臣》下载完成')

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/spark_Streaming/article/details/104359461
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 18:31:41
  • 阅读 ( 1223 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢