## Scrapy 教程
**last update: 2022-06-06 10:23:11**
----
[TOC=3,8]
----
[Scrapy | A Fast and Powerful Scraping and Web Crawling Framework](https://scrapy.org/)
https://github.com/scrapy/scrapy
https://github.com/scrapy-plugins
[Scrapy 教程 — Scrapy 2.5.0 文档](https://www.osgeo.cn/scrapy/intro/tutorial.html)
----
### 准备虚拟环境 venv
> 为一个应用创建一套“隔离”的 Python 运行环境,使用不同的虚拟环境可以解决不同应用的依赖冲突问题。
```shell
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
source venv/bin/activate
```
[12. 虚拟环境和包 — Python 3.11.3 文档](https://docs.python.org/zh-cn/3/tutorial/venv.html#tut-venv)
[virtualenv Lives!](https://hynek.me/articles/virtualenv-lives/)
**windows 环境**:
以 **管理员身份** 运行 Windows PowerShell :
```shell
PS D:\web\tutorial-env> set-executionpolicy remotesigned
PS D:\web\tutorial-env> get-executionpolicy
RemoteSigned
PS D:\web\tutorial-env> .\Scripts\activate
```
设置 PyCharm 终端自动激活虚拟环境:工具 > 终端 :勾选 【激活 virtualenv】
----
### conda
venv 虚拟环境能解决不同项目间的包版本冲突问题,但是如果我们需要不同的 python 版本呢? conda 可以帮助我们方便的安装管理不同版本的 python 和 pip。
下载 https://www.anaconda.com/products/individual (国内镜像 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/?C=M&O=D)
[conda 管理多版本python-蒲公英云](https://dandelioncloud.cn/article/details/1526009310379524098)
[Anaconda安装使用教程解决多Python版本问题_anaconda安装多个python版本_print('小白码')的博客-CSDN博客](https://blog.csdn.net/qq_50048105/article/details/113859376)
[Python3 的安装 | 静觅](https://cuiqingcai.com/30035.html)
[相关环境安装](https://setup.scrape.center/)
----
### DecryptLogin 安装与使用
```shell
pip3 install DecryptLogin
```
todo ...
----
### Scrapy 安装
```shell
pip3 install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple
scrapy -V
Scrapy 2.8.0 - no active project
```
see: [Scrapy 的安装 | 静觅](https://setup.scrape.center/scrapy)
----
### Scrapy 使用
https://github.com/orgs/Python3WebSpider/repositories?q=scrapy&type=all&language=&sort=
https://github.com/orgs/Python3WebSpider/repositories?q=Pyppeteer+&type=all&language=&sort=
[【2022 年】崔庆才 Python3 网络爬虫学习教程 | 静觅](https://cuiqingcai.com/17777.html)
#### 创建项目
```shell
scrapy startproject tutorial
```
~~~
tutorial/
scrapy.cfg # deploy configuration file
tutorial/ # project's Python module, you'll import your code from here
__init__.py
items.py # project items definition file
middlewares.py # project middlewares file
pipelines.py # project pipelines file
settings.py # project settings file
spiders/ # a directory where you'll later put your spiders
__init__.py
~~~
----
#### 创建蜘蛛
~~~shell
cd tutorial
scrapy genspider quotes quotes.toscrape.com
~~~
上面的命令会生成如下文件 tutorial/tutorial/spiders/quotes.py
~~~python
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["https://quotes.toscrape.com"]
def parse(self, response):
pass
~~~
----
#### 使用 item
item 可以帮我们规范数据字段
tutorial/tutorial/items.py
```python
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class QuoteItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
text = scrapy.Field()
author = scrapy.Field()
tags = scrapy.Field()
```
#### 运行蜘蛛
现在修改下我们的蜘蛛 tutorial/tutorial/spiders/quotes.py :
```python
import scrapy
from ..items import QuoteItem
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["https://quotes.toscrape.com"]
def parse(self, response):
for quote in response.css('div.quote'):
item = QuoteItem()
item['text'] = quote.css('span.text::text').get()
item['author'] = quote.css('small.author::text').get()
item['tags'] = quote.css('div.tags a.tag::text').getall()
yield item
```
运行:
```shell
scrapy crawl quotes -O quotes.json
```
结果如下 tutorial/quotes.json :
~~~json
[
{"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"]},
{"text": "“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”", "author": "Albert Einstein", "tags": ["inspirational", "life", "live", "miracle", "miracles"]},
{"text": "“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”", "author": "Jane Austen", "tags": ["aliteracy", "books", "classic", "humor"]},
{"text": "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”", "author": "Marilyn Monroe", "tags": ["be-yourself", "inspirational"]},
{"text": "“Try not to become a man of success. Rather become a man of value.”", "author": "Albert Einstein", "tags": ["adulthood", "success", "value"]},
{"text": "“It is better to be hated for what you are than to be loved for what you are not.”", "author": "André Gide", "tags": ["life", "love"]},
{"text": "“I have not failed. I've just found 10,000 ways that won't work.”", "author": "Thomas A. Edison", "tags": ["edison", "failure", "inspirational", "paraphrased"]},
{"text": "“A woman is like a tea bag; you never know how strong it is until it's in hot water.”", "author": "Eleanor Roosevelt", "tags": ["misattributed-eleanor-roosevelt"]},
{"text": "“A day without sunshine is like, you know, night.”", "author": "Steve Martin", "tags": ["humor", "obvious", "simile"]}
]
~~~
-----
#### 使用 itemloader
[scrapy 之 itemloader - 知乎](https://zhuanlan.zhihu.com/p/59905612/)
新建文件 tutorial/tutorial/itemloader.py
```python
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, Join, Compose
class BaseLoader(ItemLoader):
pass
class quoteLoader(BaseLoader):
pass
```
修改文件 tutorial/tutorial/spiders/quotes.py
```python
import scrapy
from ..items import QuoteItem
from ..itemloader import QuoteLoader
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["https://quotes.toscrape.com"]
# def parse(self, response):
# for quote in response.css('div.quote'):
# item = QuoteItem()
# item['text'] = quote.css('span.text::text').get()
# item['author'] = quote.css('small.author::text').get()
# item['tags'] = quote.css('div.tags a.tag::text').getall()
# yield item
def parse(self, response):
quotes = response.css('.quote')
for quote in quotes:
loader = QuoteLoader(item=QuoteItem(), selector=quote)
loader.add_css('text', '.text::text')
loader.add_css('author', '.author::text')
loader.add_css('tags', '.tag::text')
yield loader.load_item()
```
Item Loader 在每个字段中都包含了一个 输入处理器 和 一个输出处理器。
再次执行蜘蛛,发现 text 成了列表,接下来 需要利用 Item Loader 的 输入/输出处理器。
修改 tutorial/tutorial/itemloader.py
```python
class BaseLoader(ItemLoader):
default_output_processor = TakeFirst()
```
此时 text 就和之前一样了。下面将介绍一些内置的的处理器。
**Identity**
Identity 是最简单的 Processor,不进行任何处理,直接返回原来的数据
**TakeFirst**
akeFirst 返回列表的第一个非空值,类似 extract_first() 的功能,常用作 Output Processor
```python
processor = TakeFirst()
print(processor(['', 1, 2, 3]))
# 1
```
**Join**
Join 方法相当于字符串的 join() 方法,可以把列表拼合成字符串,字符串默认使用空格分隔
```python
processor = Join(',')
print(processor(['one', 'two', 'three']))
# one,two,three
```
**Compose**
Compose 是用给定的多个函数的组合而构造的 Processor,每个 输入值 被传递到第一个函数,
其输出再传递到第二个函数,依次类推,直到最后一个函数返回整个处理器的输出
```python
processor = Compose(str.upper, lambda s: s.strip())
print(processor(' hello world'))
# HELLO WORLD
```
**MapCompose**
与 Compose 类似,MapCompose 可以迭代处理一个列表输入值
```python
processor = MapCompose(str.upper, lambda s: s.strip())
print(processor(['Hello', 'World', 'Python']))
# ['HELLO', 'WORLD', 'PYTHON']
# 被处理的内容是一个可迭代对象,MapCompose 会将该对象遍历然后依次处理。
```
**SelectJmes**
SelectJmes 可以查询 JSON ,传入 Key ,返回查询所得的 Value 。不过需要先安装 `pip install jmespath` 库才可以使用它:
```python
from scrapy.loader.processors import SelectJmes
processor = SelectJmes('foo')
print(processor({'foo': 'bar'}))
# bar
```
**有两种方式使用处理器:**
1. `xxx_in` 为声明处理 `xxx` 字段的 输入处理器,`xxx_out` 为声明处理 `xxx` 字段的 输出处理器
2. `default_input_processor` 和 `default_output_processor` 属性声明默认的
输入/输出 处理器。
修改文件 tutorial/tutorial/itemloader.py
```python
from scrapy.loader import ItemLoader
from scrapy.loader.processors import Identity, TakeFirst, Join, Compose
class BaseLoader(ItemLoader):
default_output_processor = TakeFirst()
class quoteLoader(BaseLoader):
tags_out = Identity()
```
发现 只有 tags 是取多个,其它都是 取一个。
----
### Scrapy 特性
**过滤重复请求**
默认情况下,Scrapy 过滤掉对已经访问过的URL的重复请求,避免了由于编程错误而太多地访问服务器的问题。这可以通过设置进行配置 [DUPEFILTER_CLASS](https://www.osgeo.cn/scrapy/topics/settings.html#std-setting-DUPEFILTER_CLASS)
----
### 常见问题
**如何爬取更多链接?**
虽然爬虫是**从一个入口链接开始**的,但不要因此就认为它只能完成一次性的简单爬取任务,我们可在 `parse()` 中根据情况使用 `yield scrapy.Request(next_page, callback=self.parse)` 、`response.follow(next_page, self.parse)`、`yield from response.follow_all(anchors, callback=self.parse)` **继续生成其他请求,以满足爬取所有其他页面。**
----
**如何处理和保存爬取到的数据?**
```shell
scrapy runspider quotes_spider.py -o quotes.jl
cd project
scrapy crawl quotes -O quotes.json
scrapy crawl quotes -o quotes.jl
```
----
**如何使用代理?**
----
**如何分布式大规模爬取?**
----
**如何处理登录?**
[Scrapy详解之中间件(Middleware)](https://mp.weixin.qq.com/s?__biz=MzAxMjUyNDQ5OA==&mid=2653557181&idx=1&sn=c62810ab78f40336cb721212ab83f7bd&chksm=806e3f00b719b616286ec1a07f9a5b9eeaba105781f93491685fbe732c60db0118852cfeeec8&scene=27)
[scrapy中添加cookie踩坑记录_51CTO博客_scrapy cookie](https://blog.51cto.com/u_11949039/2859241)
[scrapy 基础组件专题(十二):scrapy 模拟登录](https://www.bbsmax.com/A/l1dy7YAxJe/)
> 在 `settings.py` 中设置 [`COOKIES_DEBUG=True`](https://www.osgeo.cn/scrapy/topics/downloader-middleware.html#std-setting-COOKIES_DEBUG) 能够在终端看到 cookie 的传递过程
[设置 — Scrapy 2.5.0 文档](https://www.osgeo.cn/scrapy/topics/settings.html#topics-settings-ref)
----
**如何处理验证码?**
----
**如何处理滑块等防爬人机验证?**
----
**如何处理加密防爬?**
----
**如何使用无头浏览器?**
----
**如何在 scrapy 中对接 selenium**
----
**如何管理、控制爬虫?**
[Scrapyd 1.4.1 documentation](https://scrapyd.readthedocs.io/en/latest/)
----
- 开始
- 公益
- 更好的使用看云
- 推荐书单
- 优秀资源整理
- 技术文章写作规范
- SublimeText - 编码利器
- PSR-0/PSR-4命名标准
- php的多进程实验分析
- 高级PHP
- 进程
- 信号
- 事件
- IO模型
- 同步、异步
- socket
- Swoole
- PHP扩展
- Composer
- easyswoole
- php多线程
- 守护程序
- 文件锁
- s-socket
- aphp
- 队列&并发
- 队列
- 讲个故事
- 如何最大效率的问题
- 访问式的web服务(一)
- 访问式的web服务(二)
- 请求
- 浏览器访问阻塞问题
- Swoole
- 你必须理解的计算机核心概念 - 码农翻身
- CPU阿甘 - 码农翻身
- 异步通知,那我要怎么通知你啊?
- 实时操作系统
- 深入实时 Linux
- Redis 实现队列
- redis与队列
- 定时-时钟-阻塞
- 计算机的生命
- 多进程/多线程
- 进程通信
- 拜占庭将军问题深入探讨
- JAVA CAS原理深度分析
- 队列的思考
- 走进并发的世界
- 锁
- 事务笔记
- 并发问题带来的后果
- 为什么说乐观锁是安全的
- 内存锁与内存事务 - 刘小兵2014
- 加锁还是不加锁,这是一个问题 - 码农翻身
- 编程世界的那把锁 - 码农翻身
- 如何保证万无一失
- 传统事务与柔性事务
- 大白话搞懂什么是同步/异步/阻塞/非阻塞
- redis实现锁
- 浅谈mysql事务
- PHP异常
- php错误
- 文件加载
- 路由与伪静态
- URL模式之分析
- 字符串处理
- 正则表达式
- 数组合并与+
- 文件上传
- 常用验证与过滤
- 记录
- 趣图
- foreach需要注意的问题
- Discuz!笔记
- 程序设计思维
- 抽象与具体
- 配置
- 关于如何学习的思考
- 编程思维
- 谈编程
- 如何安全的修改对象
- 临时
- 临时笔记
- 透过问题看本质
- 程序后门
- 边界检查
- session
- 安全
- 王垠
- 第三方数据接口
- 验证码问题
- 还是少不了虚拟机
- 程序员如何谈恋爱
- 程序员为什么要一直改BUG,为什么不能一次性把代码写好?
- 碎碎念
- 算法
- 实用代码
- 相对私密与绝对私密
- 学习目标
- 随记
- 编程小知识
- foo
- 落盘
- URL编码的思考
- 字符编码
- Elasticsearch
- TCP-IP协议
- 碎碎念2
- Grafana
- EFK、ELK
- RPC
- 依赖注入
- 开发笔记
- 经纬度格式转换
- php时区问题
- 解决本地开发时调用远程AIP跨域问题
- 后期静态绑定
- 谈tp的跳转提示页面
- 无限分类问题
- 生成微缩图
- MVC名词
- MVC架构
- 也许模块不是唯一的答案
- 哈希算法
- 开发后台
- 软件设计架构
- mysql表字段设计
- 上传表如何设计
- 二开心得
- awesomes-tables
- 安全的代码部署
- 微信开发笔记
- 账户授权相关
- 小程序获取是否关注其公众号
- 支付相关
- 提交订单
- 微信支付笔记
- 支付接口笔记
- 支付中心开发
- 下单与支付
- 支付流程设计
- 订单与支付设计
- 敏感操作验证
- 排序设计
- 代码的运行环境
- 搜索关键字的显示处理
- 接口异步更新ip信息
- 图片处理
- 项目搭建
- 阅读文档的新方式
- mysql_insert_id并发问题思考
- 行锁注意事项
- 细节注意
- 如何处理用户的输入
- 不可见的字符
- 抽奖
- 时间处理
- 应用开发实战
- python 学习记录
- Scrapy 教程
- Playwright 教程
- stealth.min.js
- Selenium 教程
- requests 教程
- pyautogui 教程
- Flask 教程
- PyInstaller 教程
- 蜘蛛
- python 文档相似度验证
- thinkphp5.0数据库与模型的研究
- workerman进程管理
- workerman网络分析
- java学习记录
- docker
- 笔记
- kubernetes
- Kubernetes
- PaddlePaddle
- composer
- oneinstack
- 人工智能 AI
- 京东
- pc_detailpage_wareBusiness
- doc
- 电商网站设计
- iwebshop
- 商品规格分析
- 商品属性分析
- tpshop
- 商品规格分析
- 商品属性分析
- 电商表设计
- 设计记录
- 优惠券
- 生成唯一订单号
- 购物车技术
- 分类与类型
- 微信登录与绑定
- 京东到家库存系统架构设计
- crmeb
- 命名规范
- Nginx https配置
- 关于人工智能
- 从人的思考方式到二叉树
- 架构
- 今日有感
- 文章保存
- 安全背后: 浏览器是如何校验证书的
- 避不开的分布式事务
- devops自动化运维、部署、测试的最后一公里 —— ApiFox 云时代的接口管理工具
- 找到自己今生要做的事
- 自动化生活
- 开源与浆果
- Apifox: API 接口自动化测试指南