Scrapy是一个专业的Python爬虫框架,专门用于高效抓取网页数据。相比简单的requests库,Scrapy提供了完整的爬虫解决方案,适合处理大规模数据采集任务。
Scrapy 官网:https://scrapy.org/
Scrapy具有很多强大功能:
自动处理请求和响应
支持数据清洗和存储
可以控制爬取速度
支持分布式爬取
有完善的错误处理机制
使用pip安装Scrapy:
pip install scrapy安装完成后,可以检查版本:
scrapy version使用命令行创建Scrapy项目:
scrapy startproject my_spider_project这个命令会创建一个项目文件夹,包含以下结构:
my_spider_project/
scrapy.cfg
my_spider_project/
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
spiders/
__init__.py进入项目目录,创建第一个爬虫:
cd my_spider_project
scrapy genspider example_spider example.com这会生成一个爬虫文件 spiders/example_spider.py。
打开生成的爬虫文件,修改内容:
import scrapy
class ExampleSpiderSpider(scrapy.Spider):
name = "example_spider"
allowed_domains = ["example.com"]
start_urls = ["https://example.com"]
def parse(self, response):
# 提取页面标题
title = response.css('title::text').get()
# 返回提取的数据
yield {
'title': title,
'url': response.url
}代码说明:
name:爬虫的唯一名称
allowed_domains:允许爬取的域名
start_urls:起始网址列表
parse:处理响应和提取数据的方法
在项目目录下运行:
scrapy crawl example_spider创建一个爬取图书信息的爬虫:
import scrapy
class BookSpider(scrapy.Spider):
name = "book_spider"
start_urls = [
'https://books.example.com/catalogue/page-1.html',
]
def parse(self, response):
# 提取每本图书的信息
books = response.css('article.product_pod')
for book in books:
yield {
'title': book.css('h3 a::attr(title)').get(),
'price': book.css('p.price_color::text').get(),
'rating': book.css('p.star-rating::attr(class)').get().split()[-1],
'link': response.urljoin(book.css('h3 a::attr(href)').get())
}
# 处理分页
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)修改 settings.py 文件来配置爬虫:
# 设置用户代理,模拟浏览器
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'
# 遵守robots.txt规则
ROBOTSTXT_OBEY = True
# 下载延迟,避免请求过快
DOWNLOAD_DELAY = 2
# 并发请求数
CONCURRENT_REQUESTS = 16
# 启用自动限速
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2
AUTOTHROTTLE_MAX_DELAY = 5在 items.py 中定义数据结构:
import scrapy
class BookItem(scrapy.Item):
# 定义字段
title = scrapy.Field()
price = scrapy.Field()
rating = scrapy.Field()
description = scrapy.Field()
link = scrapy.Field()
crawled_time = scrapy.Field()在爬虫中使用Item:
from my_spider_project.items import BookItem
class BookSpider(scrapy.Spider):
name = "book_spider"
def parse(self, response):
book = BookItem()
book['title'] = response.css('h1::text').get()
book['price'] = response.css('.price_color::text').get()
book['crawled_time'] = datetime.now().isoformat()
yield book在 pipelines.py 中处理爬取的数据:
import json
import csv
class JsonWriterPipeline:
def open_spider(self, spider):
self.file = open('books.json', 'w', encoding='utf-8')
self.file.write('[\n')
self.first_item = True
def close_spider(self, spider):
self.file.write('\n]')
self.file.close()
def process_item(self, item, spider):
# 转换数据为JSON格式
line = json.dumps(dict(item), ensure_ascii=False)
if not self.first_item:
self.file.write(',\n')
self.file.write(' ' + line)
self.first_item = False
return item
class PriceFilterPipeline:
def process_item(self, item, spider):
# 过滤价格过高的图书
price = item.get('price')
if price:
# 移除货币符号并转换为浮点数
price_value = float(price.replace('£', '').replace('$', ''))
if price_value > 50: # 如果价格超过50,丢弃该条目
raise DropItem(f"价格过高: {price}")
return item在 settings.py 中启用管道:
ITEM_PIPELINES = {
'my_spider_project.pipelines.PriceFilterPipeline': 100,
'my_spider_project.pipelines.JsonWriterPipeline': 200,
}数字表示优先级,越小越先执行。
在 middlewares.py 中添加自定义功能:
import random
from scrapy import signals
class RandomUserAgentMiddleware:
def __init__(self):
self.user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]
def process_request(self, request, spider):
# 随机选择用户代理
request.headers['User-Agent'] = random.choice(self.user_agents)
class ProxyMiddleware:
def process_request(self, request, spider):
# 设置代理
request.meta['proxy'] = 'http://your-proxy-server:port'启用中间件:
DOWNLOADER_MIDDLEWARES = {
'my_spider_project.middlewares.RandomUserAgentMiddleware': 400,
'my_spider_project.middlewares.ProxyMiddleware': 500,
}def parse(self, response):
# 提取文本
title = response.css('h1::text').get()
# 提取属性
image_url = response.css('img.product_image::attr(src)').get()
# 提取多个元素
categories = response.css('.breadcrumb li a::text').getall()
# 使用正则表达式过滤
price = response.css('.price_color::text').re_first(r'[\d.]+')
return {
'title': title,
'image': response.urljoin(image_url),
'categories': categories,
'price': price
}def parse(self, response):
# 使用XPath提取数据
title = response.xpath('//h1/text()').get()
# 提取包含特定文本的元素
in_stock = response.xpath('//p[contains(@class, "instock")]/text()').get()
# 复杂的XPath查询
rating = response.xpath('//p[contains(@class, "star-rating")]/@class').re_first(r'star-rating (\w+)')
return {
'title': title,
'in_stock': '有货' if '有货' in in_stock else '缺货',
'rating': rating
}class PaginatedSpider(scrapy.Spider):
name = "paginated_spider"
def start_requests(self):
# 自定义起始请求
urls = ['https://example.com/catalogue/page-1.html']
for url in urls:
yield scrapy.Request(url, callback=self.parse)
def parse(self, response):
# 提取当前页数据
for product in response.css('.product_pod'):
yield {
'name': product.css('h3 a::attr(title)').get(),
'price': product.css('.price_color::text').get()
}
# 自动跟踪下一页
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)class DeepCrawlSpider(scrapy.Spider):
name = "deep_crawl"
def parse(self, response):
# 提取详情页链接
detail_links = response.css('.product_pod h3 a::attr(href)').getall()
for link in detail_links:
yield response.follow(link, self.parse_detail)
# 继续分页
next_page = response.css('.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)
def parse_detail(self, response):
# 解析详情页
yield {
'title': response.css('h1::text').get(),
'description': response.css('#product_description + p::text').get(),
'price': response.css('.price_color::text').get(),
'url': response.url
}运行爬虫时可以直接导出数据:
# 导出为JSON
scrapy crawl book_spider -o books.json
# 导出为CSV
scrapy crawl book_spider -o books.csv
# 导出为XML
scrapy crawl book_spider -o books.xml
# 分块导出
scrapy crawl book_spider -o books.jl # JSON Lines格式class CustomExportPipeline:
def open_spider(self, spider):
self.csv_file = open('custom_export.csv', 'w', newline='', encoding='utf-8')
self.writer = csv.writer(self.csv_file)
self.writer.writerow(['标题', '价格', '评分', '爬取时间'])
def close_spider(self, spider):
self.csv_file.close()
def process_item(self, item, spider):
self.writer.writerow([
item.get('title', ''),
item.get('price', ''),
item.get('rating', ''),
item.get('crawled_time', '')
])
return itemclass RobustSpider(scrapy.Spider):
name = "robust_spider"
def start_requests(self):
# 设置自定义请求参数
for url in self.start_urls:
yield scrapy.Request(
url,
callback=self.parse,
errback=self.errback,
meta={
'max_retry_times': 3,
'retry_delay': 1000 # 1秒
}
)
def parse(self, response):
# 正常解析逻辑
pass
def errback(self, failure):
# 错误处理
self.logger.error(f"请求失败: {failure.value}")
# 可以在这里实现重试逻辑
if failure.check(HttpError):
response = failure.value.response
self.logger.error(f'HTTP错误: {response.status}')# 启动交互式shell
scrapy shell 'https://example.com'
# 在shell中测试选择器
>>> response.css('title::text').get()
>>> view(response) # 在浏览器中查看响应# 查看爬虫列表
scrapy list
# 检查爬虫代码
scrapy check book_spider
# 下载网页内容查看
scrapy fetch --nolog 'https://example.com'
# 在浏览器中查看Scrapy下载的页面
scrapy view 'https://example.com'遵守robots.txt:尊重网站的爬虫规则
设置合理延迟:避免对网站造成压力
使用用户代理:模拟真实浏览器行为
处理异常:添加完善的错误处理
限制爬取范围:只爬取需要的数据
保存爬取状态:支持断点续爬
监控爬取进度:记录日志和统计信息
一个完整的Scrapy项目通常包含:
my_scrapy_project/
scrapy.cfg
my_scrapy_project/
__init__.py
items.py # 数据模型
pipelines.py # 数据处理
settings.py # 配置
middlewares.py # 中间件
spiders/
__init__.py
books.py # 图书爬虫
news.py # 新闻爬虫
utils/
__init__.py
helpers.py # 辅助函数| 方法名 | 作用描述 | 示例 |
|---|---|---|
| start_requests() | 生成初始请求,可以自定义请求头、请求方法等。 | yield scrapy.Request(url, callback=self.parse) |
| parse(response) | 处理响应并提取数据,是爬虫的核心方法。 | yield {'title': response.css('h1::text').get()} |
| follow(url, callback) | 自动处理相对 URL 并生成新的请求,用于分页或链接跳转。 | yield response.follow(next_page, callback=self.parse) |
| closed(reason) | 爬虫关闭时调用,用于清理资源或记录日志。 | def closed(self, reason): print('Spider closed:', reason) |
| log(message) | 记录日志信息。 | self.log('This is a log message') |
| 方法名 | 作用描述 | 示例 |
|---|---|---|
| response.css(selector) | 使用 CSS 选择器提取数据。 | title = response.css('h1::text').get() |
| response.xpath(selector) | 使用 XPath 选择器提取数据。 | title = response.xpath('//h1/text()').get() |
| get() | 从 SelectorList 中提取第一个匹配的结果(字符串)。 | title = response.css('h1::text').get() |
| getall() | 从 SelectorList 中提取所有匹配的结果(列表)。 | titles = response.css('h1::text').getall() |
| attrib | 提取当前节点的属性。 | link = response.css('a::attr(href)').get() |
| 方法名 | 作用描述 | 示例 |
|---|---|---|
| scrapy.Request(url, callback, method, headers, meta) | 创建一个新的请求。 | yield scrapy.Request(url, callback=self.parse, headers=headers) |
| response.url | 获取当前响应的 URL。 | current_url = response.url |
| response.status | 获取响应的状态码。 | if response.status == 200: print('Success') |
| response.meta | 获取请求中传递的额外数据。 | value = response.meta.get('key') |
| response.headers | 获取响应的头信息。 | content_type = response.headers.get('Content-Type') |
| 方法名 | 作用描述 | 示例 |
|---|---|---|
| process_request(request, spider) | 在请求发送前处理请求(下载器中间件)。 | request.headers['User-Agent'] = 'Mozilla/5.0' |
| process_response(request, response, spider) | 在响应返回后处理响应(下载器中间件)。 | if response.status == 403: return request.replace(dont_filter=True) |
| process_item(item, spider) | 处理提取的数据(管道)。 | if item['price'] < 0: raise DropItem('Invalid price') |
| open_spider(spider) | 爬虫启动时调用(管道)。 | def open_spider(self, spider): self.file = open('items.json', 'w') |
| close_spider(spider) | 爬虫关闭时调用(管道)。 | def close_spider(self, spider): self.file.close() |
| 方法名 | 作用描述 | 示例 |
|---|---|---|
| scrapy shell | 启动交互式 Shell,用于调试和测试选择器。 | scrapy shell 'http://example.com' |
| scrapy crawl <spider_name> | 运行指定的爬虫。 | scrapy crawl myspider -o output.json |
| scrapy check | 检查爬虫代码的正确性。 | scrapy check |
| scrapy fetch | 下载指定 URL 的内容。 | scrapy fetch 'http://example.com' |
| scrapy view | 在浏览器中查看 Scrapy 下载的页面。 | scrapy view 'http://example.com' |
| 设置项 | 作用描述 | 示例 |
|---|---|---|
| USER_AGENT | 设置请求头中的 User-Agent。 | USER_AGENT = 'Mozilla/5.0' |
| ROBOTSTXT_OBEY | 是否遵守 robots.txt 规则。 | ROBOTSTXT_OBEY = False |
| DOWNLOAD_DELAY | 设置下载延迟,避免过快请求。 | DOWNLOAD_DELAY = 2 |
| CONCURRENT_REQUESTS | 设置并发请求数。 | CONCURRENT_REQUESTS = 16 |
| ITEM_PIPELINES | 启用管道。 | ITEM_PIPELINES = {'myproject.pipelines.MyPipeline': 300} |
| AUTOTHROTTLE_ENABLED | 启用自动限速扩展。 | AUTOTHROTTLE_ENABLED = True |
| 方法名 | 作用描述 | 示例 |
|---|---|---|
| response.follow_all(links, callback) | 批量处理链接并生成请求。 | yield from response.follow_all(links, callback=self.parse) |
| response.json() | 将响应内容解析为 JSON 格式。 | data = response.json() |
| response.text | 获取响应的文本内容。 | html = response.text |
| response.selector | 获取响应内容的 Selector 对象。 | title = response.selector.css('h1::text').get() |
以上表格列出了 Scrapy 中常用的方法及其作用。这些方法涵盖了爬虫开发的各个方面,包括请求生成、数据提取、中间件处理、管道操作等。通过掌握这些方法,你可以高效地编写和管理 Scrapy 爬虫。
Scrapy是一个功能完整的爬虫框架,适合处理复杂的网页抓取任务。主要特点包括:
结构化的项目组织
强大的数据提取能力
灵活的数据处理管道
可扩展的中间件系统
完善的配置管理
对于简单的爬虫任务,可以使用requests+BeautifulSoup组合。但对于大规模、复杂的爬虫项目,Scrapy是更好的选择。
记住在使用爬虫时要遵守相关法律法规和网站的使用条款。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!