下面我将为你提供一个详细的、分步的指南,从基础到进阶,教你如何用 Python 爬取一本 GitBook。

GitBook 内容特点分析
在开始之前,我们先要了解 GitBook 的内容是如何组织的:
- 静态 HTML:GitBook 的章节列表(目录)和大部分章节的正文内容是静态 HTML,这意味着我们可以直接发送 HTTP 请求来获取这些页面。
- 动态加载(部分内容):有些 GitBook 的内容(如代码块的行号、部分交互元素)可能是通过 JavaScript 动态加载的,但对于纯文本内容,我们通常不需要渲染整个页面。
- 反爬机制:
- User-Agent 检测:服务器会检查请求的
User-Agent(浏览器标识),如果是一个简单的 Python 脚本,很容易被识别为爬虫。 - 请求频率限制:如果你在短时间内发送大量请求,GitBook 服务器可能会暂时封禁你的 IP 地址。
- User-Agent 检测:服务器会检查请求的
- 内容存储:GitBook 的内容通常存储在
https://<book-name>.gitbook.io/<book-path>/这样的路径下,每个章节的 URL 都有其特定的规律。
第一步:准备工作
安装必要的库
我们将使用 requests 库来发送 HTTP 请求,BeautifulSoup 库来解析 HTML,以及 lxml 作为解析器(速度更快)。
pip install requests beautifulsoup4 lxml
选择目标 GitBook
我们以一本公开的 GitBook 为例,gitbook tutorial 官方文档:https://docs.gitbook.com/
我们的目标是爬取它的所有章节内容,并保存到本地文件中。

第二步:基础爬虫(爬取单章内容)
我们先从一个简单的目标开始:爬取一个指定章节的纯文本内容。
import requests
from bs4 import BeautifulSoup
import re
def get_chapter_content(url):
"""
获取 GitBook 单个章节的纯文本内容
"""
# 1. 设置请求头,模拟浏览器
headers = {
'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'
}
try:
# 2. 发送 HTTP 请求
response = requests.get(url, headers=headers, timeout=10)
# 3. 检查请求是否成功
response.raise_for_status() # 如果状态码不是 200,则抛出异常
# 4. 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(response.text, 'lxml')
# 5. 定位到内容区域
# 在 GitBook 中,正文内容通常在 class 为 "markdown-section" 的 div 中
content_div = soup.find('div', class_='markdown-section')
if content_div:
# 6. 提取文本并清理
# .get_text() 可以获取一个标签下的所有文本,包括子标签中的
# strip() 用于去除首尾多余的空白字符
text = content_div.get_text(separator='\n', strip=True)
# 可选:使用正则表达式清理多余的空白行
cleaned_text = re.sub(r'\n\s*\n', '\n\n', text)
return cleaned_text
else:
print(f"在 {url} 中未找到 'markdown-section' 容器。")
return None
except requests.exceptions.RequestException as e:
print(f"请求 {url} 时发生错误: {e}")
return None
# --- 测试 ---
if __name__ == '__main__':
# 替换成你想要爬取的章节 URL
test_url = "https://docs.gitbook.com/getting-started"
content = get_chapter_content(test_url)
if content:
print(f"成功获取内容,长度: {len(content)} 字符")
# 打印前 500 个字符预览
print("\n--- 内容预览 ---\n")
print(content[:500])
代码解释:
headers:我们设置了一个User-Agent,让服务器以为我们的请求来自一个真实的浏览器,这是绕过最基础反爬的关键。requests.get():发送 GET 请求获取网页源代码。response.raise_for_status():这是一种良好的实践,如果请求失败(如 404, 500),它会抛出异常。BeautifulSoup(response.text, 'lxml'):将 HTML 内容解析成一个可操作的对象。soup.find('div', class_='markdown-section'):这是最关键的一步,我们通过 CSS 选择器定位到包含正文内容的<div>标签,你可以通过浏览器“开发者工具”(F12)来验证这个选择器是否正确。.get_text():提取解析后的对象中的所有文本。
第三步:进阶爬虫(爬取整本书)
现在我们知道如何爬取单章内容,接下来要爬取整本书,我们需要:
- 获取书籍的目录页。
- 从目录页中解析出所有章节的链接。
- 遍历这些链接,调用我们上面写的函数获取每一章的内容,保存到本地文件。
import requests
from bs4 import BeautifulSoup
import os
import re
import time
# --- 复用上面的 get_chapter_content 函数 ---
def get_chapter_content(url):
headers = {
'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'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml')
content_div = soup.find('div', class_='markdown-section')
if content_div:
text = content_div.get_text(separator='\n', strip=True)
cleaned_text = re.sub(r'\n\s*\n', '\n\n', text)
return cleaned_text
return None
except requests.exceptions.RequestException as e:
print(f"请求 {url} 时发生错误: {e}")
return None
def scrape_gitbook(book_url, output_dir='gitbook_content'):
"""
爬取整本 GitBook 并保存到本地文件夹
"""
# 1. 创建输出目录
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"已创建目录: {output_dir}")
# 2. 获取目录页
headers = {
'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'
}
try:
response = requests.get(book_url, headers=headers, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"无法获取书籍目录页 {book_url}: {e}")
return
# 3. 解析章节链接
soup = BeautifulSoup(response.text, 'lxml')
# 在 GitBook 中,章节链接通常在 class 为 "anchor" 的 <a> 标签里
# 我们可以通过父级元素来更精确地定位,'li' 或 'div.chapter-item'
chapter_links = soup.select('div[class*="chapter-item"] a.anchor, li a.anchor')
if not chapter_links:
print("在目录页中未找到任何章节链接,请检查选择器是否正确。")
return
print(f"找到 {len(chapter_links)} 个章节,开始爬取...")
# 4. 遍历并爬取每个章节
for i, link in enumerate(chapter_links):
chapter_title = link.get_text(strip=True)
chapter_url = link.get('href')
# 处理相对 URL
if chapter_url.startswith('/'):
chapter_url = f"https://docs.gitbook.com{chapter_url}"
print(f"正在爬取 [{i+1}/{len(chapter_links)}]: {chapter_title} ({chapter_url})")
# 获取内容
content = get_chapter_content(chapter_url)
if content:
# 清理文件名,移除非法字符
safe_title = re.sub(r'[\\/*?:"<>|]', "", chapter_title)
file_path = os.path.join(output_dir, f"{safe_title}.txt")
# 保存到文件
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(f"# {chapter_title}\n\n")
f.write(content)
print(f" -> 已保存到: {file_path}")
except IOError as e:
print(f" -> 写入文件失败: {e}")
else:
print(f" -> 跳过: 未能获取内容。")
# 5. 礼貌性等待,避免请求过快
# time.sleep(1) # 可以根据需要调整等待时间
print("\n爬取任务完成!")
# --- 运行 ---
if __name__ == '__main__':
# 替换成你想要爬取的 GitBook 根目录 URL
target_book_url = "https://docs.gitbook.com/"
scrape_gitbook(target_book_url)
进阶代码解释:

