python爬虫实战(1)——网站小说
整本小说的爬取保存
- 目标
- 大致思路
- 页面的爬取解析—XPath
- 请求网页内容
- 解析网页内容
- 正文爬取与解析
- 单个页面数据获取
- 爬取所有页面
- 数据清洗
经过学习基础,我们学以致用一下子,爬取小说,注意这个小说本身是免费的哦,以后再进阶。
本次为实战记录,笔者很多碎碎念可忽略
目标
基于requests库和lxml中的xpath编写的爬虫,目标小说网站域名http://www.365kk.cc/
这是网上找的,小说网址很多,而且没有很多反扒机制,新手友好!
大致思路
其实也可以不写,梳理一下吧,虽然是空话
主要分三步(如同大象进冰箱hh)
- 获取网页内容,通过requests库实现;
- 解析网页内容,得到其中我们想要的部分,通过xpath实现;
- 将解析出的内容储存到文本文档中;
我们把要做的具体化,首先选取一本自己喜欢的小说,笔者以此为例,是的就是这么重口。
《我是一具尸体》
页面的爬取解析—XPath
要干嘛明确一下:
- 获取目标书籍的基本信息,包括书籍的书名、作者、简介——这些信息应该都在同一个页面中获取,即上面展示的页面;
- 获取目标书籍每一章节的标题和内容——不同章节在不同的页面,不同页面之间可以通过下一页按序跳转;
- 正文部分的存储格式应便于阅读,不能把所有文字都堆积在一起,也不能包括除了正文之外的其他无关内容;
因此,我们首先尝试请求书籍的主页,获取基本信息;紧接着再从书籍的第一章开始,不断地请求“下一页”,直到爬取整本书,并将它们以合适的格式储存在文本文档中。
笔者是用Google Chrome,在首页点击右键 —> 检查:
可以看出,浏览器下方弹出了一个窗口,这里显示的就是该页面的源代码,我们选中的内容位于一个<h1>
标签中。点击右键 -> 复制 -> 复制 XPath,即可得到书名的XPath路径,也就是书名在网页中的位置。
书名:/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1
从书籍的首页中,同理我们可以获取的信息主要包括:
- 书名 作者 最后更新时间
作者:/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]
最后更新时间:/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]
简介:/html/body/div[4]/div[1]/div/div/div[2]/div[2]
请求网页内容
使用基础的python爬虫网页请求方法: requests 库直接请求。
在请求网页时,我们需要将我们的爬虫伪装成浏览器,具体通过添加请求头 headers 实现。
请求头以字典的形式创建,可以包括很多内容,这里只设置四个字段:User-Agent, Cookie, Host 和 Conection。
在刚才打开的页面中,点击 网络(英文版是Network),刷新页面,找到其中的第一个文件 1053/ ,打开 标头 -> 请求标头 ,即可得到想要的字段数据。
import requests# 请求头,添加你的浏览器信息后才可以正常运行
headers= {'User-Agent': '...','Cookie': '...','Host': 'www.365kk.cc','Connection': 'keep-alive'
}
# 小说主页
main_url = "http://www.365kk.cc/1/1053/"
# 使用get方法请求网页
main_resp = requests.get(main_url, headers=headers)
# 将网页内容按utf-8规范解码为文本形式
main_text = main_resp.content.decode('utf-8')
print(main_text)
可以看出,我们成功请求到了网站内容,接下来只需对其进行解析,即可得到我们想要的部分。
解析网页内容
我们使用 lxml 库来解析网页内容,具体方法为将文本形式的网页内容创建为可解析的元素,再按照XPath路径访问其中的内容,代码如下:
import requests
from lxml import etree# 请求头
headers= {'User-Agent': '...','Cookie': '...','Host': 'www.365kk.cc','Connection': 'keep-alive'
}# 小说主页
main_url = "http://www.365kk.cc/1/1053/"
# 使用get方法请求网页
main_resp = requests.get(main_url, headers=headers)
# 将网页内容按utf-8规范解码为文本形式
main_text = main_resp.content.decode('utf-8')# 将文本内容创建为可解析元素
main_html = etree.HTML(main_text)# 依次获取书籍的标题、作者、最近更新时间
# main_html.xpath返回的是列表,因此需要加一个[0]来表示列表中的首个元素
# /text() 表示获取文本
bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0]
author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0]
update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0]
introduction = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[2]/text()')[0]
# 输出结果以验证
print(bookTitle)
print(author)
print(update)
print(introduction)
输出结果如下:
至此,使用基本的网页请求get方法获取目标页面中的特定内容结束,接下来就是正文解析了,
go go go
正文爬取与解析
开始爬取正文。首先尝试获取单个页面的数据,再尝试设计一个循环,依次获取所有正文数据
单个页面数据获取
打开第一章,获取章节标题和正文的XPath路径如下:
书名://*[@id="container"]/div/div/div[2]/h1
内容://*[@id="content"]
按照与上文一致的方法请求并解析网页内容,代码如下:
注意到在用这个xpath路径的时候我们是只要里面的文字部分所以要多加一个/text()
import requests
from lxml import etree# 请求头
headers= {'User-Agent': '...','Cookie': '...','Host': 'www.365kk.cc','Connection': 'keep-alive'
}# 当前页面链接
url = 'http://www.365kk.cc/1/1053/10094192.html'
resp = requests.get(url, headers)
text = resp.content.decode('utf-8')html = etree.HTML(text)title = html.xpath('//*[@id="container"]/div/div/div[2]/h1/text()')[0]
contents = html.xpath('//*[@id="content"]/text()')print(title)
for content in contents:print(content)
可以看出,我们成功获取了小说第一章第一页的标题和正文部分
接下来我们将它储存在一个txt文本文档中,关于文件读取类型主要有(并不全,只是本代码用到的)
'w'
: 清空原文档,重新写入文档 open(filename, ‘w’)'r'
: 仅读取文档,不改变其内容 open(filename, ‘r’)'a'
: 在原文档之后追加内容 open(filename, ‘a’)
文档命名为之前获取的书名 bookTitle.txt
,完整的代码如下:
import requests
from lxml import etree# 请求头
headers= {'User-Agent': '...','Cookie': '...','Host': 'www.365kk.cc','Connection': 'keep-alive'
}# 小说主页
main_url = "http://www.365kk.cc/1/1053/"# 使用get方法请求网页
main_resp = requests.get(main_url, headers=headers)# 将网页内容按utf-8规范解码为文本形式
main_text = main_resp.content.decode('utf-8')# 将文本内容创建为可解析元素
main_html = etree.HTML(main_text)bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0]
author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0]
update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0]
introduction = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[2]/text()')[0]# 第一章页面链接
url = 'http://www.365kk.cc/1/1053/10094192.html'resp = requests.get(url, headers)
text = resp.content.decode('utf-8')
html = etree.HTML(text)
title = html.xpath('//*[@id="container"]/div/div/div[2]/h1/text()')[0]
contents = html.xpath('//*[@id="content"]/text()')with open(bookTitle + '.txt', 'w', encoding='utf-8') as f:f.write(title)for content in contents:f.write(content)# 在储存文件时,每储存一段,就写入两个换行符 `\n`,避免大段文字堆积使格式更便于阅读f.write('\n\n')f.close()
运行结束后,可以看到在代码文件的同路径中,已经生成了一个文本文档。至此,我们已经完成了单个页面的数据爬取和存储,接下来只要设计循环,实现顺序爬取所有页面即可。
爬取所有页面
我们注意到,正文的每个页面底部,都有一个按钮下一页,其在网页中的结构为:
在XPath路径的末尾添加 @href 用于获取属性 href 的值:
//*[@id="container"]/div/div/div[2]/div[3]/a[3]/@href
注意到如果一章结束,下一页和下一章的链接就会有些差别
观察不同页面的链接,可以看出前缀是一致的,区别仅在后缀上,比如第一章第一页和第一章第二页的链接分别为:
http://www.365kk.cc/1/1053/10094192.html
http://www.365kk.cc/1/1053/10094192_2.html
因此,我们只需要获取下一页的链接后缀,再与前缀拼接,即可获得完整的访问链接。代码如下:
# 获取下一页链接的函数
def next_url(next_url_element):nxturl = 'http://www.365kk.cc/1/1053/'# rfind('/') 获取最后一个'/'字符的索引index = next_url_element.rfind('/') + 1nxturl += next_url_element[index:]return nxturlurl1 = '/1/1053/10094192_2.html'
url2 = '10094193.html'print(next_url(url1))
print(next_url(url2))
在爬取某一页面的内容后,我们获取下一页的链接,并请求该链接指向的网页,重复这一过程直到全部爬取完毕为止,即可实现正文的爬取。
在这一过程中,需要注意的问题有:
某一章节的内容可能分布在多个页面中,每个页面的章节标题是一致的,这一标题只需存储一次;
请求网页内容的频率不宜过高,频繁地使用同一IP地址请求网页,会触发站点的反爬虫机制,禁止你的IP继续访问网站;
爬取一次全文耗时较长,为了便于测试,我们需要先尝试爬取少量内容,代码调试完成后再爬取全文;
爬取的起点为第一章第一页,爬取的终点可以自行设置;
按照上述思想,爬取前4个页面作为测试,完整的代码如下:
import requests
from lxml import etree
import time
import random# 获取下一页链接的函数
def next_url(next_url_element):nxturl = 'http://www.365kk.cc/1/1053/'# rfind('/') 获取最后一个'/'字符的索引index = next_url_element.rfind('/') + 1nxturl += next_url_element[index:]return nxturl# 请求头,需要添加你的浏览器信息才可以运行
headers= {'User-Agent': '...','Cookie': '...','Host': 'www.365kk.cc','Connection': 'keep-alive'
}# 小说主页
main_url = "http://www.365kk.cc/1/1053/"# 使用get方法请求网页
main_resp = requests.get(main_url, headers=headers)# 将网页内容按utf-8规范解码为文本形式
main_text = main_resp.content.decode('utf-8')# 将文本内容创建为可解析元素
main_html = etree.HTML(main_text)bookTitle = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/h1/text()')[0]
author = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[1]/text()')[0]
update = main_html.xpath('/html/body/div[4]/div[1]/div/div/div[2]/div[1]/div/p[5]/text()')[0]# 调试期间仅爬取4个页面
maxPages = 4
cnt = 0
# 记录上一章节的标题
lastTitle = ''# 爬取起点
url = 'http://www.365kk.cc/1/1053/10094192.html'
# 爬取终点
endurl = 'http://www.365kk.cc/1/1053/10094194.html'while url != endurl:cnt += 1 # 记录当前爬取的页面if cnt > maxPages:break # 当爬取的页面数超过maxPages时停止resp = requests.get(url, headers)text = resp.content.decode('utf-8')html = etree.HTML(text)title = html.xpath('//*[@class="title"]/text()')[0]contents = html.xpath('//*[@id="content"]/text()')# 输出爬取进度信息print("cnt: {}, title = {}, url = {}".format(cnt, title, url))with open(bookTitle + '.txt', 'a', encoding='utf-8') as f:if title != lastTitle: # 章节标题改变f.write(title) # 写入新的章节标题lastTitle = title # 更新章节标题for content in contents:f.write(content)f.write('\n\n')f.close()# 获取"下一页"按钮指向的链接next_url_element = html.xpath('//*[@class="section-opt m-bottom-opt"]/a[3]/@href')[0]# 传入函数next_url得到下一页链接url = next_url(next_url_element)sleepTime = random.randint(2, 5) # 产生一个2~5之间的随机数time.sleep(sleepTime) # 暂停2~5之间随机的秒数print("complete!")
运行结果如下:
数据清洗
观察我们得到的文本文档,可以发现如下问题:
- 缺乏书籍信息,如之前获取的书名、作者、最后更新时间;
- 切换页面时,尤其是同一章节的不同页面之间空行过多;
- 每章节第一段缩进与其他段落不一致;
- 不同章节之间缺乏显眼的分隔符;
为了解决这些问题,我们编写一个函数 clean_data()
来实现数据清洗,清洗后的文档中,每段段首无缩进,段与段之间仅空一行,不同章节之间插入20个字符 -
用以区分,问题得以解决。
完整代码,需要自取哦
小说爬虫
相关文章:

