在上一个案例中实现了单线程爬取整本小说,这里给大家展示一下多线程爬取下载小说,可以大大提升下载速度,主要代码修改也简单。
先简单理解一下什么是多线程爬虫:一种高效的网络爬虫实现方式,通过并发执行多个任务,可以显著提高爬取效率。
这是代码示例:
import requests
import re
import os
import time
import concurrent.futures
# 导入线程池模块
def get_response(html_url):
"""
:param html_url: 请求链接
:return: 响应数据
"""
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0',
}
print(f"Attempting to access URL: {html_url}")
try:
response = requests.get(url=html_url, headers=headers, timeout=10)
response.raise_for_status() # 如果响应状态码不是200,将引发HTTPError异常
return response
except requests.exceptions.RequestException as e:
print(f"Error accessing URL {html_url}: {e}")
return None
def get_url_list(html_url):
"""
:param html_url: 小说目录页
:return: 章节链接列表和书名
"""
response = get_response(html_url)
if response is None:
return [], ""
html_data = response.text
url_list = re.findall(r'<dd><a href ="(.*?)">', html_data)
book_name = re.findall(r'<h1>(.*?)</h1>', html_data)
if book_name:
book_name = book_name[0]
else:
book_name = "Unknown"
return url_list, book_name
def get_content(html_url):
"""
:param html_url: 章节链接
:return: 章节标题和内容
"""
response = get_response(html_url)
if response is None:
return "", ""
html_data = response.text
title = re.findall(r'<h1 class="wap_none">(.*?)</h1>', html_data)
text = re.findall(r'<div id="chaptercontent" class="Readarea ReadAjax_content">(.*?)</div>', html_data, re.DOTALL)
if title and text:
title = title[0]
text = text[0]
text = re.sub(r'<br /><br /> ', '\n', text)
text = re.sub(r'<p class="readinline">.*?</p>', '', text, flags=re.DOTALL)
print(f"成功获取内容: {title}")
return title, text
else:
print(f"未能从指定链接中提取标题或内容{html_url}")
return "", ""
def save_content(book_name, title, text):
"""
:param book_name: 书名
:param title: 章节标题
:param text: 章节内容
"""
file_path = f'{book_name}\\'
if not os.path.exists(file_path):
os.makedirs(file_path)
filename = f"{title}.txt"
with open(file_path + filename, mode='w', encoding='utf-8') as file:
file.write(title)
file.write('\n')
file.write(text)
file.write('\n')
print(f"{title} 下载完成")
def main(home_url):
title, text = get_content(html_url=home_url)
save_content(book_name=book_name, title=title, text=text)
time.sleep(1) # 添加延迟,避免请求过于频繁
if __name__ == '__main__':
url = 'https://www.qu05.cc/html/43958/'
url_list, book_name = get_url_list(html_url=url)
exe= concurrent.futures.ThreadPoolExecutor(max_workers=7)
for url in url_list:
index_url = 'https://www.qu05.cc' + url
exe.submit(main,index_url)
time.sleep(1)
exe.shutdown()
接下来是一些代码流程解释,方便各位新手理解代码
1.get_response这个函数作用是发送请求获取响应,这里的html_url参数会被调用的那个函数传入
2.get_url_list函数---先调用.get_response获取响应再提取响应里的章节链接列表
3..get_content函数提取每个章节链接里的标题和文本
4..save_content函数创建以小说名字为名称的目录并保存数据
5.main函数---获取标题和文本,调用save_content函数保存数据。
大致流程就这些,代码与单线程的主要区别是添加了一个线程池模块并将线程最大数量设置为7。
相比于单线程,多线程在获取数据的速度得到了很大的提升
结果展示:
也许在这里看不出爬取的速度,大家可以自己复制代码实验一下,它与单线程相比速度以及大大提升了,这里理解之后就方便实现多本小说的爬取了 。