当前位置: 首页 > news >正文

python数据分析之爬虫基础:scrapy详解

一、爬虫工程化

在之前的爬虫学习中基本已经掌握了爬虫这门技术的大多数技术点,但是我们现在写的代码还很流程化,很难进行商用,想要爬虫达到商用级别,必须要对我们现在编写的爬虫进行大刀阔斧式的重组,以达到工程化的爬虫,所谓工程化,就是让程序更加有体系,有逻辑,更加模块化。

爬虫工程化:对爬虫的功能进行模块化的开发,并达到可以批量生产的效果(不论开发还是数据产出)

二、scrapy简介

scrapy是一个用python编写的开源网络爬虫框架,用于高效地从网站上抓取信息并提取结构化数据。

特点:速度快、简单、可扩展性强。

三、scrapy的工作流程

引擎:scrapy的核心,所有模块的衔接,数据流程处理。

调度器:本质上这东西可以看成是一个队列,里面存放着一堆我们即将要发送的请求。可以看成是一个url的容器,它决定了下一步要爬取哪一个url,在这里可以对url进行去重操作。

下载器:它的本质就是一个发送请求的模块,返回的是response对象。

爬虫:这是我们要写的的一个个部分的内容,负责解析下载器返回的response对象,从中提取我们需要的数据。

管道:这是我们要写的第二部分的内容,主要负责数据的存储和各种持久化操作。

4ba1d43cec8c43d6bacdb8b181dfd33c.jpeg

工作流程:

1、爬虫中起始的url构成的request对象,并传递给调度器。

2、引擎从调度器中获取到request对象,然后交给下载器

3、由下载器来获取到网页源代码,并封装成response对象,并回馈给引擎

4、引擎将获取到的response对象传递给spider,由spider对数据进行解析(parse),并回馈给引擎。

5、引擎将将数据传递给pipeline进行数据持久化保存或进一步的数据处理

6、在此期间如果spider中提取到的并不是数据,而是子页面url,可以进一步提交给调度器,进而重复步骤。

四、scrapy的安装

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy

查看是否安装成,可以在终端输入:

scrapy version

五、scrapy的使用

接下来,我们用scrapy来完成一个超级简单的爬虫,目标:深入理解scrapy的工作流程,以及各个模块之间是如何搭配工作的。

1、创建项目:

scrapy startproject 项目名称

示例:

scrapy startproject spider_test

创建好项目后,我们可以在pycharm中观察到scrapy帮我们创建了一个文件夹,结构如下:

2d427462a8ff4ca88c2f295c26bb05c5.png

以爬取4399小游戏的名称、类型为例子

# 首先进入项目
cd spider_test
# 创建爬虫项目
scrapy genspider xiao 4399.com # xaio代表名称后面跟的是域名

然后spiders文件夹下会生成一个xiao.py的文件,内容为:

import scrapyclass XiaoSpider(scrapy.Spider):name = "xiao" # 爬虫的名字allowed_domains = ["4399.com"] # 允许抓取的域名start_urls = ["https://www.4399.com/flash/"] # 起始页面urldef parse(self, response):# 该方法是用来处理解析的print(response)

运行该程序:

scrapy crawl xiao

但会发现有相当多的内容日志,我们可以在setting.py加上这段程序:

LOG_LEVEL = "WARNING"
# 日志的级别:DEBUG INFO WARNING ERROR CRITICAL(由低到高)设置成warning代表warning及以上的信息才能被打印

运行程序后,信息如下:

<200 https://www.4399.com/flash/>
(.venv) PS D:python学习python_studypythonProjectspider_test> import scrapyclass XiaoSpider(scrapy.Spider):name = "xiao" # 爬虫的名字allowed_domains = ["4399.com"] # 允许抓取的域名start_urls = ["https://www.4399.com/flash/"] # 起始页面urldef parse(self, response):# 该方法是用来处理解析的# print(response)# 拿到源代码# print(response.text)# 提取数据# text = response.xpath('//ul[@class="n-game cf"]/li/a/b/text()').extract()# print(text)# 分块解析数据li_list= response.xpath('//ul[@class="n-game cf"]/li')for li in li_list:name = li.xpath('a/b/text()').extract_first()category = li.xpath('em/a/text()').extract_first()time = li.xpath('em/text()').extract_first()dic = {"name":name,"category":category,"time":time}