python爬虫实战(1)——网站小说
整本小说的爬取保存 目标大致思路页面的爬取解析—XPath请求网页内容解析网页内容正文爬取与解析单个页面数据获取爬取所有页面 数据清洗 经过学习基础,我们学以致用一下子,爬取小说,注意这个小说本身是免费的哦,以后再进阶。 本次…...
git: ‘lfs‘ is not a git command. see ‘git --help‘
在克隆hugging face里面的项目文件的时候,需要用到git lfs,本文介绍安装git lfs方法 在Ubuntu下 curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash sudo apt-get install git-lfs在Windows下 到这个链…...
python案例
这猜单词游戏。具体步骤如下: 导入random模块,用于随机选择单词。 设置初始生命次数为3。 创建一个单词列表words,其中包含了一些单词。 使用random.choices()函数从单词列表中随机选择一个单词作为秘密单词secret_word。 创建一个clue列表&a…...

leetcode 63. 不同路径 II
2023.8.9 这题是不同路径I的升级版,在路径上增加了障碍物,有障碍物的地方无法通过。 我的思路依然还是使用动态规划,dp[i][j]的含义依然是到(i,j)这个位置的路径个数。只需要在dp数组中将有障碍物的地方赋为…...

c语言每日一练(5)
前言:每日一练系列,每一期都包含5道选择题,2道编程题,博主会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,暑假时三天之内必有一更,到了开学之后,将看学业情…...

