安装

Scrapy 是一个十分强大的爬虫框架,使用 pip 来安装 scrapy 需要安装大量的依赖库,至少需要的依赖库有 Twisted,lxml,pyOpenSSL。而在不同平台环境又各不相同,所以推荐使用 anaconda 来进行安装 scrapy:

conda install scrapy

测试是否安装成功,在命令行输入 scrapy,显示出版本号即安装成功。

相关链接

官网
github
官方文档
中文文档

开始使用

整体架构

本图按顺序说明整个程序执行时候发生的顺序。

注意在调用下载器时,往往有一个下载器中间件,使下载速度提速。

官网架构图

创建项目

在开始抓取之前,必须建立一个新的项目。在命令行中输入如下代码:

scrapy startproject tutorial

这将创建一个 tutorial 目录包含以下内容:

tutorial/scrapy.cfg            # deploy configuration filetutorial/             # project's Python module, you'll import your code from here__init__.pyitems.py          # project items definition filemiddlewares.py    # project middlewares filepipelines.py      # project pipelines filesettings.py       # project settings filespiders/          # a directory where you'll later put your spiders__init__.py

第一只蜘蛛

Spiders 是我们定义的类,Scrapy 会从一个网站(或一组网站)中抓取信息。我们需要定义初始请求地址,可选的是如何跟随页面中的链接,以及如何解析下载的页面内容和提取数据。

这是我们第一只蜘蛛的代码。将其保存在名为的文件中 quotes_spider.py 下 tutorial/spiders 项目中的目录:

import scrapyclass QuotesSpider(scrapy.Spider):name = 'quotes'start_urls = ['https://quotes.toscrape.com/tag/humor/',]def parse(self, response):for quote in response.css('div.quote'):yield {'author': quote.xpath('span/small/text()').get(),'text': quote.css('span.text::text').get(),}next_page = response.css('li.next a::attr("href")').get()if next_page is not None:yield response.follow(next_page, self.parse)

如上,我们的 Spider 子类 scrapy.Spider 并定义了一些属性和方法:

  • name :标识蜘蛛。它在一个项目中必须是唯一的,即不能为不同的爬行器设置相同的名称。

  • start_requests() :必须返回请求的可迭代(你可以返回请求列表或编写生成器函数),爬行器将从该请求开始爬行。后续请求将从这些初始请求中相继生成。

  • parse() :将被调用以处理为每个请求下载的响应的方法。Response 参数是 TextResponse 它保存页面内容,并具有进一步有用的方法来处理它。

这个 parse() 方法通常解析响应,将抓取的数据提取为字典,还查找要遵循的新 URL 并创建新请求 (Request )。

如何运行我们的蜘蛛

要使蜘蛛正常工作,请转到项目的顶级目录并运行:

scrapy crawl quotes

