小说爬取

网页小说爬取与转换工具:从 HTML 到 Markdown 的自动化解决方案

一、项目概述

本项目是一个用于爬取并格式化网页小说的 Python 脚本,它能够自动抓取小说内容并将其转换为 Markdown 格式,方便用户离线阅读和整理。脚本采用了多线程技术来加速下载过程,同时处理了特殊字符编码和分页问题,确保生成的 Markdown 文件内容完整且格式规范。

主要功能包括:

  • 自动获取小说目录和各章节链接
  • 处理网页中的特殊字符和图标
  • 支持多线程并行下载多个章节
  • 自动检测并合并分页内容
  • 生成格式良好的 Markdown 文件

二、核心功能模块

1. 网页内容获取与解析

1
2
3
4
5
6
7
8
9
10
11
def get_html_text(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0',
'cookie': 'jq_Obj=1; ff54ff0fa0f557568e6e69d35b815894=fcad32351f41e0bacbc3c4dc74b2a721; zh_choose=n; _ga=GA1.1.234751423.1728474308; _ga_YN9485YEKE=GS1.1.1728535222.6.1.1728536491.0.0.0'
}
try:
r = requests.get(url, headers=headers)
r.encoding = r.apparent_encoding # 将响应对象的编码设置为通过分析响应内容推断出的编码,较为准确
return r.text
except:
return ""

这个函数负责发送 HTTP 请求获取网页内容,并自动检测和设置正确的编码,确保中文内容能正常显示。

2. 目录处理与 URL 管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def fetch_and_save_urls(url):
html = get_html_text(url)
soup = BeautifulSoup(html, 'html.parser')
ul_tag = soup.find('ul', class_='chaw_c')
a_all = ul_tag.find_all('a')
urls = []
for a in a_all:
a_url = a.get('href')
a_title = a.text
urls.append({a_title: a_url})
with open('urls.yaml', 'w', encoding='utf-8') as file:
yaml.safe_dump(urls, file)
return urls

def read_urls(url):
try:
with open('urls.yaml', 'r') as file:
print('读取本地:urls')
return yaml.safe_load(file)
except FileNotFoundError:
return fetch_and_save_urls(url)

这部分代码处理小说目录页,提取所有章节的 URL 和标题,并将其保存到 YAML 文件中。下次运行时会优先读取本地文件,避免重复爬取。

3. 特殊字符处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def read_css_dict(css_url):
try:
with open('css_dict.yml', 'r') as file:
css_dict = yaml.safe_load(file)
print('读取本地:css_dict')
return css_dict
except FileNotFoundError:
css_content = get_html_text(css_url)
icon_pattern = r'\.icon-(.*?):before\s*\{\s*content:\s*"(.*?)";\s*\}'
css_dict = re.findall(icon_pattern, css_content)
with open('css_dict.yml', 'w') as file:
yaml.safe_dump(css_dict, file)
return css_dict

def replace_content(css_dict, html_content):
icon_dict = {}
for icon_class, content in css_dict:
try:
content_bytes = bytes([int(content[1:3], 16), int(content[3:], 16)])
decoded_str = content_bytes.decode('utf-16be')
icon_dict[f'icon-{icon_class}'] = decoded_str
except UnicodeDecodeError:
print(f"无法正确解码 icon-{icon_class} 的内容。")

soup = BeautifulSoup(html_content, 'html.parser')
div = soup.find('div', id='mlfy_main_text')
for icon_class, utf8_content in icon_dict.items():
for i_tag in div.find_all('i', {'class': lambda c: icon_class in c}):
if not i_tag.string:
i_tag.string = utf8_content
else:
i_tag.string = i_tag.string + utf8_content

return str(soup)

这部分代码处理网页中的特殊图标和字符。通过解析 CSS 文件,提取图标对应的编码,并将其替换为实际字符,确保内容完整。

4. 分页处理

1
2
3
4
5
6
7
8
9
10
11
12
def if_has_next(url_next):
html = get_html_text(url_next)
pattern = r'url_next:\s*"(.*?)"'
match = re.search(pattern, html)
if match:
url_next_value = match.group(1)
if '_' in url_next_value:
return url_next_value
else:
return None
else:
return None

该函数检测章节是否有下一页,并返回下一页的 URL,用于后续内容合并。

5. Markdown 转换与保存

1
2
3
4
5
6
7
8
def save_page_content(html_content, chapter):
h = html2text.HTML2Text()
markdown_content = ((h.handle(html_content)
.replace('_', '')
.replace(' ', ''))
.replace('铅笔小说网qianbiwenxue.com', ''))
with open('无敌剑域/' + chapter + '.md', 'w', encoding='utf-8') as file:
file.write(markdown_content)

这个函数将 HTML 内容转换为 Markdown 格式,并保存到文件中。它会移除多余的空格和网站标识,使生成的文件更加简洁。

6. 多线程处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def process_html_pages(chapter, url):
thread_id = threading.current_thread().ident
html_content = ''
while url:
html = get_html_text(url)
html_content += replace_content(css_dict, html)
url = if_has_next(url)

save_page_content(html_content, chapter)

end_time = time.time()
elapsed_time = end_time - start_time
return f'线程:{thread_id}下载章节《{chapter}》完毕--当前用时:{elapsed_time:.5f} 秒。'

# 主程序中的线程池部分
with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_task = {executor.submit(process_html_pages, list(item.keys())[0], list(item.values())[0]): item for item in url_s}
for future in concurrent.futures.as_completed(future_to_task):
task = future_to_task[future]
try:
result = future.result()
print(result)
except Exception as exc:
print(f"{list(task.keys())[0]} 产生了一个异常:{exc}")
failed_tasks.append(task)

这部分代码实现了多线程下载功能,通过线程池并行处理多个章节,大大提高了下载效率。同时还包含了失败重试机制,确保所有章节都能成功下载。

三、项目使用方法

  1. 安装必要的依赖库:
1
pip install requests beautifulsoup4 pyyaml html2text
  1. 修改代码中的 URL:
  • url变量设置为目标小说的目录页 URL
  • css_url变量设置为网站的 CSS 文件 URL
  1. 运行脚本:
1
python script_name.py
  1. 等待下载完成,所有章节将以 Markdown 格式保存在 “无敌剑域” 文件夹中

四、项目优化建议

  1. 添加进度条显示,让用户更直观地了解下载进度
  2. 增加配置文件,将 URL、保存路径等参数外部化,提高灵活性
  3. 改进异常处理机制,对不同类型的异常进行分类处理
  4. 添加断点续传功能,支持从上次中断的位置继续下载
  5. 增加对不同网站结构的支持,提高通用性

这个项目展示了如何使用 Python 进行网页爬取和内容处理,通过多线程技术提高效率,并利用正则表达式和 BeautifulSoup 处理复杂的网页结构。代码结构清晰,功能完整,可以作为类似项目的基础框架进行扩展。


小说爬取
http://example.com/2024/10/13/小说爬取/
作者
John Doe
发布于
2024年10月13日
许可协议