pycharm配置conda虚拟环境
📕作者简介:热编程的贝贝,致力于C/C、Java、Python等多编程语言,热爱跑步健身,喜爱音乐的一位博主。 📗本文收录于贝贝的日常汇报系列,大家有兴趣的可以看一看 📘相关专栏深度学习、…...

ubuntu 如何命令行打开系统设置(Wifi,网络,应用程序...)
关于GNOME GNOME 是一个自由、开放源代码的桌面环境,它运行在 Linux 和其他类 UNIX 操作系统上。它是 GNU 项目的一部分,旨在为 Linux 操作系统提供一个现代化、易于使用的用户界面。 GNOME 桌面环境包括许多应用程序,例如文件管理器、文本编…...
MySQL DQL 数据查询
文章目录 1.SELECT 语句2.SELECT 子句3.FROM 子句4.WHERE 子句5.GROUP BY 子句6.HAVING 子句7.ORDER BY 子句8.LIMIT 子句9.DISTINCT 子句10.JOIN 子句11.UNION 子句12.查看数据表记录数13.检查查询语句的执行效率14.查看 SQL 执行时的警告参考文献 1.SELECT 语句 MySQL 的 SE…...

深度学习基础知识笔记
深度学习要解决的问题 1 深度学习要解决的问题2 应用领域3 计算机视觉任务4 视觉任务中遇到的问题5 得分函数6 损失函数7 前向传播整体流程8 返向传播计算方法1 梯度下降 9 神经网络整体架构11 神经元个数对结果的影响12 正则化和激活函数1 正则化2 激活函数 13 神经网络过拟合…...

