最近竟然开始磕起了黄晓明和尹正的CP!!!
但是万恶的爱某艺不好好更新剧集,居然搞起了超前点映… WTF???有什么是我这个贫穷而又尊贵的VIP用户不能看的吗???
于是我决定开始看小说了!找个网站把小说爬下来慢慢看吧~
先物色一个投缘的小说网站吧
就它了!→ http://www.moyanxsw.com/binbianbushihaitanghong/
第一步:分析网页
首先需要了解要爬取网站的页面,查看网页源代码。然后根据网页源代码的结构,想好代码的步骤和思路。
在网页开发者模式中查看页面的源代码(F12)
- 先来看一下页面的请求方式:
在开发者模式的Network里找到.html的请求,可以看到请求方式是GET请求,也没有带什么特殊的请求参数之类的~ 比较简单。
(如果没有看到.html的请求,刷新一下页面就出来了)
- 然后再看代码结构:
(不太熟悉html的小伙伴可以使用圈圈里的小箭头,点一下自己想要看的元素,然后右侧就会自动跳转到该元素对应的代码区域的~四不四很简单)
小说网站的话会有两个比较重要的页面:
首先是目录页面,这里会有每一章的标题和链接,点击就会跳转到对应章节。
在目录页面中,可以看到 所有的目录都放在 < div class="book_list">
中的<a>
标签里,其中每一章的标题里带着一半链接,比如“第一节”是“href="7489410.html"
”,那后面实现跳转的时候就需要拼接一下了。
然后是正文页面,还是以第一章来举栗子:
先看它的地址:http://www.moyanxsw.com/binbianbushihaitanghong/ 加上前面章节里放的链接 7489410.html 就可以了,比较简单。
再看正文部分,是放在< div class="contentbox">
中的,也不复杂~
3. 确定实现思路:
①首先可以通过目录页面获得小说所有章节的链接地址。
②然后通过第一步获得的链接地址,爬取每一章节的正文内容。
③最后把爬取到的正文保存在本地的txt文档中。
第二步:开始编程
1. 定义库
import requests
from bs4 import BeautifulSoup
requests 库是用来发网络请求的,BeautifulSoup 是用来解析和提取 HTML 数据的。
关于它俩的详细说明可以参考我之前的文章呀:爬虫学习笔记——爬取单个网页里的所有图片(入门)
2. 对各步骤分别定义函数
2.1 获取网站数据并解析网页 getHtml():
获取网页数据还是用 requests.get()方法。
res = requests.get(url,params=params,headers=headers)
这样获取到的结果是一个 HTTP 状态码。
还需要用.text
方法再来获取一下这个结果里的文本信息。
#获取网页数据
html = requests.get(url)
htmlCode = html.text
有的网站对于反爬虫力度比较大,请求的时候可以构造一个请求头加上,请求头(Request Headers)在最前面开发者模式中的 Network 里就可以找到。
今天爬取的这个页面很简单,就直接 GET 请求 url 即可,可以啥都不带。
(我承认这段话是从我上篇文章里直接复制来的……)
接下来开始解析网页。
BeautifulSoup4 是将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,之后就可以利用 soup 加标签名轻松地获取这些标签的内容了。
参考beautifulsoup菜鸟教程
BeautifulSoup(markup,'html.parser')
- markup 是被解析的 html 格式的内容
- html.parser 表示解析用的解析器
#解析网页
soup = BeautifulSoup(htmlCode,'html.parser')
2.2 获取章节目录 getList():
因为之前确定的实现思路是通过目录来获得小说所有章节的链接地址,然后分别对每一章内容进行爬取,所以接下来先要获得章节的目录。
前面分析过 在目录页面中,所有的目录链接都放在 < div class="book_list">
中的<a>
标签里,那么这里就可以用 soup.find('div', class_="book_list").find_all('a')
来获得所有符合条件的 <a>
标签了。
然后再看 <a>
标签的结构:
我们最终需要拿到的是 a 标签中的 href 属性值,获得href属性值的方法是a['href']
(这个小说的目录都是“第一节”、“第二节”这样,没什么用,就不用取出来了,如果是带有章节名称的话,可以考虑用 .text方法来获得 a 标签里的文字内容。)
这部分的代码是这样的:
soup = getHtml(url)
#查找所有章节的链接
listBox = soup.find('div', class_="book_list").find_all('a')
#新建列表用来储存list的url
bookLists = []
for i in listBox:listUrl = i['href']#放进列表里bookLists.append(listUrl)
2.3 获取各章节正文 getNovelContent():
根据前面分析页面的结果,正文部分,是放在各个章节对应的正文页面的 < div class="contentbox">
中的,直接用 soup.find('div', class_="contentbox")
就可以获得对应 div 中的所有内容,以第一章为例,打印出来看一下。
现在这个内容是包含了 html 代码部分的,所以还需要用 .text
来获取其中的内容,再打印看看~ 是不是干净多了~
但是发现每一章后面还有几句话有点不好看,比如: “本章有错误,我要提交上一章 返回目录 下一章”、“小提示:按 回车[Enter]键 返回书目,按 ←键 返回上一页, 按 →键 进入下一页。” 之类的……
回到网页里看一下:
原因是正文底部的控件和文字也都包含在了 < div class="contentbox">
中了,暂时想不到其它比较好的获取正文内容的方法了,就只能把正文取出来以后再把多余的文字删掉了。
之后再简单处理一下,用.strip()
方法去掉收尾空格以后这部分就完成啦~
soup = getHtml(url)
#获得需要的正文内容
content = soup.find('div', class_="contentbox").text
content = content.strip()
contentCut = content.replace("本章未完,点击[ 下一页 ]继续阅读-->>","").replace("本章有错误,我要提交上一章 返回目录 下一章","").replace("小提示:按 回车[Enter]键 返回书目,按 ←键 返回上一页, 按 →键 进入下一页。","")
需要注意的是这里 getNovelContent() 的入参必须是章节正文页面的链接哟~ 就是目录页面地址和各章节 href 属性值拼接后的地址。
2.4 获得标题 getTitle():
保存下来之前我还想把标题拿到,这样保存的时候就可以直接用小说的标题作为文件名了。
(对于一篇小说似乎看起来没什么用,但是如果要爬取多本小说的话,这个方法还是很有用的~)
还是在目录页来看,这个标题是放在<div class="book_info">
中的<h1 >
标签里,所以直接用 soup.find('div',class_="book_info").find('h1').string
就可以拿出来了。
soup = getHtml(url)
title = soup.find('div',class_="book_info").find('h1').string
2.5 爬取下来后保存到本地 saveNovel():
保存时需要注意两点:
① 拼接完整的章节链接地址后才能调用获取各章节正文的函数 getNovelContent()
#获取各章节目录,这里的url是目录页的url
bookLists = getList(url)
for listUrl in bookLists:#拼接完整的章节链接地址chapterUrl = url + listUrlchapterContent = getNovelContent(chapterUrl)
② 读写文件的方法
爬取小说和爬取图片的方法不同,小说需要将爬取到的内容写入本地文件中。
Python 的文件读取有两个最基本的用法,其一 with open(…) as …
,其二 open()
配合close()
。这里我选择用第一种方法啦~
简单扩展一下:
File.open() 方法 用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数。
File.write() 方法 用于向文件中写入指定字符串。
File.close() 方法 用于关闭一个已打开的文件,关闭后的文件不能再进行读写操作。使用 close() 方法关闭文件是一个好的习惯哟~。
(想起了 “把大象装进冰箱,总共分几步?”哈哈哈)
说明一下open()函数:
open(name[, mode[, buffering]])
参数说明:
- name : 一个包含了你要访问的文件名称的字符串值。
- mode : mode 决定了打开文件的模式:只读,写入,追加等。这个参数是非强制的,默认文件访问模式为只读®。
- buffering : 如果 buffering 的值被设为 0,就不会有寄存。如果 buffering 的值取 1,访问文件时会寄存行。如果将 buffering 的值设为大于 1 的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。(这个没看明白)
r: 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
a: 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
因为爬取小说的时候是一章一章地写入文件的,所以打开文件的模式需要用 “a”——追加模式
这部分的代码如下↓,在打开文件的时候加上了解码方式’utf-8’。
#获得章节目录
bookLists = getList(url)
#获得小说标题
title = getTitle(url)
#num用于下载时计数
num = 1
#以追加模式打开文件
with open('%s.txt'%title, 'a' ,encoding='utf-8') as f:for listUrl in bookLists:#拼接完整的章节链接地址chapterUrl = url + listUrl#获得对应章节正文内容chapterContent = getNovelContent(chapterUrl)#写入章节正文f.write(chapterContent)print('***第{}章下载完成***'.format(num))num += 1
#关闭文件
f.close()
第三步:执行
run run run!
文件默认是与该 .py 文件在同一目录下的。
愉快地去看小说吧~
附上完整代码:
import requests
from bs4 import BeautifulSoupdef getHtml(url):#获取网页数据html = requests.get(url)htmlCode = html.text#解析网页soup = BeautifulSoup(htmlCode,'html.parser')#返回解析后的页面内容return soupdef getTitle(url):soup = getHtml(url)title = soup.find('div',class_="book_info").find('h1').stringreturn title#获取各章节目录链接
def getList(url):soup = getHtml(url)#查找所有章节的链接listBox = soup.find('div', class_="book_list").find_all('a')#新建列表用来储存list的urlbookLists = []for i in listBox:listUrl = i['href']#放进列表里bookLists.append(listUrl)return bookLists#获取各章节正文
def getNovelContent(url):soup = getHtml(url)#获得需要的正文内容content = soup.find('div', class_="contentbox").textcontent = content.strip()#这个地方我处理的有点粗糙了,需要根据正式情况来用contentCut = content.replace("本章未完,点击[ 下一页 ]继续阅读-->>","").replace("本章有错误,我要提交上一章 返回目录 下一章","").replace("小提示:按 回车[Enter]键 返回书目,按 ←键 返回上一页, 按 →键 进入下一页。","")return contentCut#保存到本地
def saveNovel(url):bookLists = getList(url)title = getTitle(url)num = 1with open('%s.txt'%title, 'a' ,encoding='utf-8') as f:for listUrl in bookLists:#拼接完整的章节链接地址chapterUrl = url + listUrlchapterContent = getNovelContent(chapterUrl)f.write(chapterContent)print('***第{}章下载完成***'.format(num))num += 1f.close()if __name__ == '__main__':url='http://www.moyanxsw.com/binbianbushihaitanghong/'saveNovel(url)
后记
-
这种按照目录 对目录中各个章节页面正文内容 进行爬取的方法有一个缺点,比如今天爬取的这篇文章,其实它目录中的每一章又被分为了几页来展示,按照今天这种方法只能爬取到每章的第一页内容,剩下的内容都没有拿到……
但是对于那种目录跳转到的页面中有完整的一章内容的小说,还是可以用的。
所以我决定!找时间换一种方法,再写一篇! -
其实《鬓边不是海棠红》中,齐王爷和宁九郎这对老年CP也超好磕的啊喂!
一个是贱嗖嗖忠犬王爷,一个是清冷傲娇梨园尚书,满屏幕都能感受到那种温柔的珍惜呀!(文字来自豆瓣小组 丢腻腻同学~)
撒花~~~✿✿ヽ(°▽°)ノ✿