一、Scrapy
1、概述
Scrapy是用python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架,使用Twisted高效异步网络框架来处理网络通信。它可以应用在广泛领域:Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。
2、架构
- Scrapy Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。此组件相当于爬虫的“大脑”,是 整个爬虫的调度中心。
- Scheduler(调度器):它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。初始爬取url和后续在页面里爬到的待爬取url放入调度器中,等待被爬取。调度器会自动去掉重复的url。
- Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。
- Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。
- Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
- Downloader Middlewares(下载中间件):一个可以自定义扩展下载功能的组件。
- 是在引擎和下载器之间的特定钩子(specific hook),处理它们之间的请求(request)和响应(response)。提供了一个简单的机制,通过插入自定义代码来扩展Scrapy功能。通过设置DownloaderMiddlewares来实现爬虫自动更换user-agent,IP等。
- Spider Middlewares(Spider中间件):一个可以自定扩展和操作引擎和Spider中间通信的功能组件。是在引擎和Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items或requests)。提供了同样简单机制,通过插入自定义代码来扩展Scrapy功能。
Scrapy数据流:
- ScrapyEngine打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个(批)要爬取的url(s);
- ScrapyEngine向调度器请求第一个要爬取的url,并加入到Schedule作为请求以备调度;
- ScrapyEngine向调度器请求下一个要爬取的url;
- Schedule返回下一个要爬取的url给ScrapyEngine,ScrapyEngine通过DownloaderMiddlewares将url转发给Downloader;
- 页面下载完毕,Downloader生成一个页面的Response,通过DownloaderMiddlewares发送给ScrapyEngine;
- ScrapyEngine从Downloader中接收到Response,通过SpiderMiddlewares发送给Spider处理;
- Spider处理Response并返回提取到的Item以及新的Request给ScrapyEngine;
- ScrapyEngine将Spider返回的Item交给ItemPipeline,将Spider返回的Request交给Schedule进行从第二步开始的重复操作,直到调度器中没有待处理的Request,ScrapyEngine关闭。
3、语法
-
安装scrapy:pip install **
Z:\Program Files\Python\Python312\Scripts> pip install scrapy -i https://pypi.douban.com/simple
-
创建项目:scrapy startproject **
Z:\pwork> scrapy startproject project
-
创建爬虫文件:scrapy genspider 爬虫文件名称 域名 (爬虫文件名称不能跟项目名称一致)
Z:\pwork> cd project\project\spiders Z:\pwork\project\project\spiders> scrapy genspider baidu www.baidu.com
-
运行爬虫代码:scrapy crawl **
Z:\pwork\project\project\spiders> scrapy crawl baidu
注释掉项目中setting.py文件中
ROBOTSTXT_OBEY = True
(爬虫协议)
4、项目结构说明
项目名称
项目名称
spiders ===> 存储爬虫文件
init
自定义爬虫文件 ===> 核心功能文件
init
items ===> 定义数据结构
middleware ===> 中间件(代理)
pipelines ===> 管道,用来处理下载的数据
settings ===> 配置文件(robots协议、ua定义等)
5、settings.py
# Scrapy settings for scrapy_project project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = "scrapy_project"
SPIDER_MODULES = ["scrapy_project.spiders"]
NEWSPIDER_MODULE = "scrapy_project.spiders"
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = "scrapy_project (+http://www.yourdomain.com)"
# Obey robots.txt rules
# 注掉君子协议
# ROBOTSTXT_OBEY = True
# 日志等级修改
LOG_LEVEL = 'WARNING'
LOG_FILE = 'logfile.log'
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False
# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False
# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
# "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
# "Accept-Language": "en",
#}
# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# "scrapy_project.middlewares.ScrapyProjectSpiderMiddleware": 543,
#}
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# "scrapy_project.middlewares.ScrapyProjectDownloaderMiddleware": 543,
#}
# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# "scrapy.extensions.telnet.TelnetConsole": None,
#}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# 开启管道处理
ITEM_PIPELINES = {
# 管道优先级范围:1-1000,值越小优先级越高
"scrapy_project.pipelines.ScrapyProjectPipeline": 300,
}
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = "httpcache"
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"
# Set settings whose default value is deprecated to a future-proof value
REQUEST_FINGERPRINTER_IMPLEMENTATION = "2.7"
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"
6、Scrapy Selector
Selector有四个基本的方法:
- xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
- extract(): 序列化该节点为Unicode字符串并返回list
- css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同
BeautifulSoup4 - re(): 根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
7、Scrapy shell
scrapy终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码。其本意是用来测试提取数据的代码,不过您可以将其作为正常的python终端,在上面测任何的python代码。该终端是用来测试Xpath或css表达式,查看他们的工作方式及从爬取的网页中提取的数据。
如果安装了 IPython ,Scrapy终端将使用 IPython (替代标准Python终端)。 IPython 终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。
Z:\Program Files\Python\Python312\Scripts> pip install scrapy -i https://pypi.douban.com/simple
Z:\pwork>scrapy shell www.baidu.com
...
2024-02-20 13:56:29 [asyncio] DEBUG: Using selector: SelectSelector
In [1]:
Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如 Response 对象,以及 Selector 对象 (对HTML及XML内容)。
- 当shell载入后,将得到一个包含response数据的本地 response 变量,输入 response.body 将输出response的包体,输出 response.headers 可以看到response的包头
- 输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象,此时可以通过使用 response.selector.xpath() 或 response.selector.css() 来对 response 进行查询
- Scrapy也提供了一些快捷方式,例如 response.xpath() 或 response.css() 同样可以生效
8、文件模板
使用 scrapy genspider
生成爬虫文件时,可以使用-t
参数指定生成文件的基础模板,默认使用basic模板。
basic模板文件相对路径:Python\Python312\Lib\site-packages\scrapy\templates\spiders\basic.tmpl
basic模板文件路径查询步骤:在pycharm中爬虫文件中导入scrapy包:import scrapy
,使用 ctrl + 鼠标左键
进入scrapy源码,basic文件位于其同级目录下templates\spiders文件夹中。
可直接复制一份为mytemplate.tmpl,在其中添加自己的处理逻辑,之后通过命令 scrapy genspider -t mytemplate test test.com
创建爬虫文件
可使用命令 scrapy genspider --list
查询模板文件列表
9、运行方式
在终端可通过执行命令scrapy crawl example
来运行对应爬虫文件,在pycharm中可通过__main__
方法执行
在项目的根目录下(scrapy.cf同级目录下,settings.py的上一层目录下)添加文件main.py(可以是其他名称),main.py文件代码如下:
from scrapy.cmdline import execute
import os
import sys
if __name__ == '__main__':
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# example即为爬虫文件名称
execute(['scrapy', 'crawl', 'example'])
10、示例
爬取当当网十页数据
-
spilder example.py
import scrapy from scrapy_project.items import ScrapyProjectItem class BaiduSpider(scrapy.Spider): name = "example" allowed_domains = ["category.dangdang.com"] start_urls = ["https://category.dangdang.com/cid4002145.html"] base_url = "https://category.dangdang.com/pg" page = 1 def parse(self, response, **kwargs): targets = response.xpath('//ul[@id="component_47"]/li') for target in targets: src = target.xpath('.//img/@data-original').extract_first() if not src: src = target.xpath('.//img/@src').extract_first() name = target.xpath('.//img/@alt').extract_first() price = target.xpath('.//p[@class="price"]').extract_first() item = ScrapyProjectItem(src=src, name=name, price=price) yield item if self.page < 10: self.page = self.page + 1 url = self.base_url + str(self.page) + '-cid4002145.html' yield scrapy.Request(url=url, callback=self.parse)
-
items.py
import scrapy class ScrapyProjectItem(scrapy.Item): src = scrapy.Field() name = scrapy.Field() price = scrapy.Field()
-
pipeline.py
import os import re import urllib.request # 数据保存管道 class SaveDataPipeline: def __init__(self): self.fp = None def open_spider(self, spider): path = os.getcwd() + '\\scrapy_project\\spiders\\result.json' self.fp = open(path, 'a', encoding='utf8') def process_item(self, item, spider): self.fp.write(str(item)) return item def close_spider(self, spider): self.fp.close() # 图片下载管道 class DownloadImagesPipeline: def __init__(self): self.path = None def open_spider(self, spider): # picture文件夹需手动创建 self.path = os.path.join(os.getcwd(), 'scrapy_project', 'spiders', 'picture') def process_item(self, item, spider): url = 'http:' + item.get('src') filename = os.path.join(self.path, re.sub(r'[\\/:*?"<>|]', '', item.get('name'))) + '.jpg' urllib.request.urlretrieve(url=url, filename=filename) return item
-
settings.py
# 注释遵循协议 # ROBOTSTXT_OBEY = True # 管道 ITEM_PIPELINES = { "scrapy_project.pipelines.SaveDataPipeline": 300, "scrapy_project.pipelines.DownloadImagesPipeline": 301, }
11、推荐阅读
二、CrawlSpider
1、简介
CrawlSpider 是 Scrapy 框架提供的一个特殊的 Spider 类型,除了继承到Spider的特性和功能外,还派生除了其自己独有的更加强大的特性和功能,其中最显著的功能就是”LinkExtractors链接提取器“。在Scrapy中Spider是所有爬虫的基类,其设计原则只是为了爬取start_url列表中网页。CrawlSpider 是基于广度优先算法构建的,可以自动发现并跟踪网页上的链接,并根据预定义的规则提取数据,用于处理那些需要遵循特定规则和链接提取的网站。
CrawlSpider 提供了一种更高级的方法来定义爬取规则,而无需编写大量的重复代码。它基于规则系统工作,其中每个规则由一个或多个链接提取器(LinkExtractor)和一个回调函数(callback)组成。规则定义了要提取的链接和如何处理这些链接的方法。
2、基本运行
适用于先爬取start_url列表中的网页,再从爬取的网页中获取link并继续爬取的工作。
- 链接提取器(LinkExtractor):链接提取器用于从网页中提取链接。CrawlSpider 提供了几种内置的链接提取器,如基于正则表达式、基于 CSS 选择器、基于 XPath 等,你可以根据需求选择合适的链接提取器。
- 规则(Rule):规则定义了要提取的链接和如何处理这些链接的方法。每个规则由一个链接提取器和一个回调函数组成。链接提取器用于提取链接,回调函数定义了如何处理这些链接。可以定义多个规则来处理不同类型的链接。
- 回调函数(callback):回调函数是指定规则要调用的方法。当链接提取器提取到链接时,将会调用相应的回调函数来处理提取到的链接。在回调函数中,你可以编写解析页面和提取数据的逻辑。
- follow 参数:在规则中,可以设置 follow 参数来决定是否继续跟踪从链接提取器提取的链接。如果设置为 True,则会继续跟踪这些链接并提取数据;如果设置为 False,则不会跟踪这些链接。
- allowed_domains 参数:allowed_domains 参数用于限制爬取的域名。只有在 allowed_domains 列表中的域名下的链接才会被跟踪和提取数据,其他域名下的链接将被忽略。
3、核心对象
-
Rule对象:每个Rule对爬取网站的动作定义了特定的操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用
class scrapy.contrib.spiders.Rule( link_extractor=None, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None, errback=None, )
- link_extractor为LinkExtractor,用于定义需要提取的链接
- callback参数:当link_extractor获取到链接时参数所指定的值作为回调函数,回调函数尽量不要用parse方法,crawlspider已使用了parse方法
- follow:指定了根据该规则从response提取的链接是否需要跟进。当callback为None,默认值为True
- process_links:主要用来过滤由link_extractor获取到的链接
- process_request:主要用来过滤在rule中提取到的request
-
LinkExtractors 链接提取器:提取链接。 每个LinkExtractors有唯一的公共方法是extract_links(),他接收一个response对象,并返回一个scrapy.link.Link对象。Linkextractors要实例化一次,并且extract_links方法会根据不同的response调用多次提取链接。
class scrapy.linkextractors.LinkExtractor( allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(), tags=("a", "area"), attrs=("href",), canonicalize=False, unique=True, process_value=None, deny_extensions=None, restrict_css=(), strip=True, restrict_text=None, )
- allow:满足括号中“正则表达式”的值会被提取,如果为空,则会全部匹配。
- deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取
- allow_domains:会被提取的链接domains。
- deny_domains:一定不会被提取链接的domains
- restrick_xpaths:使用xpath表达式,和allow共同作用过滤链接(只选到节点,不选到属性)
- restrict_css:使用css表达式,和allow共同作用过滤链接(只选到节点,不选到属性)
4、示例
爬取读书网业务代码:
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from crawl_spider_project.items import CrawlSpiderProjectItem
class ExampleSpider(CrawlSpider):
name = "example"
allowed_domains = ["www.dushu.com"]
start_urls = ["https://www.dushu.com/book/1211_1.html"]
rules = (Rule(LinkExtractor(allow=r"/book/1211_\d+\.html"), callback="parse_item", follow=False),)
@staticmethod
def parse_item(response):
book_list = response.xpath('//div[@class="bookslist"]//img')
for book in book_list:
src = book.xpath('./@data-original').extract_first()
name = book.xpath('./@alt').extract_first()
item = CrawlSpiderProjectItem(src=src, name=name)
yield item