对数据进行解析后,下一步就是对数据进行存储,这一步是在pipeline管道中进行的,需要用到yield将数据传递个管道。

yield dic # 可以节省内存,是因青睐调用的

因为管道默认是不生效的,需要在setting里手动开启管道

ITEM_PIPELINES = {# key就是管道的路径# value是管道的优先级,数值越小,优先级越高"spider_test.pipelines.SpiderTestPipeline": 300,
}

管道pipeline.py文件中打印数据和爬虫的名称:

class SpiderTestPipeline:def process_item(self, item, spider): # 处理数据的专用方法,item;数据,spider:爬虫print(item)print(spider.name)return item

对url进行代理,cookie以及UA等操作可以在爬虫和引擎之间或者引擎和下载器之间处理。

六、scrapy shell

scrapy shell是scrapy终端,是一个交互终端,可以在未启动spider的情况下尝试及调试爬取的代码。其本意是是用来测试提取数据的代码,不过可以将其是为正常的python终端,在上面测试任何的python代码。该终端是用来测试xpath或css表达式,查找他们的工作及爬取的网页中提取的数据,在编写spider时,该终端提供了交互性测试表达式代码的功能,免去了每次修改后运行spider的麻烦。

1、安装ipython

pip install ipython

ipython终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。

2、应用

在ipython终端直接输入:

scrapy shell www.baidu.com

3、语法

(1)response对象

response.body(二进制文本)

response.text

response.url

response.stayus

(2)response解析

response.xpath()

使用xpath路径查询特定元素,返回selector列表对象

response.css()(bs4语法)

