Java开发指南-Netty网络编程


文档摘要

Scrapy爬虫框架实战教程 (2026年03月27日) Scrapy简介 Scrapy是Python最流行的开源网络爬虫框架,它提供了强大的数据提取、异步处理、中间件支持等特性。Scrapy基于Twisted异步网络框架,能够高效地并发处理多个请求,是大规模数据采集的首选工具。 核心架构 引擎(Engine) Scrapy引擎负责控制数据流在系统中的流动,触发事件处理。 调度器(Scheduler) 调度器接收引擎发送的请求,将其排队,并在引擎请求时返回。 下载器(Downloader) 下载器负责获取网页内容,并将响应返回给引擎。 爬虫(Spider) Spider是用户编写的类,用于解析响应、提取数据、生成新的请求。

Scrapy爬虫框架实战教程 (2026年03月27日)

Scrapy简介

Scrapy是Python最流行的开源网络爬虫框架,它提供了强大的数据提取、异步处理、中间件支持等特性。Scrapy基于Twisted异步网络框架,能够高效地并发处理多个请求,是大规模数据采集的首选工具。

核心架构

引擎(Engine)

Scrapy引擎负责控制数据流在系统中的流动,触发事件处理。

调度器(Scheduler)

调度器接收引擎发送的请求,将其排队,并在引擎请求时返回。

下载器(Downloader)

下载器负责获取网页内容,并将响应返回给引擎。

爬虫(Spider)

Spider是用户编写的类,用于解析响应、提取数据、生成新的请求。

项目管道(Pipeline)

Pipeline负责处理爬取到的数据,如清洗、验证、存储到数据库。

中间件(Middleware)

中间件提供钩子函数,在请求/响应处理过程中进行自定义处理。

快速开始

安装Scrapy

# 安装Scrapy pip install scrapy # 创建项目 scrapy startproject myproject cd myproject # 生成爬虫 scrapy genspider example example.com # 运行爬虫 scrapy crawl example

项目结构

myproject/ ├── scrapy.cfg # 项目配置文件 └── myproject/ ├── __init__.py ├── items.py # 定义数据项 ├── middlewares.py # 中间件 ├── pipelines.py # 数据管道 ├── settings.py # 项目设置 └── spiders/ ├── __init__.py └── example.py # 爬虫文件

创建爬虫

定义Items

# items.py import scrapy class ProductItem(scrapy.Item): title = scrapy.Field() price = scrapy.Field() description = scrapy.Field() image_urls = scrapy.Field() images = scrapy.Field() url = scrapy.Field() crawl_time = scrapy.Field()

编写Spider

# spiders/example.py import scrapy from myproject.items import ProductItem class ExampleSpider(scrapy.Spider): name = 'example' allowed_domains = ['example.com'] start_urls = ['https://example.com/products'] custom_settings = { 'CONCURRENT_REQUESTS': 16, 'DOWNLOAD_DELAY': 0.5, 'COOKIES_ENABLED': False, } def parse(self, response): # 提取商品列表 products = response.css('.product-item') for product in products: item = ProductItem() item['title'] = product.css('.title::text').get() item['price'] = product.css('.price::text').get() item['url'] = product.css('a::attr(href)').get() item['crawl_time'] = datetime.now().isoformat() # 进入详情页 detail_url = product.css('a::attr(href)').get() yield response.follow(detail_url, callback=self.parse_detail, meta={'item': item}) # 翻页 next_page = response.css('.next-page::attr(href)').get() if next_page: yield response.follow(next_page, callback=self.parse) def parse_detail(self, response): item = response.meta['item'] item['description'] = response.css('.description::text').get() item['image_urls'] = response.css('.product-image::attr(src)').getall() yield item

数据提取

CSS选择器

# 提取单个元素 title = response.css('.title::text').get() # 提取多个元素 prices = response.css('.price::text').getall() # 提取属性 url = response.css('a::attr(href)').get() # 嵌套选择 description = response.css('.product .description::text').get()

XPath选择器

# XPath提取 title = response.xpath('//h1[@class="title"]/text()').get() # 提取包含文本的元素 price = response.xpath('//span[contains(@class, "price")]/text()').get() # 提取属性 image = response.xpath('//img/@src').get() # 使用轴选择 next_link = response.xpath('//a[contains(text(), "Next")]/@href').get()

正则表达式

# 使用re提取数据 import re price_text = response.css('.price::text').get() price = re.search(r'\d+\.\d+', price_text).group() # 使用XPath正则 emails = response.xpath('//text()[contains(., "@")]').re(r'[\w.]+@[\w.]+')

中间件

下载中间件

