Python爬虫怎么提高开发效率_使用Scrapy框架构建模块化爬虫
按业务域分包(如news/、ecom/)并分离items、pipelines,配置SPIDER_MODULES;解析规则抽离为JSON+通用解析器;中间件只做轻量反爬处理;调试时检查start_urls、allowed_domains及Item字段名。
Scrapy项目结构怎么组织才不容易后期改崩
Scrapy默认生成的目录结构偏扁平,spiders/里塞十几二十个爬虫后,items.py和pipelines.py会迅速变成全局污染源——字段名冲突、Pipeline逻辑耦合、中间件复用困难是高频问题。
推荐按业务域分包,例如:
myproject/ ├── spiders/ │ ├── news/ │ │ ├── sina_spider.py │ │ └── qq_spider.py │ └── ecom/ │ ├── jd_spider.py │ └── taobao_spider.py ├── items/ │ ├── news_items.py │ └── ecom_items.py ├── pipelines/ │ ├── news_pipelines.py │ └── ecom_pipelines.py └── middlewares.py # 全局中间件仍放这里,但只做UA/代理/重试等通用逻辑
关键点:
-
settings.py中通过SPIDER_MODULES = ['myproject.spiders.news', 'myproject.spiders.ecom']注册多个模块路径 - 每个
items.py文件只定义本域所需字段,避免ItemLoader加载时因字段缺失或类型错位报KeyError或TypeError - 在Spider类中显式导入对应
Item和Pipeline,不依赖全局import,便于单测和迁移
如何让不同爬虫复用同一套解析逻辑又不互相干扰
硬编码XPath/CSS选择器到每个Spider里,改一个网站结构就得翻10个文件。真正可维护的做法是把解析规则抽成独立配置+轻量解析器。
例如在myproject/rules/下建news_rules.json:
{
"sina.com": {
"title": "//h1[@class='main-title']/text()",
"content": "//div[@id='article']/p//text()",
"publish_time": "//span[@class='date']/text()"
},
"qq.com": {
"title": "//h1[@class='qq_title']/text()",
"content": "//div[@class='content-article']/p//text()",
"publish_time": "//span[@class='a_time']/text()"
}
}
再写一个通用解析器parser.py:
import json from scrapy.selector import Selectordef load_rules(domain): with open(f'rules/news_rules.json') as f: return json.load(f).get(domain, {})
def parse_with_rules(response, domain): rules = load_rules(domain) sel = Selector(response) return { k: sel.xpath(v).getall() if 'text()' in v else sel.xpath(v).get() for k, v in rules.items() }
这样每个Spider只需调用parse_with_rules(response, 'sina.com'),规则增删改不影响爬虫主逻辑,也不用动XPath引擎。
OFFER快
首个全流程托管的 AI 求职 Agent(自动筛选、沟通、网申)
下载
Scrapy中间件怎么写才能既处理反爬又不拖慢整个爬取流程
很多新手把所有反爬应对(UA轮换、代理切换、验证码识别)全塞进process_request(),结果请求还没发出去就卡在中间件里,DOWNLOAD_DELAY被绕过,IP被封得比爬得快。
核心原则:中间件只做「必要且轻量」的事,耗时操作必须异步或延后。
- UA轮换用
random.choice(USER_AGENTS)直接赋值request.headers['User-Agent'],别调外部API - 代理切换不要每次请求都查数据库或调API,用
scrapy-rotating-proxies这类成熟方案,它内置连接池和健康检查 - 遇到
412或503响应时,别在中间件里同步解验证码——改用errback机制,在spider.parse外单独捕获,交由专门服务处理 - 对静态资源(CSS/JS/图片)请求,用
if request.url.endswith(('.js', '.css', '.png')): return快速跳过中间件逻辑
本地调试Scrapy爬虫为什么总卡在start_urls没反应
最常见原因是start_urls为空,或start_requests()方法返回了空迭代器,而Scrapy不会报错,只是静默退出。
排查步骤:
- 运行
scrapy crawl myspider -s LOG_LEVEL=DEBUG,看日志里有没有Scheduling行 - 在
start_requests()里加self.logger.info(f"Generated {len(urls)} start requests"),确认URL列表非空 - 检查
allowed_domains是否误写了子域名(如写成www.sina.com但实际URL是news.sina.com),导致Scrapy自动过滤掉所有请求 - 如果用了
CrawlSpider,确认rules里的LinkExtractor正则是否写错,restrict_xpaths路径是否存在——它不会告诉你提取不到链接,只会安静地不发起任何请求
真正麻烦的是那种“看起来跑起来了但数据根本没进Pipeline”的情况:往往是因为Item字段名拼错,或者ItemLoader里add_xpath()传入了不存在的节点,导致字段为None,而Pipeline里又没做空值校验,最终数据被静默丢弃。