此命令会运行我们刚刚添加的名为 quotes 的 spider ,会发送一些请求到 quotes.toscrape.com 网址。将得到类似于以下内容的输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXho5Hyh-1660531064581)(https://img.picgo.net/2022/08/10/2.png)]

在后台发生了什么?

Scrapy 调度 Spider 的方法 scrapy.Request 返回的对象。start_requests 在收到每个响应的响应后,它会实例化 Response 对象并调用与请求关联的回调方法(在本例中为 parse 方法),将响应作为参数传递。

启动 start_requests 的快捷方式

无需实现 start_requests() 方法,该方法生成 scrapy.Request 对象,我们可以只定义一个具有 url 列表的 start_urls 的类属性。这些 url 列表会被 start_requests() 方法作为默认参数来使用:

import scrapy

class QuotesSpider(scrapy.Spider):name = "quotes"start_urls = ['http://quotes.toscrape.com/page/1/','http://quotes.toscrape.com/page/2/',]def parse(self, response):page = response.url.split("/")[-2]filename = f'quotes-{page}.html'with open(filename, 'wb') as f:f.write(response.body)

调用 parse() 方法来处理这些 URL 的每个请求,即使我们还没有显式地告诉 Scrapy 这样做。发生这种情况是因为 parse() 是 Scrapy 的默认回调方法。

提取数据

安装 pyquery

这里不使用 scrapy 自带的 css 选择器和 XPath 表达式,使用和 jquery 语法一样的 pyquery,首先使用 anaconda 安装 pyquery:

conda install pyquery

提取名言和作者

我们待提取的 html 结构如下:

<div class="quote"><span class="text">“The world as we have created it is a process of our thinking. It cannot be changed withoutchanging our thinking.”</span><span>by <small class="author">Albert Einstein</small><a href="/author/Albert-Einstein">(about)</a></span><div class="tags">Tags:<a class="tag" href="/tag/change/page/1/">change</a><a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a><a class="tag" href="/tag/thinking/page/1/">thinking</a><a class="tag" href="/tag/world/page/1/">world</a></div>
</div>

使用 pyquery 把名言、作者和标签提取出来:

from pyquery import PyQuery as pqdoc = pq(response.body)
for quote in doc('.quote').items():text = quote('.text').text()author = quote('.author').text()tags = quote('.tags .tag').text().split(' ')print(dict(text=text, author=author, tags=tags))
# 输出:
{'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”', 'author': 'Albert Einstein', 'tags': ['change', 'deep-thoughts', 'thinking', 'world']}
{'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”', 'author': 'J.K. Rowling', 'tags': ['abilities', 'choices']}

在 Spider 中提取数据

目前为止,Spider 并没有提取任何数据,只是将整个 HTML 页面保存到本地文件中。接下来把上面的提取逻辑集成到 Spider 中。

Scrapy 的蜘蛛通常会生成许多字典,其中包含从页面中提取的数据。所以在回调中使用 Python 关键字 yield,如下所示:

import scrapy
from pyquery import PyQuery as pqclass QuotesSpider(scrapy.Spider):name = "quotes"start_urls = ['https://quotes.toscrape.com/page/1/','https://quotes.toscrape.com/page/2/',]def parse(self, response):for quote in quotes.items():yield {'text': quote('.text').text(),'author': quote('.author').text(),'tags': quote('.tags .tag').text().split(' '),}

运行上面的蜘蛛,会输出如下提取的数据和日志::

存储抓取的数据

存储抓取数据的最简单方法是使用 Feed exports ,使用以下命令:

scrapy crawl quotes -O quotes.json
# 或者保存为csv
scrapy crawl quotes -O quotes.csv
# 解决中文乱码
scrapy crawl quotes -O quotes.json -s FEED_EXPORT_ENCODING=utf-8

这将生成一个quotes.json包含所有抓取项目的文件,并以JSON序列化。若出现中文乱码,则添加-s FEED_EXPORT_ENCODING=utf-8即可。

命令行-O(大写的O)开关覆盖任何现有文件;而是使用-o(小写的o)将新内容附加到任何现有文件。但是,附加到 JSON 文件会使文件内容无效 JSON。附加到文件时,请考虑使用不同的序列化格式,例如JSON Lines:

scrapy crawl quotes -o quotes.jl

JSON Lines格式很有用,因为它类似于流,你可以轻松地将新记录附加到它。当你运行两次时,它没有同样的 JSON 问题。此外,由于每条记录都是单独的一行,因此你可以处理大文件而无需将所有内容都放入内存中,有JQ之类的工具可以在命令行中帮助执行此操作。

在小型项目(如本教程中的项目)中,这应该足够了。但是,如果你想对抓取的项目执行更复杂的操作,你可以编写一个Item Pipeline。创建项目时,已为你设置了 Item Pipelines 的占位符文件,在 tutorial/pipelines.py. 如果你只想存储抓取的项目,则不需要实现任何项目管道。

跟踪链接

如果不光是想从 https://quotes.toscrape.com 的前两页中抓取,而是想从网站的所有页面中抓取内容,那么就需要跟踪链接。

首先是提取我们关注页面的链接。检查页面,可以看到有一个链接上面带有前往下一页的标记:

<ul class="pager"><li class="next"><a href="/page/2/">Next <span aria-hidden="true">&rarr;</span></a></li>
</ul>

把这个标记提取出来:

next_page = doc('.pager .next a').attr('href')

然后把Spider修改为递归地跟随到下一页的链接,然后从中提取数据:

import scrapy
from pyquery import PyQuery as pqclass QuotesSpider(scrapy.Spider):name = "quotes"start_urls = ['https://quotes.toscrape.com/page/1/',]def parse(self, response):for quote in quotes.items():yield {'text': quote('.text').text(),'author': quote('.author').text(),'tags': quote('.tags .tag').text().split(' '),}next_page = doc('.pager .next a').attr('href')if next_page is not None:next_page = response.urljoin(next_page)yield scrapy.Request(next_page, callback=self.parse)

在提取数据后,该parse()方法查找到下一页的链接,使用该 urljoin()方法构建一个完整的绝对 URL(因为链接可以是相对的)并产生一个到下一页的新请求,将自己注册为回调来处理下一页的数据提取并保持爬取通过所有页面。

你在这里看到的是 Scrapy 的以下链接机制:当你在回调方法中产生一个请求时,Scrapy 将安排发送该请求并注册一个回调方法以在该请求完成时执行。

使用它,你可以构建复杂的爬虫,根据你定义的规则跟踪链接,并根据它访问的页面提取不同类型的数据。

在我们的示例中,它创建了一种循环,跟踪所有指向下一页的链接,直到找不到一个——这对于爬取博客、论坛和其他带有分页的站点非常方便。

创建请求的快捷方式

作为创建请求对象的快捷方式,可以使用 response.follow:

import scrapy
from pyquery import PyQuery as pqclass QuotesSpider(scrapy.Spider):name = "quotes"start_urls = ['https://quotes.toscrape.com/page/1/',]def parse(self, response):for quote in quotes.items():yield {'text': quote('.text').text(),'author': quote('.author').text(),'tags': quote('.tags .tag').text().split(' '),}next_page = doc('.pager .next a').attr('href')if next_page is not None:yield response.follow(next_page, callback=self.parse)

与 scrapy.Request 不同,response.follow直接支持相对 URL - 无需调用 urljoin。注意response.follow只返回一个 Request 实例;你仍然需要提交这个请求。

你还可以将选择器传递给response.follow而不是字符串;这个选择器应该提取必要的属性:

for href in response.css('ul.pager a::attr(href)'):yield response.follow(href, callback=self.parse)

对于元素有一个快捷方式:response.follow自动使用它们的 href 属性。所以代码可以进一步缩短:

for a in response.css('ul.pager a'):yield response.follow(a, callback=self.parse)

要从一个可迭代对象创建多个请求,你可以 response.follow_all改用:

anchors = response.css('ul.pager a')
yield from response.follow_all(anchors, callback=self.parse)

或者,进一步缩短它:

yield from response.follow_all(css='ul.pager a', callback=self.parse)

更多示例和模式

这是另一个蜘蛛,它说明了回调和以下链接,这次是为了抓取作者信息:

import scrapy

class AuthorSpider(scrapy.Spider):name = 'author'start_urls = ['https://quotes.toscrape.com/']def parse(self, response):author_page_links = response.css('.author + a')yield from response.follow_all(author_page_links, self.parse_author)pagination_links = response.css('.pager .next a')yield from response.follow_all(pagination_links, self.parse)def parse_author(self, response):doc = pq(response.body)yield {'name': doc('.author-title').text(),'birthdate': doc('.author-born-date').text(),'address': doc('.author-born-location').text(),'bio': doc('.author-description').text(),}

这个蜘蛛会从主页开始,它会跟随到所有作者页面的链接,在每个作者页面调用回调函数parse_author,来抓取作者信息。

使用蜘蛛参数

可以在运行Spider时使用 -a 选项提供命令行参数:

scrapy crawl quotes -O quotes-humor.json -a tag=humor

这些参数被传递给 Spider 的__init__方法并默认成为 Spider 的属性。

示例中,为tag参数提供的值将通过self.tag传递进去。可以使用它让蜘蛛仅获取带有特定标签的信息,并根据参数构建 URL:

import scrapyclass QuotesSpider(scrapy.Spider):name = "quotes"def start_requests(self):url = 'https://quotes.toscrape.com/'tag = getattr(self, 'tag', None)if tag is not None:url = url + 'tag/' + tagyield scrapy.Request(url, self.parse)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(),}next_page = response.css('li.next a::attr(href)').get()if next_page is not None:yield response.follow(next_page, self.parse)