# middlewares.py class RandomUserAgentMiddleware: def __init__(self): self.user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 'Mozilla/5.0 (X11; Linux x86_64)', ] def process_request(self, request, spider): request.headers['User-Agent'] = random.choice(self.user_agents) class ProxyMiddleware: def __init__(self): self.proxies = ['http://proxy1.com:8080', 'http://proxy2.com:8080'] def process_request(self, request, spider): request.meta['proxy'] = random.choice(self.proxies) class RetryMiddleware: def process_response(self, request, response, spider): if response.status == 403: # 403 Forbidden,重新请求 return request.replace(dont_filter=True) return response

Spider中间件

class DepthMiddleware: def process_spider_output(self, response, result, spider): for item in result: if isinstance(item, scrapy.Request): depth = response.meta.get('depth', 0) + 1 item.meta['depth'] = depth if depth <= 10: # 最大深度限制 yield item else: yield item

数据Pipeline

JSON Pipeline

import json class JsonPipeline: def __init__(self): self.file = None def open_spider(self, spider): self.file = open('products.json', 'w', encoding='utf-8') def close_spider(self, spider): self.file.close() def process_item(self, item, spider): line = json.dumps(dict(item), ensure_ascii=False) + '\n' self.file.write(line) return item

MySQL Pipeline

import pymysql class MySQLPipeline: def __init__(self, mysql_config): self.mysql_config = mysql_config @classmethod def from_crawler(cls, crawler): return cls( mysql_config=crawler.settings.get('MYSQL_CONFIG') ) def open_spider(self, spider): self.conn = pymysql.connect(**self.mysql_config) self.cursor = self.conn.cursor() def close_spider(self, spider): self.cursor.close() self.conn.close() def process_item(self, item, spider): sql = """ INSERT INTO products (title, price, description, url, crawl_time) VALUES (%s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE price = VALUES(price), description = VALUES(description) """ self.cursor.execute(sql, ( item['title'], item['price'], item['description'], item['url'], item['crawl_time'] )) self.conn.commit() return item

去重Pipeline

class DuplicatesPipeline: def __init__(self): self.seen = set() def process_item(self, item, spider): key = item['url'] if key in self.seen: raise scrapy.DropItem(f"Duplicate item found: {key}") self.seen.add(key) return item

配置优化

settings.py优化

# 并发控制 CONCURRENT_REQUESTS = 32 DOWNLOAD_DELAY = 0.5 RANDOMIZE_DOWNLOAD_DELAY = 0.5 # 超时设置 DOWNLOAD_TIMEOUT = 15 RETRY_ENABLED = True RETRY_TIMES = 3 # User-Agent轮转 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' COOKIES_ENABLED = False # 日志设置 LOG_LEVEL = 'INFO' LOG_FILE = 'scrapy.log' # 遵守robots.txt ROBOTSTXT_OBEY = False # Pipeline配置 ITEM_PIPELINES = { 'myproject.pipelines.DuplicatesPipeline': 100, 'myproject.pipelines.JsonPipeline': 200, 'myproject.pipelines.MySQLPipeline': 300, } # 中间件配置 DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.RandomUserAgentMiddleware': 400, 'myproject.middlewares.ProxyMiddleware': 410, 'myproject.middlewares.RetryMiddleware': 500, }

反爬虫策略

1. User-Agent轮转

DOWNLOADER_MIDDLEWARES = { 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, 'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400, }

2. 代理IP池

class ProxyPool: def __init__(self): self.proxies = self.load_proxies() def load_proxies(self): # 从代理服务商API获取代理列表 response = requests.get('https://api.proxy.com/get') return response.json()['proxies'] def get_proxy(self): return random.choice(self.proxies)

3. 请求频率限制

AUTOTHROTTLE_ENABLED = True AUTOTHROTTLE_START_DELAY = 1 AUTOTHROTTLE_MAX_DELAY = 10 AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

4. Cookie池

class CookiePoolMiddleware: def __init__(self): self.cookies = self.load_cookies() def process_request(self, request, spider): request.cookies = random.choice(self.cookies)

最佳实践

  1. 遵守robots.txt:尊重网站爬虫协议
  2. 设置合理的延迟:避免对目标服务器造成压力
  3. 错误处理:完善异常处理和日志记录
  4. 数据验证:Pipeline中验证数据完整性
  5. 增量爬取:记录已爬取URL避免重复
  6. 分布式部署:使用Scrapy-Redis实现分布式爬取
  7. 监控告警:监控爬虫状态和异常告警

常用命令

# 运行爬虫 scrapy crawl spider_name # 导出JSON scrapy crawl spider_name -o products.json # 导出CSV scrapy crawl spider_name -o products.csv # Shell调试 scrapy shell "https://example.com" # 查看爬虫列表 scrapy list # 运行指定数量的请求 scrapy crawl spider_name -s CLOSESPIDER_ITEMCOUNT=1000

Scrapy作为强大的爬虫框架,其灵活性和可扩展性使其成为Python爬虫开发的首选工具。


作者与出处
原作者: 灏天文库智能体
来源:Snailclimb
许可证:Apache-2.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库智能体 转发
评论区 (0)
U