获取内容:response.css(‘#su::text’).extract_first()

获取属性:response.css(‘#su::attr(“value”)’).extract_first()

(3)selector对象

extract()

提取selector对象的值,如果提取不到,则会报错

使用xpath请求到的对象是一个selector对象,需要使用extract()方法拆包

extract_first()

提取selector列表中的第一个值,若提取不到返回空值

以获取百度网页百度一下为例:

scrapy shell www.baidu.com
response.xpath('//input[@id="su"]/@value').extract_first()

直接在终端输入即可,不需要输入ipython。

七、yield

1、带有yeild的函数不再是一个普通函数,而是一个生成器generator,用于迭代。

2、yield是一个类似于return的关键字,迭代一次遇到yield时就返回后面的值。重点是:下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行。

简要理解:yield就是return一个返回值,并记住这个返回的位置,下次迭代就从这个位置后开始。

案例:当当网(1)yield (2)管道封装(3)多条管道下载(4)多页数据下载

首先需要在终端创建项目以及爬虫名称

scrapy startproject scrapy_dangdang

cd scrapy_dangdang

scrapy genspider dangdang www.dangdang.com

因为要爬取书的名称、图片以及价格,可以在items.py中自定义数据结构。

class ScrapyDangdangItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()# 图名src = scrapy.Field()# 名称name = scrapy.Field()# 价格price = scrapy.Field()

在dangdang.py中去执行解析数据并发送url给引擎

import scrapy
from scrapy_dangdang.items import ScrapyDangdangItemclass DangdangSpider(scrapy.Spider):name = "dangdang"allowed_domains = ["www.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.01.02.00.00.00.html"]def parse(self, response):# src = //ul[@id="component_59"]/li//img/@src# name = //ul[@id="component_59"]/li//img/@alt# price = //ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()# 所有的seletor的对象,都可以再次调用xpath方法li_list = response.xpath('//ul[@id="component_59"]/li')for li in li_list:src = li.xpath('.//img/@data-original').extract_first() # 这里需要注意:除了第一条数据没有懒加载,其它均有懒加载if src:src = srcelse:src = li.xpath('.//img/@src').extract_first()name = li.xpath('.//img/@alt').extract_first()price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()book = ScrapyDangdangItem(src=src, name=name, price=price)yield book # 需要在setting中手动开启pipeline管道

然后就需要保存数据,这个时候就用到了管道pipeline,我们需要在setting中手动打开管道。

ITEM_PIPELINES = {"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,
}

key就是管道的路径,value是管道的优先级,数值越小,优先级越高。

在pipeline中保存数据:

from itemadapter import ItemAdapterclass ScrapyDangdangPipeline:# 在爬虫文件执行之前,就执行的方法:def open_spider(self, spider):self.fp = open("book.json","w",encoding="utf-8") # 打开文件但并未关闭,所以w的模式写入def process_item(self, item, spider):# item就是我们的数据# with open("book.json", "a", encoding="utf-8") as f: # 这里需要采用追加的形式,因为数据是一条条传递来的,每一个对象都打开一次文件,从而导致数据被覆盖#     f.write(str(item))# 但上述方法并不是最优解,因为我们对文件的操作过于频繁,,会有频繁的IO操作。这个时候我们通过两个方法来改进代码self.fp.write(str(item)    )return item# 爬虫文件执行后,执行的方法。def close_spider(self,spider):self.fp.close()

多条管道下载,实现一边下载图片,一边下载json数据。

# setting文件中需要加上这条管道。因为图片下载慢,因此优先级就低一些
ITEM_PIPELINES = {"scrapy_dangdang.pipelines.ScrapyDangdangPipeline": 300,"scrapy_dangdang.pipelines.DangDangDownloadImg":301
}
# pipelines文件
import urllib.request
class DangDangDownloadImg:def process_item(self, item, spider):url = "http:" + item.get('src') # 这里需要注意的是:下载下来的路径前面无http,需要拼接filename = './books/'+item.get("name")+'.jpg'urllib.request.urlretrieve(url=url,filename=filename)return item

完成当当网的多页数据下载,因为每一页爬取的业务都是一样的,所以我们只需要将执行的那个页的请求再次调用parse方法即可。

import scrapy
from scrapy_dangdang.items import ScrapyDangdangItem
from scrapy.http import Request
class DangdangSpider(scrapy.Spider):name = "dangdang"# 如果十多页下载的话,那么必须要调整的是allowed_domins的范围,一般情况只写域名allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/pg1-cp01.01.02.00.00.00.html"]base_url = "https://category.dangdang.com/pg"page = 1def parse(self, response):# src = //ul[@id="component_59"]/li//img/@src# name = //ul[@id="component_59"]/li//img/@alt# price = //ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()# 所有的seletor的对象,都可以再次调用xpath方法li_list = response.xpath('//ul[@id="component_59"]/li')for li in li_list:src = li.xpath('.//img/@data-original').extract_first() # 这里需要注意:除了第一条数据没有懒加载,其它均有懒加载if src:src = srcelse:src = li.xpath('.//img/@src').extract_first()name = li.xpath('.//img/@alt').extract_first()price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()book = ScrapyDangdangItem(src=src, name=name, price=price)yield book # 需要在setting中手动开启pipeline管道if self.page < 100:self.page = self.page + 1url = self.base_url + str(self.page) + "-cp01.01.02.00.00.00.html"# 调用parse方法,callback表示要执行的函数yield scrapy.Request(url=url, callback=self.parse)

八、CrawlSpider

CrawlSpider可以定义规则,在解析html内容的时候,可以根据连接规则提出指定的链接,然后再向这些链接发送请求。所以,如果有需要跟进链接的需求,意思就是爬取了网页之后,需要提取链接再次爬取,使用CrawlSpider是非常合适的。

1、提取链接

提取链接器,在这里就可以写规则提取指定连接

scrapy.linkectractors.LinkExtractor(

allow = (), # 正则表达式,提取符合正则的链接

dengy = (), # (不用)正则表达式 不提取符合正则的链接

allow_domins = (), # (不用)允许的域名

restrict_xpaths = (), # xpath,提取符合xpath规则的链接

restrict_css = (), # 提取符合选择器规则的链接

2、模拟使用

正则用法:links = LinkExtractor(allow=r"list_23d+.html")

xpath语法:links = LinkExtractor(restrict_xpaths=r"//div[@class=‘x’]")

css语法:links = LinkExtractor(restrict_css=“.x”)

3、提取连接

link.extract_links(response)

案例:以爬取多页读书网数据为例(每一页数据的结构是相似的),并存储到数据库中。

4d25e838334c4f3b828495da7e0d822a.png

scrapy shell https://www.dushu.com/book/1107.html
# 导包
In [1]: from scrapy.linkextractors import LinkExtractor
# 通过re正则表达式提取数据
In [2]: link = LinkExtractor(allow=r'/book/1107_d+.html')
# 提取连接
In [3]: link.extract_links(response)

9440a84fd9cf453a98654afcac23eb57.png

注意事项:

1、callback只能写函数名字符串,callback=“parse_item”

2、在基本的spider中,如果重新发送请求,那么callback写的是 callback=self.parse_item follow=true 是否跟进就是按照提取连接规则进行提取

代码复现:

# 在终端创建项目
scrapy stratproject scrapy_readbook
# 进入根目录
scrapy startproject scrapy_readbook
# 创建爬虫文件这里是有所不同的,需要注意
scrapy genspider -t crawl read www.dushu.com/book/1107.html

80c88904007a4c0ea8afde366e2f8645.png

上面是原始的read爬虫文件,还是有所不同的。

把数据存储在MySQL中,这里可能需要一些MySQL以及pymysql的知识。可以根据情况进行学习。这里我们只需要在pipelines文件中写主要的存储逻辑,并且在settings文件中进行多管道下载:

# settings文件
DB_HOST = "localhost"
DB_PORT = 3306
DB_USER = "root"
DB_PASSWORD = "0219423"
ITEM_PIPELINES = {"scrapy_readbook.pipelines.ScrapyReadbookPipeline": 300,"scrapy_readbook.pipelines.PyMysqlPipeline":301
}
# pipelines文件
# 加载settings文件
from scrapy.utils.project import get_project_settings
from pymysql import Connection
class PyMysqlPipeline:def open_spider(self, spider):settings = get_project_settings()self.host = settings["DB_HOST"]self.port = settings["DB_PORT"]self.user = settings["DB_USER"]self.password = settings["DB_PASSWORD"]self.connect()def connect(self):self.conn = Connection(host=self.host,port=self.port,user=self.user,password=self.password)self.cursor = self.conn.cursor()self.conn.select_db("test")def process_item(self, item, spider):sql = 'insert into spider(name,src) values("{}","{}")'.format(item['name'], item['src'])self.cursor.execute(sql)self.conn.commit()return itemdef close_spider(self, spider):self.cursor.close()self.conn.close()

ebcfbba868ff469db8ebaa10f7e72ce1.png

九、scrapy的post请求

我们还是以百度翻译为例,在这里我就只写出逻辑执行的文件:

import scrapy
import jsonclass PostSpider(scrapy.Spider):name = "post"allowed_domains = ["fanyi.baidu.com"]# post请求 如果没有参数 那么这个请求将没有任何意义,所以start_urls也就没有用了,从而parse方法也无用# start_urls = ["https://fanyi.baidu.com/sug"]# def parse(self, response):#     passdef start_requests(self):url = "https://fanyi.baidu.com/sug"data={"kw": "spider"}yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_second)def parse_second(self, response):content = response.textobj = json.loads(content)print(obj)

相关文章:

python数据分析之爬虫基础:scrapy详解

一、爬虫工程化 在之前的爬虫学习中基本已经掌握了爬虫这门技术的大多数技术点&#xff0c;但是我们现在写的代码还很流程化&#xff0c;很难进行商用&#xff0c;想要爬虫达到商用级别&#xff0c;必须要对我们现在编写的爬虫进行大刀阔斧式的重组&#xff0c;以达到工程化的…...

openwrt 负载均衡方法 openwrt负载均衡本地源接口

openwrt 负载均衡方法 openwrt负载均衡本地源接口_mob6454cc647bdb的技术博客_51CTO博客 本人注重原理分析&#xff0c;要求对其原理掌握&#xff0c;否则按教程操作&#xff0c;你怕是什么都学不会&#xff0c;仔细看&#xff0c;认真记比较好。 首先确认一下基本细节 1、路由…...

Linux高级--3.3.2.6高并发编程之“内存屏障”“CPU屏障”“编译屏障”

一、内存屏障 在 Linux C 语言编程 中&#xff0c;内存屏障&#xff08;Memory Barrier&#xff09; 是一种用于控制内存访问顺序的技术。它主要用于多处理器系统中&#xff0c;确保某些操作按预期顺序执行&#xff0c;避免 CPU 和编译器对内存访问进行优化&#xff0c;从而影…...

【含开题报告+文档+PPT+源码】基于SpringBoot的智能安全与急救知识科普系统设计与实现

开题报告 在全球范围内&#xff0c;安全与急救知识的普及已成为提升公众安全素养、减少意外伤害发生率、提高突发事件应对能力的重要举措。尤其是在当今社会&#xff0c;人们面临的生活、工作环境日益复杂&#xff0c;交通事故、火灾、溺水、突发疾病等各种意外事件的发生概率…...

EMQX5.X版本性能配置调优参数

EMQX 主配置文件为 emqx.conf&#xff0c;根据安装方式其所在位置有所不同&#xff1a; 安装方式配置文件所在位置DEB 或 RPM 包安装/etc/emqx/emqx.confDocker 容器/opt/emqx/etc/emqx.conf解压缩包安装./etc/emqx.conf EMQ X 消息服务器默认占用的 TCP 端口包括: 端口 说明…...

电脑配置maven-3.6.1版本

不要使用太高的版本。 apache-maven-3.6.1-bin.zip 下载这个的maven压缩包 使用3.6.1版本。 解压缩放在本地软甲目录下面&#xff1a; 配置系统环境变量 在系统环境下面配置MAVEN_HOME 点击path 新增一条 在cmd中输入 mvn -v 检查maven的版本 配置阿里云镜像和本地的仓库 …...

水电站视频智能监控系统方案设计与技术应用方案

一、背景需求 水电站作为国家重要的能源基地&#xff0c;其安全运行对于保障能源供应和社会稳定具有重要意义。然而&#xff0c;传统的人工监控方式存在着诸多问题&#xff0c;如人力成本高、监控范围有限、反应不及时等。因此&#xff0c;水电站急需引进一种先进的视频智能监控…...

React 组件通信完整指南 以及 自定义事件发布订阅系统

React 组件通信完整指南 1. 父子组件通信 1.1 父组件向子组件传递数据 // 父组件 function ParentComponent() {const [data, setData] useState(Hello from parent);return <ChildComponent message{data} />; }// 子组件 function ChildComponent({ message }) {re…...

华为 AI Agent:企业内部管理的智能变革引擎(11/30)

一、华为 AI Agent 引领企业管理新潮流 在当今数字化飞速发展的时代&#xff0c;企业内部管理的高效性与智能化成为了决定企业竞争力的关键因素。华为&#xff0c;作为全球领先的科技巨头&#xff0c;其 AI Agent 技术在企业内部管理中的应用正掀起一场全新的变革浪潮。 AI Ag…...

【Pandas】pandas Series empty

Pandas2.2 Series Attributes 方法描述Series.index每个数据点的标签或索引Series.array对象底层的数据数组Series.values以NumPy数组的形式访问Series中的数据值Series.dtype用于获取 Pandas Series 中数据的类型&#xff08;dtype&#xff09;Series.shape用于获取 Pandas …...

Git如何设置和修改当前分支跟踪的上游分支

目录 前言 背景 设置当前分支跟踪的上游分支 当前分支已有关联&#xff0c;删除其关联&#xff0c;重新设置上游 常用的分支操作 参考资料 前言 仅做学习记录&#xff0c;侵删 背景 在项目开发过程中&#xff0c;从master新建分支时&#xff0c;会出现没有追踪的上游分…...

GitHub新手用法详解【适合新手入门-建议收藏!!!】

目录 什么是Github&#xff0c;为什么使用它&#xff1f; 一、GitHub账号的注册与登录 二、 gitbash安装详解 1.git bash的下载与安装 2.git常用命令 3. Git 和 GitHub 的绑定 1. 获取SSH keys 2.绑定ssh密钥 三、通过Git将代码提交到GitHub 1.克隆仓库 2.测试提交代码…...

游戏开发线性空间下PS工作流程

前言 使用基于物理的渲染&#xff0c;为了保证光照计算的准确&#xff0c;需要使用线性空间&#xff1b; 使用线性空间会带来一个问题&#xff0c;ui 在游戏引擎中的渲染结果与 PS 中的不一致&#xff1a; PS&#xff08;颜色空间默认是sRGB伽马空间&#xff09;&#xff1a…...

7-10 最长公共子序列

目录 题目描述 输入格式: 输出格式: 输入样例: 输出样例: 解题思路&#xff1a; 详细代码&#xff1a; 题目描述 给出 1~n 的两个排列 P1 和 P2&#xff0c;求它们的最长公共子序列。 n 在 5~1000 之间。 输入格式: 第一行是一个数 n 接下来两行&#xff0c;每行为 n 个数&…...

亚远景-ISO 21434标准下的汽车网络安全:风险评估与管理的关键实践

ISO 21434标准&#xff0c;全称为ISO/SAE 21434 "Road Vehicles - Cybersecurity Engineering"&#xff0c;是国际标准化组织(ISO)发布的针对汽车领域的标准&#xff0c;旨在指导汽车制造商、供应商和相关利益相关方在汽车系统中应用适当的网络安全措施。在ISO 21434…...

C++ 的 source_location

1 __FILE__ 和 __LINE__ ​ 你一定看过这样的代码&#xff1a; printf("Internal error at \"%s\" on line %d.\n", __FILE__, __LINE__); 这行代码的作用就是打印出 printf() 函数调用发生时所在的源代码文件名&#xff08;包含路径&#xff09;和这行代…...

[python SQLAlchemy数据库操作入门]-14.实时数据采集 记录股市动态

哈喽,大家好,我是木头左! 要使用 SQLAlchemy 进行实时数据采集,首先需要搭建相应的开发环境。以下是所需的主要步骤: 安装 Python:确保你的系统上已经安装了 Python,推荐使用 Python 3.x 版本。创建虚拟环境:为了隔离项目依赖,建议为每个项目创建一个虚拟环境。可以使…...

`we_chat_union_id IS NOT NULL` 和 `we_chat_union_id != ‘‘` 这两个条件之间的区别

文章目录 1、什么是空字符串&#xff1f;2、两个引号之间加上空格 好的&#xff0c;我们来详细解释一下 we_chat_union_id IS NOT NULL 和 we_chat_union_id ! 这两个条件之间的区别&#xff0c;以及它们在 SQL 查询中的作用&#xff1a; 1. we_chat_union_id IS NOT NULL 含…...

【和春笋一起学C++】文本输入与读取

前言&#xff1a;前面学习了while语句后&#xff0c;下面用while语句实现一个重要的功能&#xff0c;逐字符的读取键盘输入的字符序列&#xff0c;并输出到显示屏上。 准备知识&#xff1a; C的输入输出包含以下3方面的内容&#xff1a; 对系统指定的标准设备的输入和输出。即…...

D类音频应用EMI管理

1、前言 对于EMI&#xff0c;首先需要理解天线。频率和波长之间的关系&#xff0c;如下图所示。   作为有效天线所需的最短长度是λ/4。在空气中&#xff0c;介电常数是1&#xff0c;但是在FR4或玻璃环氧PCB的情况下&#xff0c;介电常数大约4.8。这种效应会导致信号在FR4材…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...