在互联网的世界里,不少人都有过在盗版小说网站阅读的经历。那些网站,充斥着各种令人厌烦的广告,随处可见。这些盗版网站的运作模式,往往是运用爬虫技术,从正版网站窃取小说资源,批量下载后堆砌在自家网页上。为了盈利,他们与广告商勾结,凭借用户的浏览量赚取报酬。而那些流氓广告更是恶劣至极,在程序设计上极具侵扰性,在特定时间间隔后,会自动打开广告,极大地影响阅读体验。那么,今天就给大家带来一个教程,教大家如何亲手将喜欢的小说通过合法途径爬取到本地,享受纯净、舒适的阅读时光。
前提声明:
1、本教程仅用于学习和研究使用,不得用于商业行为。
2、请确保在合法合规的前提下使用本代码。
一、前言
在数字化浪潮下,网络小说资源极为丰富,很多人都希望能将心仪的小说保存下来以便离线阅读。爬虫技术就能帮我们实现这个需求,但在开始之前,再次强调:请尊重版权,仅将爬虫技术用于合法的个人学习与研究用途。
二、明确目标并分析网页
在这里我们选择爬取的小说网站:小说,以小说:夜的命名术为例。
打开开发者工具(F12)进行分析,比如小说的标题、章节列表、章节内容等信息。这一步就像是绘制地图,为后续的爬虫工作指引方向。
三、单章小说下载
3.1、发送请求并获取数据
首先,我们要获取小说的网址为:
python
https://www.bigudu.cc/137_137176/648241408.html
然后导入数据请求模块,定制请求头,发送请求数据。
python
import requests
url = "https://www.bigudu.cc/137_137176/648241408.html"
headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)
response = response.text
3.2、解析数据
我们想要拿到网页源码中的小说标题以及小说内容,这时候就要解析数据,解析数据,在这里我们通过xpath语句来实现:
python
tree = etree.HTML(response)
title = tree.xpath('//div[@class="content"]/h1/text()')[0]
details = tree.xpath('//*[@id="content"]/text()')[:-2]
for content in detalis:
f.write(title + '\n')
f.write(content+"\n")
3.3、保存数据
有了数据之后,就要把数据保存起来,我们定义一个文件夹"夜的命名术",导入os模块。通过os.path.join函数将文件夹路径和文件名拼接成问政的文件路径。最后在open函数中使用这个完整的文件路径来打开文件进行写入操作,这样就可以把文件保存到指定文件夹下了。
python
tree = etree.HTML(response)
title = tree.xpath('//div[@class="content"]/h1/text()')[0]
details = tree.xpath('//*[@id="content"]/text()')[:-2]
file_path = os.path.join("夜的命名术",title+'.txt')
with open(file_path, 'a', encoding='utf-8') as f:
f.write(title + '\n')
for content in details:
f.write(content+"\n")
print(content)
四、整本小说下载
想要获取整本小说,最重要的就是解析目录页的网页源码。一般小说网站的目录页会包含各章节链接,然后我们去观察页面章节来链接的规律。
如图所示,各章节链接皆存于标签为"dd"的元素的"href"属性内。我们首先需获取小说目录页的源代码,运用 xpath 语句精准提取出链接数据,并进行字符串的拼接操作以获取完整的章节链接。接下来,针对每一章节定制相应的请求对象,参照单章内容抓取的方式,将每一章的数据分别存储至指定文件夹中,由此,便可成功获取整本小说的文字内容。
python
import requests
from lxml import etree
import os
base_url = "https://www.blqudu.cc/137_137176/"
headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
html_data = requests.get(url=base_url, headers=headers).text
tree = etree.HTML(html_data)
url_list= tree.xpath('//div[@class="listmain"]//a/@href')
for url in url_list:
new_url ="https://www.bigudu.cc" + url
response = requests.get(url=new_url, headers=headers).text
tree = etree.HTML(response)
title = tree.xpath('//div[@class="content"]/h1/text()')[0]
details = tree.xpath('//*[@id="content"]/text()')[:-2]
file_path = os.path.join("夜的命名术", title + '.txt')
with open(file_path, 'a', encoding='utf-8') as f:
f.write(title + '\n')
for content in details:
f.write(content + '\n')
print(content)
五、多线程采集小说
上面整本小说的在频繁的操作磁盘IO,下载相当的慢,我们可以采用多线程的方式去下载,只需要把每一块功能封装成函数,然后用线程池的一些模块,进行使用:
python
import requests
from lxml import etree
import os
import concurrent.futures
def get_response(base_url):
"""
发送请求的函数
:param base_url: 请求链接
:return: response响应对象
"""
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
response = requests.get(url=base_url, headers=headers).text
return response
def get_list_url(base_url):
"""
获取章节url
:param base_url: 小说目录页面
:return:
"""
html_data = get_response(base_url)
tree = etree.HTML(html_data)
url_list = tree.xpath('//div[@class="listmain"]//a/@href')
return url_list
def get_content(base_url):
"""
获取小说内容及标题
:param base_url: 小说章节url
:return:
"""
response = get_response(base_url)
tree = etree.HTML(response)
html_data = get_response(base_url)
title = tree.xpath('//div[@class="content"]/h1/text()')[0]
details = tree.xpath('//*[@id="content"]/text()')[:-2]
return title, details
def download_file(title, details):
file_path = os.path.join("夜的命名术", title + '.txt')
with open(file_path, 'a', encoding='utf-8') as f:
f.write(title + '\n')
for content in details:
f.write(content + '\n')
print(content)
def main(home_url):
title,details =get_content(home_url)
download_file(title, details)
if __name__ == '__main__':
first_url = "https://www.blqudu.cc/137_137176/"
url_list = get_list_url(base_url=first_url)
exe = concurrent.futures.ThreadPoolExecutor(max_workers=8)
for url in url_list:
new_url = "https://www.bigudu.cc" + url
exe.submit(main, new_url)
exe.shutdown()
六、小说分类下载
我们先采集分类页面中的小说并进行下载。首先,将分类页面的 URL 作为起始链接传入,随后执行 get_novel_id 函数,对分类页面里的所有小说链接进行解析。接着,使用 for 循环遍历每一本小说,从而获取每本小说目录的 URL,依此方法循环往复操作下去。
python
import requests
from lxml import etree
import os
def get_response(base_url):
"""
发送请求的函数
:param base_url: 请求链接
:return: response响应对象
"""
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
}
response = requests.get(url=base_url, headers=headers).text
return response
def get_list_url(base_url):
"""
获取章节url
:param base_url: 小说目录页面
:return:
"""
html_data = get_response(base_url)
tree = etree.HTML(html_data)
url_list = tree.xpath('//div[@class="listmain"]//a/@href')
name = tree.xpath('//*[@id="info"]/h1/text()')[0]
return name,url_list
def get_content(base_url):
"""
获取小说内容及标题
:param base_url: 小说章节url
:return:
"""
response = get_response(base_url)
tree = etree.HTML(response)
html_data = get_response(base_url)
title = tree.xpath('//div[@class="content"]/h1/text()')[0]
details = tree.xpath('//*[@id="content"]/text()')[:-2]
return title, details
def get_novel_id(base_url):
"""
获取分类链接
:param base_url:分类页小说链接
:return:
"""
novel_data = get_response(base_url)
tree = etree.HTML(novel_data)
novel_id = tree.xpath('//ul//span[@class="s2"]/a/@href')
return novel_id
def download_file(title, details):
file_path = os.path.join("夜的命名术", title + '.txt')
with open(file_path, 'a', encoding='utf-8') as f:
f.write(title + '\n')
print(title)
for content in details:
f.write(content + '\n')
# print(content)
def main(home_url):
novel_id = get_novel_id(home_url)
for novel_id in novel_id:
novel_url = 'https://www.bigudu.cc' + novel_id
name,url_list = get_list_url(novel_url)
print(name,url_list)
for url in url_list:
new_url = "https://www.bigudu.cc" + url
title, details = get_content(new_url)
download_file(title, details)
if __name__ == '__main__':
first_url = "https://www.blqudu.cc/class/1_1.html"
main(first_url)
七、总结
在着手筹备小说搜索GUI界面的进程中,我原以为技术实现与交互设计会是主要挑战,然而实际操作后却发现,一个关键难点横亘在前,即寻觅一个拥有丰富小说资源储备的网站作为数据依托。我起初选定的目标网站,其搜索框所对应的域名已被转让,这一突发状况使得原计划中以该网站为基础构建搜索功能的设想难以施行,鉴于此,当前的小说搜索GUI界面开发计划只能暂且搁置,以待后续寻找更为合适的解决方案。