Python 实战:用 requests 与正则表达式批量抓取起点免费小说章节

张开发
2026/6/3 15:05:49 15 分钟阅读
Python 实战:用 requests 与正则表达式批量抓取起点免费小说章节
1. 准备工作与环境搭建在开始爬取起点小说网之前我们需要先准备好Python开发环境。我推荐使用Python 3.6及以上版本因为这个版本对字符串处理和正则表达式的支持更加完善。如果你是Python新手建议先安装Anaconda它集成了Python和常用的数据分析库省去了很多配置的麻烦。安装必要的库非常简单只需要在命令行中执行以下命令pip install requestsrequests库是Python中最流行的HTTP客户端库相比Python自带的urllib它的API设计更加人性化使用起来更加顺手。我曾经尝试过用urllib来抓取网页光是处理cookie和header就让我头疼不已而requests只需要几行代码就能搞定。为了模拟浏览器访问我们还需要准备一个User-Agent。这个可以在浏览器的开发者工具中找到或者直接使用我提供的这个通用User-Agentheaders { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 }2. 分析起点小说网页面结构以《冒牌大英雄》为例我们先打开小说的目录页https://book.qidian.com/info/131957。按下F12打开开发者工具切换到Elements标签页仔细查看章节列表的HTML结构。我发现起点小说网的章节链接都放在li>import requests import re import time base_url https://book.qidian.com/info/131957 response requests.get(base_url, headersheaders) html response.text接下来是最关键的部分 - 用正则表达式提取章节链接。这里有个小技巧先用浏览器查看网页源代码找到章节链接的规律。我经过多次尝试发现这个正则表达式效果最好chapter_pattern re.compile(rli>for url in chapter_urls: try: full_url https: url chapter_response requests.get(full_url, headersheaders) chapter_html chapter_response.text # 提取标题和正文 title_pattern re.compile(rh3 classj_chapterName(.*?)/h3, re.S) content_pattern re.compile(rdiv classread-content j_readContent(.*?)/div, re.S) title title_pattern.search(chapter_html).group(1) content content_pattern.search(chapter_html).group(1) # 简单的HTML清理 content content.replace(p, \n).replace(/p, ) # 保存文件 filename f{title}_{int(time.time())}.html with open(filename, w, encodingutf-8) as f: f.write(fh1{title}/h1) f.write(content) time.sleep(1) # 礼貌性延时 except Exception as e: print(f处理章节{url}时出错{str(e)}) continue4. 优化与异常处理在实际运行中我发现有几个常见问题需要注意。首先是编码问题起点小说网使用的是UTF-8编码但有时响应头中可能没有明确指定所以最好手动设置chapter_response.encoding utf-8其次是反爬机制起点小说网会对频繁的请求进行限制。我建议在请求之间加入随机延时并考虑使用代理IPimport random time.sleep(random.uniform(1, 3))对于大规模爬取可以考虑使用会话(Session)来保持连接减少TCP握手开销session requests.Session() response session.get(url, headersheaders)最后记得添加完善的异常处理。网络请求可能会因为各种原因失败良好的异常处理能让程序更加健壮try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() except requests.exceptions.RequestException as e: print(f请求失败{str(e)}) return None5. 数据存储与后续处理我们选择将每个章节保存为单独的HTML文件文件名包含标题和时间戳。这种存储方式有几个优点一是便于管理每个章节独立二是保留了HTML格式方便后续阅读三是时间戳可以避免文件名冲突。如果你想把所有章节合并成一个文件可以稍作修改with open(full_novel.html, a, encodingutf-8) as f: f.write(fh1{title}/h1) f.write(content)对于更高级的需求比如建立全文搜索或者制作电子书可以考虑将内容存入数据库或者生成EPUB格式。SQLite是个不错的选择它不需要单独的服务器Python原生支持import sqlite3 conn sqlite3.connect(novel.db) c conn.cursor() c.execute(CREATE TABLE IF NOT EXISTS chapters (title text, content text, url text, timestamp integer)) # 保存章节时 c.execute(INSERT INTO chapters VALUES (?,?,?,?), (title, content, full_url, int(time.time()))) conn.commit()6. 爬虫伦理与注意事项在编写爬虫时我们必须遵守网站的robots.txt规则。起点小说网的robots.txt对爬虫有一定限制因此在实际操作中要注意控制请求频率不要给服务器造成过大负担仅用于个人学习不要大规模爬取不要绕过付费章节的限制尊重版权不要将爬取内容用于商业用途我曾经因为请求太频繁被暂时封禁IP后来通过降低频率和使用随机User-Agent解决了这个问题。建议在代码中加入这样的设置headers_list [ {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...}, {User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...}, # 更多浏览器User-Agent ] headers random.choice(headers_list)7. 正则表达式进阶技巧在提取网页内容时正则表达式的编写很关键。经过多次实践我总结出几个提高匹配准确率的方法尽量使用非贪婪匹配.*?避免匹配过多内容对于可能包含换行的内容记得加上re.S标志先匹配大块内容再从中提取细节使用分组()来精确提取目标内容比如提取正文时可以这样优化正则表达式content_pattern re.compile( rdiv classread-content j_readContent\s*(.*?)\s*/div, re.S )如果遇到复杂的HTML结构可以考虑先用BeautifulSoup解析再用正则表达式提取特定内容。虽然这会增加依赖但处理复杂页面时会更可靠from bs4 import BeautifulSoup soup BeautifulSoup(html, html.parser) content_div soup.find(div, class_read-content) if content_div: content str(content_div)8. 项目扩展与进阶方向这个基础爬虫还可以进一步扩展。比如添加进度显示让用户知道爬取进度total len(chapter_urls) for i, url in enumerate(chapter_urls, 1): print(f\r进度{i}/{total} {i/total:.1%}, end) # 爬取代码...如果想爬取多部小说可以抽象出一个小说类class NovelSpider: def __init__(self, book_id): self.base_url fhttps://book.qidian.com/info/{book_id} self.headers {...} def get_chapters(self): # 获取章节列表 def download_chapter(self, url): # 下载单个章节 def run(self): # 执行完整爬取流程对于需要登录才能查看的内容可以添加cookie处理cookies {key: value} # 从浏览器复制 response requests.get(url, headersheaders, cookiescookies)最后提醒一点网页结构可能会变所以正则表达式也需要定期检查更新。我建议把正则表达式模式放在代码开头作为常量方便修改CHAPTER_PATTERN re.compile(rli>

更多文章