python爬虫入门之爬取小说.md

新手教学:用Python爬取小说

我们在学习Python之余总想着让其更具趣味性,可以更好地学习。下面我将讲解如何去从网站中爬取我们想看的小说。让我们枯燥无聊的学习生涯稍微多些趣味。
需要只是一点点对requests库、Beautiful库及python基础知识的了解。
Python版本:Python3.X
运行平台:Windows
IDE:PyCharm
浏览器:Chrome
参考: http://blog.csdn.net/c406495762
本次讲解所有代码已放入GitHub上托管。详细了解请点击

了解爬虫

我们一直口口声声说着用爬虫去爬取某某数据。
那么问题来了,什么是爬虫呢?
网络爬虫,别称网络蜘蛛,它是一段程序(一个脚本),能够模拟浏览器自动的浏览网页,来自动地批量地采集我们需要的资源。
废话不多说,开始今天的爬虫之旅。

爬取章节内容

俗话说得好,饭要一口一口吃,路要一步一步走。我们要爬取每一章内容,须先知晓如何爬取一章内容。
本次爬取网站:笔趣阁
本次爬取小说:《修真四万年》
第一步,根据URL获取网页的HTML信息
在python3中可以用requests库进行网页爬取。

import requests
if __name__=='__main__':	
	target='http://www.biquge.cm/6/6217/3638117.html'
	req=requests.get(url=target)
	print(req.text)
	

运行结果如下:

我们轻松地获得了HTML信息,但为什么会出现乱码呢?
事实上这是由于字符编码问题引起的。往往用requests库抓取中文网页时容易出现乱码问题。那如何解决呢?
下面介绍两种方法:
(1)我们可以在调用response.text()之前使用response.encoding=‘编码格式’
这个方法需要我们进入所爬去网站去查看编码格式。
点击鼠标右键,点击检查,得到如下界面:

点击右侧head标签得到如图:

可以很显然看到编码格式为‘gbk’。
所以我们可以写出代码来解决乱码问题:

import requests
if __name__=='__main__':
	target='http://www.biquge.cm/6/6217/3638117.html'
	req=requests.get(url=target)
	req.encoding='gbk'
	print(req.text)

运行得到:

(2)使用req.content返回bytes型数据,然后将bytes型数据转化为str数据。
代码如下:

import requests
if __name__=='__main__':
	target='http://www.biquge.cm/6/6217/3638117.html'
	req=requests.get(url=target)
	html=req.content
	html_doc=str(html,'gbk')
	print(html_doc)

解决完乱码问题后我们得到了我们需要的能理解看懂的html信息,但是,有很多信息是我们不想看到的,我们只想要小说正文内容。我们不关心div、br这些html标签。
那如何把正文内容从这些众多的html标签中提取出来呢?
这就用到了BeautifulSoup库。
同理按上面方法我们查看第一章目标网页:

我们可以看到文章的所有内容都放在了一个名为div的东西下面,这个东西就是html标签。 HTML标签是HTML语言中最基本的单位,HTML标签是HTML最重要的组成部分。
我们可以看到,我们所需的文本内容在一个div标签下,这个标签是这样的:

div id=“content”

知道了这个内容,我们就可以用BeautifulSoup来提取我们想要的内容了。编写代码如下:

from bs4 import BeautifulSoup
import requests
if __name__=='__main__':
	target='http://www.biquge.cm/6/6217/3638117.html'
	req=requests.get(url=target)
	html=req.content
	html_doc=str(html,'gbk')
	bf=BeautifulSoup(html_doc)
	texts=bf.find_all('div',id="content")
	print(texts)

在解析html之前,先创建一个BeautifulSoup对象,BeautifulSoup函数里面的参数就是我们获得的html信息。然后使用find_all方法,获取html信息中所有id属性为content的div标签。
运行代码查看我们匹配结果:

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

from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
     target = 'http://www.biquge.cm/6/6217/3638117.html'
     req = requests.get(url = target)
     html = req.content
     html_doc=str(html,'gbk')
     bf = BeautifulSoup(html_doc)
     texts = bf.find_all('div', id="content")
     print(texts[0].text)

find_all匹配的返回的结果是一个列表。提取匹配结果后,使用text属性,提取文本内容,滤除br标签。得到:

我们已经顺利获得了一个章节的内容,要想下载正本小说,我们就要获取每个章节的链接。我们先分析下小说目录:http://www.biquge.cm/6/6217/

在网址进行审查得到:

我们看到每个章节的名字存放在了<a>标签里面。<a>标签还有一个href属性。<a>标签定义了一个超链接,用于从一张页面链接到另一张页面。<a> 标签最重要的属性是 href 属性,它指示链接的目标。

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