scrape_gitbook():这是主函数,协调整个爬取过程。os.makedirs():用于创建保存内容的文件夹。soup.select(...):这是BeautifulSoup的 CSS 选择器功能,非常强大,我们用它来找到所有章节的链接,选择器'div[class*="chapter-item"] a.anchor'会查找所有class属性中包含 "chapter-item" 的div下的a标签,并且这个a标签的class包含 "anchor",这比find更灵活,能匹配多个元素。- 处理相对 URL:目录页中的链接可能是
/getting-started这样的相对路径,我们需要将其拼接成完整的https://...URL。 enumerate():获取索引和元素,方便我们显示进度。- 文件名安全处理:
re.sub(r'[\\/*?:"<>|]', "", chapter_title)移除了 Windows 文件名中不允许的字符。 time.sleep():在每次请求之间加入短暂等待,这是对服务器的基本礼貌,可以有效避免被封禁。
第四步:处理更复杂的情况(动态加载)
有些 GitBook 的内容(如代码高亮、图表)可能依赖于 JavaScript,对于这类情况,requests 和 BeautifulSoup 可能无法获取到最终渲染的 HTML。
这时,我们需要使用 Selenium 或 Playwright 这样的浏览器自动化工具,它们可以像真人一样打开一个浏览器,加载页面,等待 JavaScript 执行完毕后再获取源代码。
使用 Selenium 的示例
-
安装 Selenium 和浏览器驱动
pip install selenium
你还需要下载对应浏览器的驱动程序,ChromeDriver,并确保它在你的系统路径中,或者通过代码指定其路径。
-
修改爬虫代码
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time
# ... (其他函数保持不变) ...
def get_chapter_content_selenium(url):
"""
使用 Selenium 获取可能包含动态内容的章节
"""
# 1. 配置 Selenium
# 你需要下载好 chromedriver 并指定其路径
# service = Service(executable_path='path/to/your/chromedriver')
# driver = webdriver.Chrome(service=service)
# 或者如果你的 chromedriver 在 PATH 中
options = webdriver.ChromeOptions()
# options.add_argument('--headless') # 无头模式,不显示浏览器窗口
driver = webdriver.Chrome(options=options)
try:
# 2. 打开页面
driver.get(url)
# 3. 等待 JavaScript 渲染
# 可以等待一个特定的元素加载完成,或者简单等待几秒
# 这里我们简单等待 3 秒
time.sleep(3)
# 4. 获取渲染后的页面源代码
page_source = driver.page_source
# 5. 使用 BeautifulSoup 解析
soup = BeautifulSoup(page_source, 'lxml')
content_div = soup.find('div', class_='markdown-section')
if content_div:
text = content_div.get_text(separator='\n', strip=True)
return text
return None
finally:
# 6. 关闭浏览器
driver.quit()
# 在你的主爬虫函数中,将 get_chapter_content 替换为 get_chapter_content_selenium
# scrape_gitbook(book_url, output_dir='gitbook_content_selenium')
Selenium 的优缺点:
- 优点:可以处理任何动态加载的内容,爬取效果最接近真实用户。
- 缺点:
- 速度慢,因为要启动一个完整的浏览器。
- 资源消耗大。
- 配置相对复杂(需要管理浏览器驱动)。
- 容易被更高级的反爬策略检测。
总结与最佳实践
- 首选
requests+BeautifulSoup:对于大多数 GitBook,静态解析已经足够,这是最快、最轻量的方案。 - 设置
User-Agent:这是爬虫的“身份证”,必须设置。 - 控制请求频率:使用
time.sleep()给服务器喘息的时间,避免被封 IP。 - 错误处理:网络请求随时可能失败,使用
try...except来捕获异常,使你的脚本更健壮。 - 分析目标:在写爬虫前,先用浏览器开发者工具(F12)仔细分析目标网页的结构,找到最稳定、最精确的 CSS 选择器。
- 尊重
robots.txt:虽然 GitBook 的robots.txt通常允许爬取,但这是一个好习惯,你可以访问https://docs.gitbook.com/robots.txt来查看规则。 - 合法合规:爬取数据前,请确保目标网站允许你这么做,并且不要用于商业用途或对服务器造成过大压力。
希望这个详细的指南能帮助你成功爬取你想要的 GitBook 内容!