如果将tag=humor参数传递给这个蜘蛛,那么它只会访问来自humor标签的 URL,例如 https://quotes.toscrape.com/tag/humor

结尾

本教程只介绍 Scrapy 的基础知识,还有很多其他特性请参考官方文档的教程。

python爬虫利器之scrapy的基本教程相关推荐

  1. Python 爬虫利器之 Pyppeteer 的用法

    如果大家对 Python 爬虫有所了解的话,想必你应该听说过 Selenium 这个库,这实际上是一个自动化测试工具,现在已经被广泛用于网络爬虫中来应对 JavaScript 渲染的页面的抓取. 很多 ...

  2. Python爬虫利器之PhantomJS的用法

    前言 大家有没有发现之前我们写的爬虫都有一个共性,就是只能爬取单纯的html代码,如果页面是JS渲染的该怎么办呢?如果我们单纯去分析一个个后台的请求,手动去摸索JS渲染的到的一些结果,那简直没天理了. ...

  3. Python爬虫利器之Beautiful Soup的全世界最强用法 五百行文章!

    0. 前言 爬虫是一个非常有意思的东西,比如自己做的一个网页上面什么数据都没有就可以爬虫别人的 然后进行去重 数据分析等等 在这里因为爬虫涉及到的方面非常多 1. Beautiful Soup的简介 ...

  4. Python爬虫利器之Beautiful Soup的用法,以及实例!

    可以利用 pip 来安装: pip install beautifulsoup4 源代码: import requests r = requests.get("https://python1 ...

  5. 芝麻HTTP: Python爬虫利器之PyQuery的用法

    2019独角兽企业重金招聘Python工程师标准>>> 前言 你是否觉得 XPath 的用法多少有点晦涩难记呢? 你是否觉得 BeautifulSoup 的语法多少有些悭吝难懂呢? ...

  6. Python爬虫5.3 — scrapy框架spider[Request和Response]模块的使用

    Python爬虫5.3 - scrapy框架spider[Request和Response]模块的使用 综述 Request对象 scrapy.Request()函数讲解: Response对象 发送 ...

  7. python利器的使用-图文详解python开发利器之ulipad的使用实践

    Ulipad是一个国人limodou编写的专业Python编辑器,它基于wxpython开发的GUI(图形化界面).下面这篇文章主要介绍了python开发利器之ulipad的使用实践,文中介绍的非常详 ...

  8. python利器怎么编程-Python任务调度利器之APScheduler详解

    任务调度应用场景 所谓的任务调度是指安排任务的执行计划,即何时执行,怎么执行等.在现实项目中经常出现它们的身影:特别是数据类项目,比如实时统计每5分钟网站的访问量,就需要每5分钟定时从日志数据分析访问 ...

  9. Python爬虫1.4 — requests高级用法教程

    Python爬虫1.4 - requests高级用法教程 综述 设置请求头(headers) 设置代理服务器IP(proxy) Cookies(Session) requests.Session() ...

最新文章

  1. Laravel 上使用 phpexcel的两种方式
  2. qt 拖拽 修改大小
  3. r语言 断轴 画图_R语言作图——Density plot
  4. 开源组件DocX导出Word
  5. Android蓝牙A2DP连接实现
  6. 12. 星际争霸之php设计模式--模板模式
  7. 软件工程第三次作业(最大子段和)
  8. SwiftUI资源列表
  9. 第八章《Unity游戏优化》内存管理
  10. pyqt5在图元上画图_PyQt5的PyQtGraph实践系列1:添加图形到PyQt5布局
  11. 基于单片机的“彩灯控制器”的程序设计与调试
  12. php三极管导通条件,三极管的导通条件 - 三级管饱和导通的条件是什么?
  13. 优秀程序员的博客有哪些?
  14. 假如王思聪是个程序员...
  15. Vue3动态引入图片
  16. 视频会议,远程协助平台接入亮亮视野AR眼镜,UVC摄像头方案
  17. 【树】二叉树的两种非递归遍历方法
  18. HDU4801 转魔方、DFS模拟
  19. C++-STL--吐泡泡
  20. 脑科学读物阅读笔记系列 - 拉马钱德兰《脑中魅影》- 2. 幻肢痛

热门文章

  1. 解决Hander dispatch failed;nested exception is java .lang. AbstractMethodError:Method com/mchange/v2/c
  2. IntelliJ IDEA类和方法注释模板配置
  3. 打破双亲委派的几种方式
  4. 【PCIE】PCIE TLP包解析
  5. Win7电脑usb接口没有反应解决方法
  6. PY_matplotlib
  7. requestAnimationFrame运动框架实现播放中连续变速动画效果
  8. win7怎么装python_如何在win7上面安装python的包
  9. 树上统计——基于树的搜索
  10. 技术栈中的爱马仕?Facebook发布全新JavaScript引擎:Hermes