1. 环境准备与Python安装
1.1 检查系统要求
Scrapy是一个基于Python的开源网络爬虫框架,需要在Windows系统上安装Python环境。首先确认你的Windows系统版本(Windows 7/8/10/11均可,但推荐Windows 10或11)。
1.2 安装Python
下载Python安装包:
- 访问Python官方网站:https://www.python.org/downloads/
- 下载最新的Python 3.x版本(推荐Python 3.9或更高版本)
- 注意:下载时选择”Windows installer (64-bit)“(如果你的系统是64位)
安装Python:
- 运行下载的安装程序
- 重要:在安装界面勾选”Add Python to PATH”选项
- 选择”Install Now”进行默认安装
- 安装完成后,按Win+R,输入
cmd打开命令提示符,输入以下命令验证安装:
应显示类似python --versionPython 3.10.6的版本信息
验证pip安装:
- 在命令提示符中输入:
应显示pip的版本信息,如pip --versionpip 23.2.1 from ...
- 在命令提示符中输入:
1.3 配置虚拟环境(推荐)
为避免项目依赖冲突,建议为每个项目创建独立的虚拟环境:
安装virtualenv(如果尚未安装):
pip install virtualenv创建项目目录:
mkdir C:\scrapy_projects cd C:\scrapy_projects创建虚拟环境:
virtualenv scrapy_env激活虚拟环境:
scrapy_env\Scripts\activate激活后命令提示符前会显示
(scrapy_env)前缀
2. 安装Scrapy框架
2.1 通过pip安装Scrapy
在激活的虚拟环境中执行:
pip install scrapy
2.2 验证Scrapy安装
安装完成后,输入以下命令验证:
scrapy --version
应显示Scrapy的版本信息,如Scrapy 2.11.0
2.3 解决常见安装问题
问题1:安装速度慢或超时
使用国内镜像源加速安装:
pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple
问题2:缺少依赖库
Scrapy依赖Twisted、lxml等库,如果安装失败,可以尝试:
pip install twisted lxml cryptography
pip install scrapy
问题3:Windows系统缺少C++编译工具
某些依赖需要编译,如果遇到错误:
- 安装Microsoft Visual C++ Build Tools:https://visualstudio.microsoft.com/visual-cpp-build-tools/
- 或者安装预编译的wheel包:
pip install scrapy --only-binary :all:
3. 创建Scrapy爬虫项目
3.1 创建项目结构
在项目目录下(如C:\scrapy_projects)执行:
scrapy startproject myspider
这将创建以下目录结构:
myspider/
├── scrapy.cfg # 项目配置文件
└── myspider/ # 项目Python包
├── __init__.py
├── items.py # 定义数据结构
├── middlewares.py # 中间件
├── pipelines.py # 数据处理管道
├── settings.py # 项目设置
└── spiders/ # 爬虫存放目录
└── __init__.py
3.2 创建第一个爬虫
进入项目目录:
cd myspider
创建爬虫文件(以爬取豆瓣电影Top250为例):
scrapy genspider douban_spider movie.douban.com
这将在spiders/目录下生成douban_spider.py文件,初始内容如下:
import scrapy
class DoubanSpiderSpider(scrapy.Spider):
name = "douban_spider"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com/top250"]
def parse(self, response):
pass
4. 编写爬虫代码
4.1 完整爬虫示例
修改douban_spider.py文件,实现爬取豆瓣电影Top250:
import scrapy
from scrapy.http import Request
import re
class DoubanSpiderSpider(scrapy.Spider):
name = "douban_spider"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com/top250"]
# 设置请求头,模拟浏览器访问
custom_settings = {
'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',
'ROBOTSTXT_OBEY': False, # 不遵守robots.txt(仅用于学习)
'DOWNLOAD_DELAY': 1, # 下载延迟,避免被封
}
def parse(self, response):
"""解析豆瓣电影Top250列表页"""
# 提取所有电影条目
movie_items = response.css('div.item')
for movie in movie_items:
# 提取电影信息
title = movie.css('span.title::text').get()
rating = movie.css('span.rating_num::text').get()
quote = movie.css('span.inq::text').get()
# 提取电影详情页链接
detail_url = movie.css('a::attr(href)').get()
# 构造详情页请求,传递电影标题作为参数
yield Request(
url=detail_url,
callback=self.parse_detail,
meta={'title': title, 'rating': rating, 'quote': quote}
)
# 处理分页
next_page = response.css('span.next a::attr(href)').get()
if next_page:
yield Request(
url=response.urljoin(next_page),
callback=self.parse
)
def parse_detail(self, response):
"""解析电影详情页"""
title = response.meta['title']
rating = response.meta['rating']
quote = response.meta['quote']
# 提取导演、演员等信息
director = response.css('a[href*="celebrity"]::text').getall()
director = [d.strip() for d in director if d.strip()]
# 提取电影类型
genres = response.css('span[property="v:genre"]::text').getall()
# 提取上映时间
release_date = response.css('span[property="v:initialReleaseDate"]::text').get()
# 提取电影时长
duration = response.css('span[property="v:runtime"]::text').get()
# 提取剧情简介
summary = response.css('span[property="v:summary"]::text').get()
if summary:
summary = summary.strip()
# 提取评分人数
votes = response.css('span[property="v:votes"]::text').get()
# 返回结构化数据
yield {
'title': title,
'rating': rating,
'quote': quote,
'director': director,
'genres': genres,
'release_date': release_date,
'duration': duration,
'summary': summary,
'votes': votes,
'url': response.url
}
4.2 定义数据结构(可选)
在items.py中定义数据结构,使数据更规范:
import scrapy
class MovieItem(scrapy.Item):
title = scrapy.Field()
rating = scrapy.Field()
quote = scrapy.Field()
director = scrapy.Field()
genres = scrapy.Field()
release_date = scrapy.Field()
duration = scrapy.Field()
summary = scrapy.Field()
votes = scrapy.Field()
url = scrapy.Field()
然后修改爬虫代码,使用Item对象:
from myspider.items import MovieItem
# 在parse_detail方法中:
item = MovieItem()
item['title'] = title
item['rating'] = rating
# ... 其他字段
yield item
5. 运行爬虫并保存数据
5.1 运行爬虫
在项目根目录(C:\scrapy_projects\myspider)下执行:
scrapy crawl douban_spider
5.2 保存数据到JSON文件
scrapy crawl douban_spider -o movies.json
5.3 保存数据到CSV文件
scrapy crawl douban_spider -o movies.csv
5.4 保存数据到JSON Lines格式(推荐)
scrapy crawl douban_spider -o movies.jsonl
5.5 运行爬虫并查看输出
scrapy crawl douban_spider -L INFO
6. 配置文件详解
6.1 修改settings.py配置
在myspider/settings.py中添加以下配置:
# 禁用robots.txt遵守(仅用于学习)
ROBOTSTXT_OBEY = False
# 设置请求头
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'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',
}
# 下载延迟(避免被封)
DOWNLOAD_DELAY = 1
# 并发请求数
CONCURRENT_REQUESTS = 16
# 启用管道(用于数据处理)
ITEM_PIPELINES = {
'myspider.pipelines.MyspiderPipeline': 300,
}
# 启用中间件
DOWNLOADER_MIDDLEWARES = {
'myspider.middlewares.MyspiderDownloaderMiddleware': 543,
}
# 启用扩展
EXTENSIONS = {
'scrapy.extensions.telnet.TelnetConsole': None,
}
# 日志级别
LOG_LEVEL = 'INFO'
# 数据导出编码
FEED_EXPORT_ENCODING = 'utf-8'
6.2 创建数据处理管道
在pipelines.py中创建数据清洗管道:
import json
import csv
class MyspiderPipeline:
def __init__(self):
self.file = open('movies_cleaned.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
# 数据清洗:去除空格
if item.get('title'):
item['title'] = item['title'].strip()
# 数据验证:确保评分是数字
if item.get('rating'):
try:
item['rating'] = float(item['rating'])
except:
item['rating'] = None
# 数据转换:将导演列表转为字符串
if item.get('director'):
item['director'] = ', '.join(item['director'])
# 写入JSON文件
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
def close_spider(self, spider):
self.file.close()
7. 调试与测试
7.1 使用Scrapy Shell调试
在项目目录下执行:
scrapy shell "https://movie.douban.com/top250"
在Shell中测试选择器:
# 查看页面标题
response.css('title::text').get()
# 提取所有电影标题
response.css('div.item span.title::text').getall()
# 提取第一个电影的评分
response.css('div.item span.rating_num::text').get()
# 查看页面结构
response.body
7.2 使用浏览器开发者工具
- 打开豆瓣电影Top250页面
- 按F12打开开发者工具
- 选择”Elements”标签,右键点击元素,选择”Copy” → “Copy selector”
- 在Scrapy中使用该选择器测试
7.3 常见问题排查
问题1:爬虫没有返回任何数据
- 检查选择器是否正确
- 使用Scrapy Shell测试选择器
- 检查网络连接和网站是否可访问
问题2:被网站封禁
- 增加下载延迟:
DOWNLOAD_DELAY = 2 - 使用代理IP:在settings.py中添加:
PROXY = 'http://your-proxy-ip:port' DOWNLOADER_MIDDLEWARES = { 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110, } - 使用随机User-Agent:
在settings.py中添加:pip install scrapy-user-agentsDOWNLOADER_MIDDLEWARES = { 'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400, }
问题3:数据格式错误
- 检查数据类型转换
- 在pipeline中添加异常处理
- 使用try-except捕获错误
8. 项目结构优化
8.1 创建多个爬虫
scrapy genspider baidu_spider www.baidu.com
scrapy genspider jd_spider www.jd.com
8.2 创建通用爬虫模板
在spiders/目录下创建base_spider.py:
import scrapy
from scrapy.http import Request
class BaseSpider(scrapy.Spider):
"""通用爬虫基类"""
def start_requests(self):
"""生成初始请求"""
for url in self.start_urls:
yield Request(
url=url,
callback=self.parse,
headers={'User-Agent': self.get_random_user_agent()}
)
def get_random_user_agent(self):
"""获取随机User-Agent"""
import random
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
]
return random.choice(user_agents)
8.3 创建数据存储模块
在项目根目录下创建data_storage.py:
import json
import csv
import sqlite3
from datetime import datetime
class DataStorage:
"""数据存储管理器"""
def __init__(self, storage_type='json'):
self.storage_type = storage_type
self.file = None
self.conn = None
def open(self, filename):
"""打开存储文件"""
if self.storage_type == 'json':
self.file = open(filename, 'w', encoding='utf-8')
elif self.storage_type == 'csv':
self.file = open(filename, 'w', newline='', encoding='utf-8')
self.writer = csv.writer(self.file)
# 写入表头
self.writer.writerow(['title', 'rating', 'quote', 'director', 'genres',
'release_date', 'duration', 'summary', 'votes', 'url'])
elif self.storage_type == 'sqlite':
self.conn = sqlite3.connect(filename)
self.create_table()
def create_table(self):
"""创建数据库表"""
cursor = self.conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS movies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
rating REAL,
quote TEXT,
director TEXT,
genres TEXT,
release_date TEXT,
duration TEXT,
summary TEXT,
votes TEXT,
url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
self.conn.commit()
def save(self, data):
"""保存数据"""
if self.storage_type == 'json':
line = json.dumps(data, ensure_ascii=False) + "\n"
self.file.write(line)
elif self.storage_type == 'csv':
row = [
data.get('title', ''),
data.get('rating', ''),
data.get('quote', ''),
', '.join(data.get('director', [])),
', '.join(data.get('genres', [])),
data.get('release_date', ''),
data.get('duration', ''),
data.get('summary', ''),
data.get('votes', ''),
data.get('url', '')
]
self.writer.writerow(row)
elif self.storage_type == 'sqlite':
cursor = self.conn.cursor()
cursor.execute('''
INSERT INTO movies (title, rating, quote, director, genres,
release_date, duration, summary, votes, url)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
data.get('title'),
data.get('rating'),
data.get('quote'),
', '.join(data.get('director', [])),
', '.join(data.get('genres', [])),
data.get('release_date'),
data.get('duration'),
data.get('summary'),
data.get('votes'),
data.get('url')
))
self.conn.commit()
def close(self):
"""关闭存储"""
if self.file:
self.file.close()
if self.conn:
self.conn.close()
9. 高级功能
9.1 使用代理IP
# 在settings.py中添加
PROXY_LIST = [
'http://proxy1.example.com:8080',
'http://proxy2.example.com:8080',
]
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
}
9.2 使用分布式爬虫
安装Scrapy-Redis:
pip install scrapy-redis
修改settings.py:
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_URL = 'redis://localhost:6379/0'
9.3 使用Scrapy-Cloud
pip install scrapy-cloud
10. 部署与自动化
10.1 创建批处理脚本
在项目根目录创建run_spider.bat:
@echo off
cd /d C:\scrapy_projects\myspider
scrapy crawl douban_spider -o movies_%date:~0,4%%date:~5,2%%date:~8,2%.jsonl
echo 爬虫执行完成
pause
10.2 使用Windows任务计划程序
- 打开”任务计划程序”
- 创建基本任务
- 设置触发器(每天/每周)
- 操作:启动程序
- 程序:
C:\scrapy_projects\myspider\run_spider.bat
10.3 使用Docker容器化(可选)
创建Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["scrapy", "crawl", "douban_spider"]
11. 最佳实践与注意事项
11.1 遵守法律法规
- 仅用于学习和研究目的
- 遵守目标网站的robots.txt协议
- 不要对网站造成过大访问压力
- 尊重网站的版权和数据使用条款
11.2 性能优化
- 合理设置并发数和下载延迟
- 使用缓存机制避免重复请求
- 优化选择器性能
- 使用异步IO处理大量数据
11.3 错误处理
- 添加重试机制
- 处理网络超时
- 处理页面解析错误
- 记录日志便于调试
11.4 数据安全
- 不要存储敏感信息
- 加密存储重要数据
- 定期清理临时文件
12. 总结
通过以上步骤,你已经成功在Windows系统上安装了Scrapy并创建了一个完整的爬虫项目。从环境准备、框架安装、项目创建、代码编写到运行调试,每个环节都有详细的说明和示例代码。
记住,爬虫技术是一把双刃剑,务必合法合规使用。在实际应用中,你可能需要根据具体网站的结构和反爬机制调整代码。建议先从简单的静态网站开始练习,逐步掌握更复杂的爬虫技术。
如果你在实践过程中遇到任何问题,可以:
- 查看Scrapy官方文档:https://docs.scrapy.org/
- 使用Scrapy Shell调试选择器
- 在Stack Overflow搜索相关问题
- 加入Scrapy社区获取帮助
祝你爬虫学习顺利!