怎么系统的学习机器学习、深度学习?当然是看书了
目录 前言 内容简介 学完本书,你将能够 作者简介 本书目录 京东自购链接 前言 近年来,机器学习方法凭借其理解海量数据和自主决策的能力,已在医疗保健、 机器人、生物学、物理学、大众消费和互联网服务等行业得到了广泛的应用。自从Ale…...

无涯教程-Perl - binmode函数
描述 此函数设置在区分两者的操作系统上以二进制形式读取和写入FILEHANDLE的格式。非二进制文件的CR LF序列在输入时转换为LF,在LF时在输出时转换为CR LF。这对于使用两个字符分隔文本文件中的行的操作系统(MS-DOS)至关重要,但对使用单个字符的操作系统(Unix,Mac OS,QNX)没有影…...
Spring Boot Maven package时显式的跳过test内容
在pom.xml的编译插件部分显式的增加一段内容: <plugin> <!-- maven打包时,显式的跳过test部分 --><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.…...

排序算法————基数排序(RadixSort)
基数排序的概念: 什么是基数排序???基数排序是一种和快排、归并、希尔等等不一样的排序...它不需要比较和移动就可以完成整型的排序。它是时间复杂度是O(K*N),空间复杂度是O(KM&…...
leetcode做题笔记75颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函数的情况下解决…...

聊一下互联网开源变现
(点击即可收听) 互联网开源变现其实是指通过开源软件或者开放源代码的方式,实现收益或盈利。这种方式越来越被广泛应用于互联网行业 在互联网开源变现的模式中,最常见的方式是通过捐款、广告、付费支持或者授权等方式获利。 例如,有些开源软件…...
PHP日期差计算器,计算两个时间相差 年/月/日
1. 计算两个日期相隔多少年,多少月,多少天示例:laravel框架实现 /*** 天数计算* return \Illuminate\Http\JsonResponse*/public function loveDateCal(){$start_date $this->request(start_date);$end_date $this->request(end_date…...

20230812在WIN10下使用python3将SRT格式的字幕转换为SSA格式
20230812在WIN10下使用python3将SRT格式的字幕转换为SSA格式 2023/8/12 20:58 本文的SSA格式以【Batch Subtitles Converter(批量字幕转换) v1.23】的格式为准! 1、 缘起:网上找到的各种各样的字幕转换软件/小工具都不是让自己完全满意! 【都…...

matlab使用教程(13)—稀疏矩阵创建和使用
使用稀疏矩阵存储包含众多零值元素的数据,可以节省大量内存并加快该数据的处理速度。sparse 是一种属性,可以将该属性分配给由 double 或 logical 元素组成的任何二维 MATLAB 矩阵。通过 sparse 属性,MATLAB 可以: • 仅存储矩…...

UI美工设计的主要职责(合集)
UI美工设计的主要职责1 职责: 1、执行公司的规章制度及专业管理办法; 2、 负责重点项目的原型设计和产品流程设计、视觉设计,优化网站和移动端的设计流程和规范,制定产品 UI/UE规范及文档编写; 3、负责使用PS、AI、illustrator、MarkMan、…...

【前端二次开发框架关于关闭eslint】
前端二次开发框架关于关闭eslint 方法一方法二方法三方法四:以下是若想要关闭项目中的部分代码时: 方法一 在vue.config.js里面进行配置: module.exports {lintOnSave:false,//是否开启eslint保存检测 ,它的有效值为 true || false || err…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...