http://www.biquge.cm/6/6217/3638117.html

第一章 法宝坟墓

我们可以看到<a> 标签中href属性存放的属性值/1_1094/5403177.html是章节URLhttp://www.biqukan.com/1_1094/5403177.html的后半部分。那这样,我们就可以根据<a>标签的href属性值获得每个章节的链接和名称了。

编写代码如下:

from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
     target = 'http://www.biquge.cm/6/6217/'
     req = requests.get(url = target)
     html = req.content
     html_doc=str(html,'gbk')
     div_bf = BeautifulSoup(html_doc)
     div = div_bf.find_all('div', id='list' )
     print(div[0])

运行结果:

接下来再匹配每一个<a>标签,并用a.get(‘href’)方法获取href的属性值,使用a.string方法获取章节名。代码如下:

from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
     server = 'http://www.biquge.cm/'
     target =  'http://www.biquge.cm/6/6217/'
     req = requests.get(url = target)
     html = req.content
     html_doc=str(html,'gbk')
     div_bf = BeautifulSoup(html_doc)
     div = div_bf.find_all('div', id='list'  )
     a_bf = BeautifulSoup(str(div[0]))
     a = a_bf.find_all('a')
     for each in a:
          print(each.string, server + each.get('href'))

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

整合代码:

# -*- coding:UTF-8 -*-

from bs4 import BeautifulSoup
import requests, sys


class downloader(object):
    # 类说明下载笔趣阁修真四万年,self只有在类的方法中才会有,
    # 独立的函数或方法是不必带有self的。self在定义类的方法时是必须有的,
    # 虽然在调用时不必传入相应的参数。

    def __init__(self):  # 初始化变量
        # 爬取图书网站,self.server='http://www.biquge.cm/' 的意思
        # 就是把外部传来的参数server的值赋值给类downloader自己的属性
        # 变量self.server,下面函数可共用
        self.server = 'http://www.biquge.cm/'
        self.target = 'http://www.biquge.cm/6/6217/'
        # 爬取图书主目录所在网址,用来获取每一章节url
        self.names = []  # 存放章节名
        self.urls = []  # 存放章节链接
        self.nums = 0  # 章节数

    def get_download_url(self):  # 获取下载链接
        req = requests.get(url=self.target)  # 获得该网页的html信息
        html = req.content  # #获得html文件
        html_doc=str(html,'gbk')
        div_bf = BeautifulSoup(html_doc)  # 创建一个BeautifulSoup对象
        div = div_bf.find_all('div', id='list')
        # 匹配div标签,获取id属性list的内容,用find_all是因为获得的是列表
        a_bf = BeautifulSoup(str(div[0]))  # 以list中的内容构建Beautiful对象
        a = a_bf.find_all('a')  # 找到所有a标签,a标签中为每一章网址
        self.nums = len(a[:])  # 索引从第一章到最后一章,记数
        for each in a[:]:
            self.names.append(each.string)  # 添加a标签下的字符串即章节名
            self.urls.append(self.server + each.get('href'))
        # 获取每一章链接,用get()方法获取a标签中href属性

    def get_contents(self, target):  # 获取章节内容
        req = requests.get(url=target)
        html = req.content
        html_doc=str(html,'gbk')
        bf = BeautifulSoup(html_doc)
        texts = bf.find_all('div', id="content")
        # 获取div标签id属性content的内容
        texts = texts[0].text.replace('\xa0'*8,'\n')
        # 剔除"'div'标签及'id=content'"和br/,
        # 提取texts内容中text文本
        return texts

    def writer(self, name, path, text):  # 将爬取的文章内容写入文件
        with open(path, 'a' ,encoding='utf-8')as f:
            # ‘a’表示追加到文件,encoding='utf-8'表示以utf-8进行编码 
            f.write(name + '\n')  # write方法写入章节名并换行
            f.writelines(text)  # writelines方法写入文章内容
            f.write('\n')  # 换行写完一章


# 当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行
if __name__ == "__main__":
    dl = downloader()
    dl.get_download_url()
    print('《修真四万年》开始下载:')
    for i in range(dl.nums):
        dl.writer(dl.names[i], '修真四万年.txt', dl.get_contents(dl.urls[i]))
        sys.stdout.write("  已下载:%.3f%%" % float(i / dl.nums) + '\r')
        # sys.stdout是映射到打开脚本的窗口
        sys.stdout.flush()
    # 在Linux系统下,必须加入sys.stdout.flush()才能一秒输一个数字
    # 在Windows系统下,加不加sys.stdout.flush()都能一秒输出一个数字
    print('《修真四万年》下载完成')

猜你喜欢

转载自blog.csdn.net/qq_41813030/article/details/82764061