微信搜索【猿码记】查看更多文章...
1.介绍
Scrapy
是一个用于爬取网站数据的Python
框架。它提供了一套强大而灵活的工具,使开发者能够轻松地创建和管理爬虫,从而从网站中提取所需的信息。框架要求Python的版本 3.8+
-
Github Star:49.6k: https://github.com/scrapy/scrapy -
中文文档: https://www.osgeo.cn/scrapy/intro/tutorial.html
1.1 特点和优势
以下是一些Scrapy的主要特点和优势:
-
结构化的框架: Scrapy
提供了一个清晰的结构,将爬虫的不同部分组织起来,包括爬虫代码、中间件、下载器等。这种结构使得代码模块化,易于维护和扩展。 -
异步处理: Scrapy
使用Twisted
异步网络引擎,能够并行处理多个请求,提高爬取效率。这使得Scrapy
适用于大规模的数据抓取任务。 -
内置的选择器: Scrapy
内置了强大的选择器(XPath和CSS选择器
),使得开发者能够方便地从HTML
或XML
文档中提取所需的数据。 -
中间件支持: 可以通过中间件进行请求和响应的预处理,例如添加代理、修改 User-Agent
等,从而提高爬虫的灵活性和适应性。 -
自动限速: Scrapy
支持自动限速功能,可以设置每秒发送请求的数量,防止对目标服务器造成过大的负担,也有助于规避反爬虫策略。 -
扩展性: Scrapy
提供了丰富的插件系统,允许开发者通过编写扩展或中间件来自定义爬虫的行为,使其更适应特定的需求。 -
数据存储: 支持将爬取到的数据存储到多种格式,包括 JSON、CSV、XML
等,也可以存储到数据库中。 -
调试工具: Scrapy
提供了命令行工具和图形界面(Scrapy Shell
)用于调试和测试爬虫,方便开发者查看和验证提取的数据。
1.2 安装
# 安装
$ pip install scrapy
# 查看版本
$ scrapy version
Scrapy 2.11.0
1.3 子命令
$ scrapy
Scrapy 2.11.0 - active project: scrapy_study_demo
Usage:
scrapy <command> [options] [args]
Available commands:
bench Run quick benchmark test
check Check spider contracts
crawl Run a spider
edit Edit spider
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
list List available spiders
parse Parse URL (using its spider) and print the results
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy
主要子命令及其作用:
-
bench
: 运行快速的性能基准测试,用于评估Scrapy
的性能。 -
check
: 用于检查Scrapy
项目的代码并执行一些基本的检查,以确保项目的结构和配置正确; -
crawl
: 用于启动一个爬虫,如:scrapy crawl xxx
; -
edit
: 用于编辑爬虫代码,可以指定使用哪个编辑器打开,一般不用; -
fetch
: 使用Scrapy
下载器获取指定URL
的内容,并将其显示在命令行中。用于测试单个URL
的下载和解析。 -
genspider
: 生成新的爬虫spider
,使用预定义的模板来快速创建新的爬虫项目。可以指定爬虫的名称、域名等参数。 -
list
: 用于列出可用的爬虫,需求在爬虫项目目录下执行; -
runspider
: 运行一个独立的爬虫,而不必创建整个Scrapy
项目。这是一种简便的方式来测试或运行单个爬虫文件。 -
settings
: 获取当前Scrapy
项目的设置值。可以查看当前项目的配置信息。 -
shell
: 启动一个交互式的Scrapy
控制台,方便开发者在命令行中测试和调试爬虫代码,查看和提取数据。 -
startproject
: 创建一个新的Scrapy
项目。该命令将生成一个包含基本结构的新目录,包括默认的设置、爬虫模板等。 -
version
: 显示当前安装的Scrapy
版本号。 -
view
: 在默认的Web
浏览器中打开指定的URL
,以查看网页内容。可以帮助开发者直观地查看网页,了解其结构。
2.初始化项目
scrapy
提供了类似脚手架子命令:scrapy startproject xxx
,可以用来直接创建一个项目,免去我们一些繁琐工作;
2.1 创建项目
startproject
: 创建一个新的Scrapy
项目。该命令将生成一个包含基本结构的新目录,包括默认的设置、爬虫模板等。
# 创建项目
$ scrapy startproject scrapy_study_demo
New Scrapy project 'scrapy_study_demo', using template directory '/opt/anaconda3/envs/py310/lib/python3.10/site-packages/scrapy/templates/project', created in:
/Users/hui/ProjectSpace/PythonItem/scrapy_study_demo
You can start your first spider with:
cd scrapy_study_demo
scrapy genspider example example.com
# 添加示例
$ cd scrapy_study_demo && scrapy genspider example example.com
Created spider 'example' using template 'basic' in module:
scrapy_study_demo.spiders.example
2.2 目录介绍
scrapy_study_demo # 项目目录
├── scrapy.cfg # 配置文件
└── scrapy_study_demo # 项目的Python包
├── items.py # 定义爬取的数据结构(Item)
├── middlewares.py# 中间件
├── pipelines.py # 管道
├── settings.py #项目的配置文件
└── spiders # 存放爬虫代码的目录
└── example.py # scrapy genspider example example.com 创建的实例代码
上述部分文件的具体作用说明:
-
items.py
: 定义爬取的数据结构(Item
)。每个Item
对应爬虫要提取的信息的数据模型。 -
middlewares.py
: 包含中间件的文件。中间件是在Scrapy
引擎和下载器之间处理请求和响应的钩子。 -
pipelines.py
: 包含管道的文件。管道负责处理爬取到的Item
,例如存储到数据库或文件。 -
settings.py
: 项目的配置文件,包含各种配置选项,如下载延迟、用户代理等
3.实战示例
下面以爬取豆瓣:【全年代日本动画Top500口碑排行榜】为实战示例,仅供学习使用,勿做其他用途~
访问地址: https://www.douban.com/doulist/45955373/
3.1 创建爬虫
genspider
: 生成新的爬虫spider
,使用预定义的模板来快速创建新的爬虫项目。可以指定爬虫的名称、域名等参数。
# 在当前项目执行,后面网址是我们需要爬的网页
$ scrapy genspider animeRank https://www.douban.com/doulist/45955373/
Created spider 'animeRank' using template 'basic' in module:
scrapy_study_demo.spiders.animeRank
上述命令会创建文件:scrapy_study_demo/spiders/animeRank.py
,并生成填充一些代码,个人感觉代码生成的有写瑕疵,如下:
@注: 创建的类名,手动做了修改,上图未标注;
优化后代码如下:
import scrapy
from typing import Any
from scrapy.http import Response
class AnimeRankSpider(scrapy.Spider):
name = "animeRank" # 爬虫名称,必须是唯一
allowed_domains = ["www.douban.com"] #
start_urls = ["https://www.douban.com/doulist/45955373/"]
def parse(self, response: Response, **kwargs: Any):
# 打印抓取的网页HTML
print("网页内容:", response.text)
pass
3.2 启动爬虫
crawl
: 用于启动一个爬虫,如:scrapy crawl xxx
;
# animeRank取自 name = "animeRank"
$ scrapy crawl animeRank
...
2023-12-20 12:04:17 [scrapy.core.engine] INFO: Spider opened
2023-12-20 12:04:17 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2023-12-20 12:04:17 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2023-12-20 12:04:17 [scrapy.core.engine] DEBUG: Crawled (403) <GET https://www.douban.com/robots.txt> (referer: None)
2023-12-20 12:04:18 [scrapy.core.engine] DEBUG: Crawled (403) <GET https://www.douban.com/doulist/45955373/> (referer: None)
# 具体错误信息
2023-12-20 12:04:18 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403 https://www.douban.com/doulist/45955373/>: HTTP status code is not handled or not allowed
2023-12-20 12:04:18 [scrapy.core.engine] INFO: Closing spider (finished)
...
一般网站都会做一些反爬虫措施,从上面错误可以看出,在爬取网页时,豆瓣返回了403,用scrapy view
验证下
$ scrapy view https://www.douban.com/doulist/45955373
3.3 修改配置
a.常用配置信息
有时针对一些反爬虫网站,需要做些额外配置才能进行数据获取,我们简单了解下常用的配置有哪些?
-
USER_AGENT
: 设置请求的User-Agent
头,模拟不同浏览器的请求。 -
DOWNLOAD_DELAY
: 设置请求的下载延迟,以避免对目标网站的过度请求。 -
CONCURRENT_REQUESTS
: 设置同时发送的请求数。 -
CONCURRENT_REQUESTS_PER_DOMAIN
: 设置单个域名同时发送的请求数。 -
COOKIES_ENABLED
: 是否启用Cookies
处理。 -
DEFAULT_REQUEST_HEADERS
: 设置默认的请求头。 -
ROBOTSTXT_OBEY
: 是否遵循网站的robots.txt
规则。True/False
-
LOG_LEVEL
: 设置日志输出的级别,DEBUG、INFO、WARNING、ERROR、CRITICAL
-
LOG_STDOUT
: 输出日志信息到标准输出(控制台); -
LOG_FILE
: 可以指定日志文件,默认输出到控制台; -
AUTOTHROTTLE_ENABLED
: 启用自动限速调整下载延迟,True/False
; -
AUTOTHROTTLE_START_DELAY
: 自动限速扩展时的初始等待时间,单位秒; -
AUTOTHROTTLE_MAX_DELAY
: 自动限速算法允许的最大等待时间,单位秒;
上面配置信息只是九牛一毛,更多配置信息可以查看文档: https://www.osgeo.cn/scrapy/topics/settings.html 记住一些常用的,其他的用到时候查看下文档即可;
b.修改配置
针对被扒网站返回403的问题,一般情况只需要设置下请求的User-Agent
头,模拟是浏览器的请求即可。修改文件:scrapy_study_demo/settings.py
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
c.再次运行爬虫
$ scrapy crawl animeRank
网页内容: <!DOCTYPE html>
<html lang="zh-CN" class="ua-windows ua-webkit">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="renderer" content="webkit">
<meta name="referrer" content="always">
<meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />
<title>全年代日本动画Top500口碑排行榜</title>
...
3.4 debug爬虫
为了方便后面解析内容,验证代码是否解析有误,我们先研究下怎么使用pycharm+debug
的方式来运行程序;不能每次都通过scrapy crawl animeRank
,这样定位问题会很慢;
1.创建运行文件
创建目录和文件crawlrun/anime_rank.py
,内容如下:
from scrapy.cmdline import execute
execute("scrapy crawl animeRank".split())
2.debug运行文件
3.5 解析语法
走到这一步,说明我们学会了项目的基础使用,解决了403问题,但是我们还没有解析网页内容,网页往往是由一堆HTML
组成,那如何获取我们想要的内容呢? Scrapy
框架提供了两种机制,分别是基于 XPath 和 CSS 的表达式;
1.html代码
使用两种方式分别解析下面前端代码,并提取出标题和链接
<body>
<div class="container">
<h2 class="title">三国演义</h2>
<a href="https://example.com/sanguo" class="link">Link 1</a>
<h2 class="title">西游记</h2>
<a href="https://example.com/xiyouji" class="link">Link 2</a>
<h2 class="title">水浒传</h2>
<a href="https://example.com/shuihu" class="link">Link 3</a>
</div>
</body>
2.使用XPath解析
def parse(self, response: Response, **kwargs: Any):
# 使用XPath选择器提取页面中的标题和链接
titles = response.xpath('//h2[@class="title"]/text()').getall()
links = response.xpath('//a[@class="link"]/@href').getall()
# 打印结果
for title, link in zip(titles, links):
print(f'Title: {title}, Link: {link}')
3.使用CSS解析
def parse(self, response: Response, **kwargs: Any):
# 使用XPath选择器提取页面中的标题和链接
titles = response.css('h2.title::text').getall()
links = response.css('a.link::attr(href)').getall()
# 打印结果
for title, link in zip(titles, links):
content = f'Title: {title}, Link: {link}'
print(content)
@注: 限于文章篇幅,这里只做简单示例,不做过多讲解;更多使用方式请参考文档:https://www.osgeo.cn/scrapy/topics/selectors.html#selecting-attributes
3.6 解析内容
上面简单介绍了下解析语法,接着继续我们的爬虫示例,在爬取数据之前,我们先定义下数据结构,方便后面保存结构化数据;
1. 定义数据结构
在文件:scrapy_study_demo/items.py
下定义数据结构:
import scrapy
class JapaneseAnimeItem(scrapy.Item):
""" 定义动漫电影数据结构信息"""
ranking = scrapy.Field() # 排名
title = scrapy.Field() # 名称
postImg = scrapy.Field() # 图片海报
year = scrapy.Field() # 年份
score = scrapy.Field() # 评分
ratingPeople = scrapy.Field() # 评分人数
2.爬虫代码
修改完整代码如下:
import scrapy
from typing import Any, Optional, Union
from scrapy import Spider
from scrapy.http import Response
from twisted.internet.defer import Deferred
from scrapy_study_demo import JapaneseAnimeItem
class AnimeRankSpider(scrapy.Spider):
name = "animeRank"
allowed_domains = ["www.douban.com"]
start_urls = ["https://www.douban.com/doulist/45955373/"]
max_pages = 2
result = []
def __init__(self, name: Optional[str] = None, **kwargs: Any):
super().__init__(name, **kwargs)
self.page_count = 1 # 计数器,默认
def parse(self, response: Response, **kwargs: Any):
"""解析网页内容"""
# 打印抓取的网页HTML
listItems = response.xpath('//div[@class="article"]//div[@class="doulist-item"]')
for item in listItems:
ranking = item.xpath('.//span[@class="pos"]/text()').get()
title = item.xpath('.//div[@class="title"]/a/text()').get()
score = item.xpath('.//span[@class="rating_nums"]/text()').get()
ratingPeople = item.xpath('.//div[@class="rating"]/span[last()]/text()').get()
postImg = item.xpath('.//div[@class="post"]/a/img/@src').get()
year = item.xpath('.//div[@class="abstract"]/text()').re_first(r'年份:\s*(\d+)')
tmp = JapaneseAnimeItem()
tmp["ranking"] = ranking.strip() if ranking else None
tmp["title"] = title.strip() if title else None
tmp["postImg"] = postImg.strip() if postImg else None
tmp["year"] = year.strip() if year else None
tmp["score"] = score.strip() if score else None
tmp["ratingPeople"] = ratingPeople.strip() if ratingPeople else None
self.result.append(tmp)
yield tmp
# 处理分页
next_page = response.css('.next a::attr(href)').get()
if next_page and self.page_count < self.max_pages:
self.page_count += 1
print(f"准备请求{self.page_count}页, 链接:{next_page}")
yield scrapy.Request(url=next_page, callback=self.parse)
def close(self, reason: str) -> Union[Deferred, None]:
# 这里可以打印汇总后的数据
print("result:", self.result)
return super().close(self, reason)
3.运行输出json
等代码调试完成后,可以运行下面程序,把爬取到的结果,输出到json
文件,我们也可以在close
或者parse
函数中对数据进行入库操作,具体结合实际业务使用。
$ scrapy crawl animeRank -O animeRank.json
准备请求2页, 链接:https://www.douban.com/doulist/45955373/?start=25&sort=seq&playable=0&sub_type=
准备请求3页, 链接:https://www.douban.com/doulist/45955373/?start=50&sort=seq&playable=0&sub_type=
准备请求4页, 链接:https://www.douban.com/doulist/45955373/?start=75&sort=seq&playable=0&sub_type=
准备请求5页, 链接:https://www.douban.com/doulist/45955373/?start=100&sort=seq&playable=0&sub_type=
Scrapy
是一个很成熟,功能很强大的爬虫框架,通过本文我们只是触及到了它众多功能的表面,不同的项目有不同的需求,在使用Scrapy
的过程中,可以不断查阅官方文档,来了解更多使用方法。后续有时间会在写一篇续篇....
本文由 mdnice 多平台发布