<think>我们计划分步骤实现:1.单页爬取:使用requests发送请求,配合XPath解析,但需要先解决登录问题(因为豆瓣评论可能需要登录)。2.考虑到豆瓣可能有反爬,我们先用Selenium模拟登录获取cookies,然后给requests使用。3.多页爬取:通过分析翻页链接,循环请求每一页。4.保存为CSV文件。然而,根据引用[1]和引用[2]的思路,我们可以结合使用两种方法:-使用Selenium模拟登录获取cookies,然后使用requests进行高效的请求(避免Selenium的慢速)。-使用Selenium进行翻页操作,但这样效率较低,所以优先考虑构造翻页URL。但是,豆瓣电影的评论翻页URL有规律:-第一页:https://movie.douban.com/subject/35927898/comments?start=0&limit=20&status=P&sort=new_score-第二页:https://movie.douban.com/subject/35927898/comments?start=20&limit=20&status=P&sort=new_score-所以,翻页只需改变start参数,每次增加20。因此,我们可以:1.用Selenium登录获取cookies(避免验证码问题,如果遇到验证码则需要手动处理)。2.用requests携带cookies请求单页,解析数据。3.循环构造翻页URL,实现多页爬取。步骤:a.定义电影ID和初始URL。b.使用Selenium登录豆瓣,获取cookies。c.使用requests发送请求,并用lxml的etree解析HTML。d.编写XPath提取评论数据。e.循环翻页,直到达到指定页数或没有下一页。f.将数据保存为CSV。注意:豆瓣有反爬,需要设置请求头(User-Agent等)和适当的延时。根据引用[2]中的示例,我们将构建一个完整的爬虫程序,包含以下功能:-初始化浏览器(Selenium)-登录豆瓣(如果遇到验证码,可能需要人工干预)-获取cookies-关闭浏览器(因为后续使用requests)-用requests请求多页-解析并保存数据代码结构:1.配置参数(电影ID,页数,保存文件名等)2.使用Selenium登录并获取cookies3.定义请求头4.定义解析页面的函数5.循环请求每一页6.将数据写入CSV由于豆瓣评论需要登录才能查看更多,所以必须登录。但是,我们也可以尝试不登录查看,但可能只能看到部分评论。所以这里采用登录方式。具体实现:</think># 豆瓣电影《人生路不熟》评论爬取方案
我将结合requests库和selenium库实现单页和多页爬取功能,并将结果保存为CSV文件。以下是完整实现代码:
```python
import csv
import time
import requests
from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from lxml import etree
# 电影ID和基础URL
MOVIE_ID = "35927898"
BASE_URL = f"https://movie.douban.com/subject/{MOVIE_ID}/comments"
CSV_FILE = "douban_comments.csv"
def login_douban(username, password):
"""使用Selenium登录豆瓣并返回cookies"""
edge_options = Options()
edge_options.add_argument("--headless") # 无头模式
driver = webdriver.Edge(options=edge_options)
try:
driver.get("https://accounts.douban.com/passport/login")
time.sleep(2)
# 切换到密码登录
driver.find_element(By.CLASS_NAME, 'account-tab-account').click()
time.sleep(1)
# 输入账号密码
driver.find_element(By.ID, 'username').send_keys(username)
driver.find_element(By.ID, 'password').send_keys(password)
driver.find_element(By.XPATH, '//a[contains(@class,"btn-account")]').click()
# 等待登录完成
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CLASS_NAME, 'nav-user-avatar'))
)
return {c['name']: c['value'] for c in driver.get_cookies()}
finally:
driver.quit()
def get_headers():
"""返回请求头信息"""
return {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.43',
'Referer': BASE_URL,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
def parse_comments(html):
"""解析评论内容"""
tree = etree.HTML(html)
comments = []
for comment in tree.xpath('//div[@class="comment-item"]'):
try:
comment_data = {
'user_name': comment.xpath('.//div[@class="avatar"]/a/@title')[0],
'user_url': comment.xpath('.//div[@class="avatar"]/a/@href')[0],
'content': ''.join(comment.xpath('.//span[@class="short"]/text()')).strip(),
'time': comment.xpath('.//span[@class="comment-time"]/@title')[0],
'votes': int(comment.xpath('.//span[@class="votes"]/text()')[0])
}
comments.append(comment_data)
except IndexError:
continue # 跳过解析失败的评论
return comments
def scrape_single_page(cookies, headers):
"""爬取单页评论"""
response = requests.get(BASE_URL, headers=headers, cookies=cookies)
if response.status_code == 200:
return parse_comments(response.text)
else:
print(f"请求失败,状态码: {response.status_code}")
return []
def scrape_multiple_pages(cookies, headers, pages=5):
"""爬取多页评论"""
all_comments = []
for page in range(pages):
start = page * 20
url = f"{BASE_URL}?start={start}&limit=20&status=P&sort=new_score"
print(f"正在爬取第 {page+1} 页: {url}")
response = requests.get(url, headers=headers, cookies=cookies)
if response.status_code == 200:
page_comments = parse_comments(response.text)
all_comments.extend(page_comments)
print(f"获取到 {len(page_comments)} 条评论")
time.sleep(2) # 添加延时防止被封
else:
print(f"第 {page+1} 页请求失败,状态码: {response.status_code}")
return all_comments
def save_to_csv(comments, filename=CSV_FILE):
"""保存评论到CSV文件"""
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['user_name', 'user_url', 'content', 'time', 'votes'])
writer.writeheader()
writer.writerows(comments)
print(f"已保存 {len(comments)} 条评论到 {filename}")
def main():
# 替换为你的豆瓣账号
USERNAME = "your_username@example.com"
PASSWORD = "your_password"
# 获取登录cookies
print("正在登录豆瓣...")
cookies = login_douban(USERNAME, PASSWORD)
headers = get_headers()
# 单页爬取
print("\n开始单页爬取...")
single_page_comments = scrape_single_page(cookies, headers)
print(f"获取到 {len(single_page_comments)} 条评论")
# 多页爬取
print("\n开始多页爬取(5页)...")
all_comments = scrape_multiple_pages(cookies, headers, pages=5)
print(f"总共获取到 {len(all_comments)} 条评论")
# 保存结果
save_to_csv(all_comments)
if __name__ == "__main__":
main()
```
## 实现说明
### 1. 技术组合使用
- **Selenium**:用于模拟登录获取cookies
- **Requests**:用于高效发送HTTP请求
- **XPath**:用于解析HTML提取数据
- **CSV模块**:用于数据存储
### 2. 关键功能实现
1. **登录获取cookies**:
```python
def login_douban(username, password):
# 使用无头模式Edge浏览器登录
# 返回cookies字典
```
2. **单页爬取**:
```python
def scrape_single_page(cookies, headers):
# 请求基础URL
# 解析返回的HTML
```
3. **多页爬取**:
```python
def scrape_multiple_pages(cookies, headers, pages=5):
# 通过修改start参数实现翻页
# start = 0, 20, 40, ... 对应第1,2,3...页
```
4. **XPath解析规则**:
```xpath
//div[@class="comment-item"] # 评论容器
.//div[@class="avatar"]/a/@title # 用户名
.//div[@class="avatar"]/a/@href # 用户主页
.//span[@class="short"]/text() # 评论内容
.//span[@class="comment-time"]/@title # 评论时间
.//span[@class="votes"]/text() # 有用数
```
5. **CSV存储**:
```python
def save_to_csv(comments, filename):
# 使用csv.DictWriter写入数据
# 包含字段: user_name, user_url, content, time, votes
```
### 3. 使用说明
1. 安装依赖:
```bash
pip install selenium requests lxml
```
2. 下载匹配的[Edge WebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)
3. 替换代码中的账号信息:
```python
USERNAME = "your_username@example.com"
PASSWORD = "your_password"
```
4. 运行脚本:
```bash
python douban_crawler.py
```
### 4. 反爬应对措施
1. 使用真实User-Agent和Referer
2. 添加请求延时(2秒/页)
3. 使用登录后的cookies
4. 无头模式降低被识别概率
5. 异常处理跳过解析失败的评论
> 本代码仅用于教育目的,实际使用请遵守豆瓣的robots.txt规定和服务条款[^1]。
## 示例输出(CSV)
user_name | user_url | content | time | votes
----------|----------|---------|------|-------
影迷小王 | https://www.douban.com/people/user123/ | 笑点密集,全程无尿点! | 2023-05-01 12:30:45 | 256
电影达人 | https://www.douban.com/people/user456/ | 范丞丞表现超出预期 | 2023-05-01 10:15:22 | 189