这篇笔记📒记录我的第一个Python爬虫程序,我们要抓取豆瓣读书 Top 250这个页面,想要的结果就是豆瓣图书top250 表格文件,包含书名、作者、出版社、评分等信息
要抓取的页面截图
脚本执行后的结果,生成一个CSV文件
我们要写一个网页抓取脚本,使用Python的
selenium
和BeautifulSoup
来从豆瓣读书Top 250页面提取书籍信息(如书名、出版信息、评分和简介),然后将这些数据保存到一个CSV文件中。分为以下几个主要部分:
- 导入库:加载必要的工具
- 设置浏览器:配置一个自动化浏览器来访问网页
- 定义函数:
- get_books(): 从网页提取书籍数据
- save_to_csv(): 将数据保存到CSV文件
- 主函数
main()
: 控制整个流程,循环抓取所有页面。
1. 导入库
python
import csv
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
import csv
: 导入Python内置的csv
模块,用于读写CSV文件import time
: 导入time
模块,提供时间相关的功能,比如让程序暂停几秒钟from selenium import webdriver
: 导入selenium
库的webdriver
模块,用于控制浏览器(比如Chrome).selenium
是一个自动化测试工具,这里用来模拟浏览器访问网页。from selenium.webdriver.chrome.service import Service
: 导入Service
类,用于管理Chrome浏览器的驱动程序。from selenium.webdriver.chrome.options import Options
: 导入Options
类,用于设置浏览器的选项(比如无界面模式)from selenium.webdriver.common.by import By
: 导入By
类,用于制定如何查找网页元素(比如通过类名、ID等)。这里虽然没有直接使用,但它是selenium
的常用工具。from webdriver_manager.chrome import ChromeDriverManager
: 导入ChromeDriverManager
,它会自动下载并管理Chrome浏览器的驱动程序(chromedriver
),无需手动安装。from bs4 import BeautifulSoup
: 导入BeautifulSoup
, 一个强大的HTML杰西库,用来从网页源码中提取数据
2.设置 Chrome WebDriver
python
options = Options()
options.add_argument('--headless') // 无UI模式
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options)
options = Options()
: 创建一个Options
对象,用于设置浏览器行为。options.add_argument("--headless")
: 添加一个参数,让浏览器以"无UI模式模式"运行。也就是说,浏览器会在后台运行,因为我们抓取数据,不需要界面。driver = webdriver.Chrome(...)
: 创建一个 Chrome 浏览器的实例。service=Service(ChromeDriverManager().install())
: 使用ChromeDriverManager
自动下载并设置Chrome驱动程序。驱动程序是Python和浏览器之间的桥梁options=options
: 将我们设置的无UI模式应用到浏览器
3. 定义get_books
函数
python
def get_books(url):
driver.get(url)
time.sleep(3) # 等待JavaScript加载
soup = BeautifulSoup(driver.page_source, 'html.parser')
books = []
items = soup.find_all('tr', class_='item')
print(f"Found {len(items)} items") # 调试
for item in items:
title = item.find('div', class_="pl2').find('a').text.strip()
info = item.find('p', class_='pl').text.strip()
rating_tag = item.find('span', class_='rating_nums')
rating = rating_tag.text if rating_tag else '无评分'
quote_tag = item.find('span', class_='inq')
quote = quote.tag.text if quote_tag else '暂无简介'
books.append({'title': title, 'info': info, 'rating': rating, 'quote': quote})
return books
这个函数的作用是给制定的URL (豆瓣读书页面)提取书籍信息
def get_books(url)
: 定义一个函数,参数是url
(网页地址)driver.get(url)
: 让浏览器访问指定的URL。time.sleep(3)
: 暂停3秒钟。因为豆瓣的页面使用JavaScript动态加载内容,暂停一下确保页面完全加载soup = BeautifulSoup(driver.page_source, 'html.parse')
:driver.page-source
: 获取浏览器加载后的网页源码(HTML)BeautifulSoup(..., 'html.parser')
: 用BeautifulSoup
解析HTML, 方便提取数据
books = []
: 创建一个空列表, 用来存储书籍信息
items = soup.find_all('tr', class_='item')
find_all('tr', class_='item')
: 查找所有HTML中的<tr>
标签,且带有class="item"
的属性。在豆瓣页面,每本书的信息都包裹在一个这样的<tr>
标签里
print(f"Found {len(items)} items")
: 打印找到的书籍数量,用于调试。如果数量不对,可能说明网页结构变了for item in items:
: 循环遍历每本书的<tr>
标签,提取信息:title = item.find('div', class_='pl2').find('a').text.strip()
:find('div', class_='pl2')
: 找到<div class="pl2">
, 这是书名所在的区域find('a'): 找到里面的<a>
标签(超链接),书名在其中.text
: 提取文本内容.strip()
: 去除多余的空格或换行符
info = item.find('p', class_='pl').text.strip()
: 找到<p class="pl">
, 这是出版信息(如作者、出版社、价格)rating_tag = item.find('span', class_='rating_nums)
: 找到评分所在的<span class="rating_nums">
rating = rating_tag.text if rating_tag else "无评分"
:- 如果找到了评分标签,就取其文本;如果没找到(rating_tag 是 None),返回"无评分"。
quote_tag = item.find('span', class_='inq')
:找到简介所在的 。- quote = quote_tag.text if quote_tag else "暂无简介":同上,取简介或返回默认值。
- books.append({...}) :将每本书的信息(字典形式)添加到 books 列表中。
return books
: 返回包含所有书籍信息的列表
这个函数就像一个"图书管理员",它打开网页,找到每本书的"卡片"(<tr>
),然后把卡片上的信息(书名、评分等)抄下来,整理成清单。
4. 定义save_to_csv
函数
python
def save_to_csv(books, filename="douban_books_top_250.csv"):
with open(filename, mode="w", encoding="utf-8-sig", newline="") as file:
writer = csv.DictWriter(file, filenames=["序号", "书名", "出版信息", "评分", "简介"])
writer.writeheader()
# 增加索引从1到250
for index, book in enumerate(books, start=1)
book['序号'] = index
writer.writerow(book)
print(f"Data saved to {filename}")
这个函数将书籍数据保存到CSV文件中
def save_to_csv(books, filename="douban_books_top_250.csv")
: 定义函数,参数是书籍列表books
和文件名(默认是douban_books_top_250
),如果调用时不提供文件名,就用这个默认值with open(filename, mode="w", encoding="utf-8-stig", newline="") as file:
:- open(): 打开一个文件
- mode="w": 以写入模式打开(会覆盖原有文件)
- encoding="utf-8-stig": 使用UTF-8编码(支持中文),
sig
确保Windows的Excel中打开时中文不会乱码 - newline="": 避免CSV文件有多余空行(这是Python处理CSV时的常见设置)
- with: 这是一个上下文管理器,自动在操作完成后关闭文件,防止资源浪费
as file
: 将打开的文件对象命名为file
,后续操作会用到它
- `writer = csv.DictWriter(file, fieldnames=["序号", "书名", "出版信息", "评分", "简介"])
- 创建一个CSV写入器,制定列名(表头),这些名字会出现在文件的第一行
- writer.writeheader(): 将
fieldnames
中指定的列名写入CSV文件的第一行
python
for index, book in enumerate(books, start=1):
book["序号"] = index
writer.writerow(book)
-
for index, book in enumerate(books, start=1): :
- 这是一个循环,遍历 books 列表中的每一本书。
- enumerate(books, start=1) :
- enumerate 是一个内置函数,它会给列表中的每个元素配一个编号。
- start=1:让编号从 1 开始(默认是 0)。
- 每次循环:
- index 是当前的编号(1, 2, 3, ..., 250)。
- book 是当前书的字典,比如 {'title': '活着', 'info': '余华 / 作家出版社', ...}。
-
book["序号"] = index:
- 为当前书的字典添加一个新键值对:"序号": index。
- 比如,第一本书的字典会变成 {'title': '活着', 'info': '...', '序号': 1}。
-
writer.writerow(book) :
- 将当前书的字典写入 CSV 文件的一行。
- DictWriter 会根据 fieldnames 的顺序,自动匹配字典的键,把值写入对应列。
- 比如:2,活着,余华 / 作家出版社,9.2,生的苦难与伟大。
5. 定义 main
函数
python
def main():
base_url = "https://book.douban.com/top250"
all_books = []
for start in range(0, 250, 25):
url = f"{base_url}?start={start}"
books = get_books(url)
all_books.extend(books)
save_to_csv(all_books)
driver.quit()
这个函数是程序的核心,控制抓取和保存的流程。
- base_url = "book.douban.com/top250" :豆瓣 Top 250 的基础 URL。
- all_books = [] :创建一个空列表,存储所有 250 本书的信息。
- for start in range(0, 250, 25): :
- range(0, 250, 25):生成一个序列:0, 25, 50, ..., 225。豆瓣每页显示 25 本书,总共 250 本,分 10 页。
- 每次循环抓取一页。
- url = f"{base_url}?start={start}" :构造每页的 URL,比如 book.douban.com/top250?star... 1 页)、?start=25(第 2 页)。
- books = get_books(url) :调用 get_books 函数,抓取当前页的书籍。
- all_books.extend(books) :将当前页的书籍添加到总列表中(extend 是列表的合并方法)。
- save_to_csv(all_books) :抓取完所有页面后,将数据保存到 CSV。
- driver.quit() :关闭浏览器,释放资源。
6. 执行代码
python
if __name__ == "__main__"
main()
- if name == "main": :这是 Python 的习惯写法,确保 main() 只在直接运行这个脚本时执行(而不是被其他文件导入时运行)。
- main() :调用主函数,启动程序。
代码的工作流程
- 配置并启动一个无界面的 Chrome 浏览器。
- 循环访问豆瓣 Top 250 的 10 个页面(每页 25 本书)。
- 对每个页面:
- 用 selenium 加载网页。 - 用 BeautifulSoup 解析网页,提取书籍信息。
- 将所有 250 本书的信息存入列表。
- 将列表保存到 douban_books.csv 文件。
- 关闭浏览器。 执行结果
完整代码
python
import csv
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from bs4 import BeautifulSoup
# Setup Chrome WebDriver
options = Options()
options.add_argument("--headless") # Run in headless mode (no UI)
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
def get_books(url):
driver.get(url)
time.sleep(3) # Wait for JavaScript to load (adjust as needed)
soup = BeautifulSoup(driver.page_source, 'html.parser')
books = []
items = soup.find_all('tr', class_='item') # Douban uses <tr> for book items
print(f"Found {len(items)} items") # Debugging output
for item in items:
title = item.find('div', class_='pl2').find('a').text.strip()
info = item.find('p', class_='pl').text.strip()
rating_tag = item.find('span', class_='rating_nums')
rating = rating_tag.text if rating_tag else "无评分"
quote_tag = item.find('span', class_='inq')
quote = quote_tag.text if quote_tag else "暂无简介"
books.append({'书名': title, '出版信息': info, '评分': rating, '简介': quote})
return books
# Save data to CSV
def save_to_csv(books, filename="douban_books_top_250.csv"):
with open(filename, mode="w", encoding="utf-8-sig", newline="") as file:
writer = csv.DictWriter(file, fieldnames=["序号", "书名", "出版信息", "评分", "简介"])
writer.writeheader()
# Add index number from 1 to 250
for index, book in enumerate(books, start=1):
book["序号"] = index
writer.writerow(book)
print(f"Data saved to {filename}")
# Main function
def main():
base_url = "https://book.douban.com/top250"
all_books = []
for start in range(0, 250, 25):
url = f"{base_url}?start={start}"
books = get_books(url)
all_books.extend(books)
# Save to CSV
save_to_csv(all_books)
driver.quit()
if __name__ == "__main__":
main()