## 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 接口自動化測試指南