2.1 Spiders (爬虫) Scrapy 核心组件详解:Spiders (爬虫) 在 Scrapy 框架中,Spiders (爬虫) 是整个爬取流程的核心驱动力。它们定义了如何从特定网站或多个网站抓取数据,并从页面中提取结构化数据。简单来说,Spiders 负责告诉 Scrapy "去哪里爬","怎么爬",以及 "爬什么"。 2.1 Spiders (爬虫) 详解 Spiders 本质上是 Python 类,继承自 类。 你需要定义 Spiders 的一些关键属性和方法来定制爬取行为。 2.1.1 Spiders 的核心属性: (必须): 爬虫的名称,用于唯一标识一个爬虫。在运行 Scrapy 时,你需要通过这个名称来指定要运行的爬虫。 (可选): 一个包含允许爬取的域名列表。
在 Scrapy 框架中,Spiders (爬虫) 是整个爬取流程的核心驱动力。它们定义了如何从特定网站或多个网站抓取数据,并从页面中提取结构化数据。简单来说,Spiders 负责告诉 Scrapy "去哪里爬","怎么爬",以及 "爬什么"。
Spiders 本质上是 Python 类,继承自 scrapy.Spider 类。 你需要定义 Spiders 的一些关键属性和方法来定制爬取行为。
2.1.1 Spiders 的核心属性:
name (必须): 爬虫的名称,用于唯一标识一个爬虫。在运行 Scrapy 时,你需要通过这个名称来指定要运行的爬虫。
allowed_domains (可选): 一个包含允许爬取的域名列表。Scrapy 会自动过滤掉不属于这些域名的 URL。 这有助于避免爬虫爬出目标网站的范围。
start_urls (必须): 一个包含起始 URL 的列表。爬虫会从这些 URL 开始爬取。
start_requests() (可选): 如果需要自定义起始请求,可以重写这个方法。它必须返回一个可迭代的 scrapy.Request 对象。 这允许你设置请求头、cookies、代理等。
custom_settings (可选): 一个字典,用于覆盖 Scrapy 项目的全局设置。这使得你可以为特定的爬虫定制设置,例如 User-Agent、下载延迟等。
2.1.2 Spiders 的核心方法:
parse(response) (必须): 这是最重要的一个方法。它接收一个 scrapy.http.Response 对象作为参数,该对象包含了下载的页面内容。 你需要在 parse 方法中提取数据,并生成 scrapy.Item 对象,或者生成新的 scrapy.Request 对象,以便继续爬取。2.1.3 Spiders 的工作流程:
Spiders 的工作流程可以用下图表示:
定义 Spider: 你首先需要定义一个 Spider 类,并设置其 name、allowed_domains 和 start_urls 属性。
Scrapy 引擎启动: Scrapy 引擎启动后,会找到你定义的 Spider,并从 start_urls 中获取起始 URL。
生成 Request 对象: Scrapy 会为每个起始 URL 生成一个 scrapy.Request 对象。
调度器和下载器: Request 对象会被发送到调度器,然后经过下载器中间件,最终由下载器下载页面内容。
生成 Response 对象: 下载器将下载的页面内容封装成一个 scrapy.http.Response 对象。
调用 parse() 方法: Response 对象会被传递给 Spider 的 parse() 方法。
提取数据和生成 Item: 在 parse() 方法中,你需要使用 CSS 选择器或 XPath 表达式从 Response 对象中提取数据,并将提取的数据填充到 scrapy.Item 对象中。
生成新的 Request 对象 (可选): 如果页面中包含需要进一步爬取的链接,你可以在 parse() 方法中生成新的 scrapy.Request 对象,并将它们返回给 Scrapy 引擎。
Item Pipeline 处理: Item 对象会被发送到 Item Pipeline 进行处理,例如数据清洗、验证、存储等。
循环爬取: Scrapy 会不断地从 parse() 方法返回的 Request 对象中爬取新的页面,直到所有页面都被爬取完毕。
以下是一个简单的 Spider 示例,用于爬取 quotes.toscrape.com 网站上的名言:
import scrapy class QuotesSpider(scrapy.Spider): name = "quotes" allowed_domains = ["quotes.toscrape.com"] start_urls = [ "http://quotes.toscrape.com/page/1/", "http://quotes.toscrape.com/page/2/", ] def parse(self, response): for quote in response.css("div.quote"): yield { "text": quote.css("span.text::text").get(), "author": quote.css("small.author::text").get(), "tags": quote.css("div.tags a.tag::text").getall(), } # 爬取下一页 next_page = response.css("li.next a::attr(href)").get() if next_page is not None: next_page = response.urljoin(next_page) # 确保URL是绝对路径 yield scrapy.Request(next_page, callback=self.parse) # 回调函数仍然是parse
代码详解:
name = "quotes": 定义爬虫的名称为 "quotes"。
allowed_domains = ["quotes.toscrape.com"]: 限制爬取范围为 "quotes.toscrape.com" 域名。
start_urls = [...]: 定义起始 URL 列表,这里是网站的前两页。
parse(self, response): 这是核心的解析方法。
response.css("div.quote"): 使用 CSS 选择器选择所有包含名言的 div 元素。
quote.css("span.text::text").get(): 从每个名言的 div 元素中,使用 CSS 选择器提取名言文本。::text 表示提取文本内容,.get() 返回第一个匹配的结果。
quote.css("small.author::text").get(): 提取作者姓名。
quote.css("div.tags a.tag::text").getall(): 提取标签列表。 getall() 返回所有匹配的结果。
yield { ... }: 使用 yield 关键字返回一个 Python 字典,这个字典会被 Scrapy 自动转换为 scrapy.Item 对象。
爬取下一页:
response.css("li.next a::attr(href)").get(): 使用 CSS 选择器提取下一页的链接。::attr(href) 表示提取 href 属性的值。
if next_page is not None:: 判断是否存在下一页链接。
next_page = response.urljoin(next_page): 将相对 URL 转换为绝对 URL。
yield scrapy.Request(next_page, callback=self.parse): 创建一个新的 scrapy.Request 对象,并指定回调函数为 self.parse。 这使得爬虫可以递归地爬取所有页面。
运行 Spider:
要运行这个 Spider,你需要创建一个 Scrapy 项目,并将这个代码保存为一个 Python 文件(例如 quotes_spider.py)。 然后,在项目目录下运行以下命令:
scrapy crawl quotes -o quotes.json
这个命令会运行名为 "quotes" 的爬虫,并将爬取到的数据保存到 quotes.json 文件中。
2.1.5 自定义 Request 对象
你可以通过 scrapy.Request 对象来控制请求的各个方面,例如:
url: 请求的 URL。
callback: 处理 Response 的回调函数。
method: HTTP 方法 (GET, POST, PUT, DELETE, 等)。
headers: 请求头。
body: 请求体 (用于 POST 请求)。
cookies: Cookie。
meta: 一个字典,用于在不同的回调函数之间传递数据。
dont_filter: 如果设置为 True,则 Scrapy 不会过滤这个请求。 这可以用于重复爬取同一个 URL。
errback: 处理请求错误的的回调函数。
示例:使用 POST 请求:
import scrapy class PostSpider(scrapy.Spider): name = "post_example" allowed_domains = ["example.com"] start_urls = ["http://example.com/login"] def parse(self, response): # 假设登录表单需要用户名和密码 yield scrapy.FormRequest( url="http://example.com/login", formdata={"username": "your_username", "password": "your_password"}, callback=self.after_login, ) def after_login(self, response): # 登录成功后的处理逻辑 if "Welcome" in response.text: self.log("Login successful!") # 继续爬取其他页面 yield scrapy.Request("http://example.com/profile", callback=self.parse_profile) else: self.log("Login failed!") def parse_profile(self, response): # 解析个人资料页面 pass
代码详解:
scrapy.FormRequest 是 scrapy.Request 的一个子类,专门用于处理 HTML 表单。
formdata 参数是一个字典,包含了表单字段和对应的值。
callback 参数指定了登录成功后要调用的回调函数。
2.1.6 使用 meta 传递数据
meta 属性是一个非常有用的工具,可以在不同的回调函数之间传递数据。
示例:
import scrapy class MetaSpider(scrapy.Spider): name = "meta_example" allowed_domains = ["example.com"] start_urls = ["http://example.com/item/1"] def parse(self, response): item_name = response.css("h1::text").get() yield scrapy.Request( url="http://example.com/item/1/details", callback=self.parse_details, meta={"item_name": item_name}, # 传递 item_name 到下一个回调函数 ) def parse_details(self, response): item_name = response.meta["item_name"] # 从 meta 中获取 item_name item_description = response.css("p::text").get() yield { "name": item_name, "description": item_description, }
代码详解:
在 parse 方法中,我们将 item_name 存储到 meta 字典中。
在 parse_details 方法中,我们从 response.meta 中获取 item_name。
你可以使用 errback 参数来处理请求错误。
示例:
import scrapy class ErrorSpider(scrapy.Spider): name = "error_example" allowed_domains = ["example.com"] start_urls = ["http://example.com/nonexistent_page"] def parse(self, response): self.log("Successfully fetched page: %s" % response.url) def errback_httpbin(self, failure): # log all failures self.logger.error(repr(failure)) # in case you want to do something special for some request # do if failure.request is some_request self.logger.error("Request URL: %s" % failure.request.url)
总结:
Spiders 是 Scrapy 的核心组件,理解 Spiders 的工作原理对于编写高效、可靠的爬虫至关重要。 通过定义 name, allowed_domains, start_urls,和重写 parse() 方法,你可以定制爬虫的行为,提取所需的数据,并控制爬取流程。 此外,使用 scrapy.Request 对象,meta 属性,和 errback 可以进一步控制请求的各个方面,处理错误,并在不同的回调函数之间传递数据。 掌握这些知识,你就可以构建强大的 Scrapy 爬虫,从互联网上抓取各种各样的数据。