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

Python爬虫学习 | Scrapy框架详解

一.Scrapy框架简介

何为框架,就相当于一个封装了很多功能的结构体,它帮我们把主要的结构给搭建好了,我们只需往骨架里添加内容就行。scrapy框架是一个为了爬取网站数据,提取数据的框架,我们熟知爬虫总共有四大部分,请求、响应、解析、存储,scrapy框架都已经搭建好了。scrapy是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架,scrapy使用了一种非阻塞(又名异步)的代码实现并发的,Scrapy之所以能实现异步,得益于twisted框架。twisted有事件队列,哪一个事件有活动,就会执行!Scrapy它集成高性能异步下载,队列,分布式,解析,持久化等。

1.五大核心组件

引擎(Scrapy)

框架核心,用来处理整个系统的数据流的流动, 触发事务(判断是何种数据流,然后再调用相应的方法)。也就是负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等,所以被称为框架的核心。

调度器(Scheduler)

用来接受引擎发过来的请求,并按照一定的方式进行整理排列,放到队列中,当引擎需要时,交还给引擎。可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。

下载器(Downloader)

负责下载引擎发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。Scrapy下载器是建立在twisted这个高效的异步模型上的。

爬虫(Spiders)

用户根据自己的需求,编写程序,用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。跟进的URL提交给引擎,再次进入Scheduler(调度器)。

项目管道(Pipeline)

负责处理爬虫提取出来的item,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。

2.工作流程

Scrapy中的数据流由引擎控制,其过程如下:

(1)用户编写爬虫主程序将需要下载的页面请求requests递交给引擎,引擎将请求转发给调度器;

(2)调度实现了优先级、去重等策略,调度从队列中取出一个请求,交给引擎转发给下载器(引擎和下载器中间有中间件,作用是对请求加工如:对requests添加代理、ua、cookie,response进行过滤等);

(3)下载器下载页面,将生成的响应通过下载器中间件发送到引擎;

(4) 爬虫主程序进行解析,这个时候解析函数将产生两类数据,一种是items、一种是链接(URL),其中requests按上面步骤交给调度器;items交给数据管道(数据管道实现数据的最终处理);

官方文档

英文版:https://docs.scrapy.org/en/latest/

http://doc.scrapy.org/en/master/

中文版:https://scrapy-chs.readthedocs.io/zh_CN/latest/intro/overview.html

https://www.osgeo.cn/scrapy/topics/architecture.html

二、安装及常用命令介绍

1. 安装

Linux:pip3 install scrapy

Windows:

  a. pip3 install wheelb. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twistedc. shift右击进入下载目录,执行 pip3 install typed\_ast-1.4.0-cp36-cp36m-win32.whld. pip3 install pywin32e. pip3 install scrapy

2.scrapy基本命令行

(1)创建一个新的项目
scrapy startproject ProjectName(2)生成爬虫
scrapy genspider +SpiderName+website(3)运行(crawl)             # -o output
scrapy crawl +SpiderName
scrapy crawl SpiderName \-o file.json
scrapy crawl SpiderName\-o file.csv(4)检查spider文件是否有语法错误
scrapy check(5)list返回项目所有spider名称
scrapy list(6)测试电脑当前爬取速度性能:
scrapy bench(7)scrapy runspider
scrapy runspider zufang\_spider.py(8)编辑spider文件:
scrapy edit <spider>
相当于打开vim模式,实际并不好用,在IDE中编辑更为合适。(9)将网页内容下载下来,然后在终端打印当前返回的内容,相当于 request 和 urllib 方法:
scrapy fetch <url>(10)将网页内容保存下来,并在浏览器中打开当前网页内容,直观呈现要爬取网页的内容: 
scrapy view <url>(11)进入终端。打开 scrapy 显示台,类似ipython,可以用来做测试:
scrapy shell \[url\](12)输出格式化内容:
scrapy parse <url> \[options\](13)返回系统设置信息:
scrapy settings \[options\]
如:
$ scrapy settings \--get BOT\_NAME
scrapybot(14)显示scrapy版本:
scrapy version \[\-v\]
后面加 \-v 可以显示scrapy依赖库的版本

三、简单实例

以麦田租房信息爬取为例,网站http://bj.maitian.cn/zfall/PG1

**1.**创建项目

scrapy startproject houseinfo

生成项目结构:

scrapy.cfg 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)

items.py 设置数据存储模板,用于结构化数据,如:Django的Model

pipelines 数据持久化处理

settings.py 配置文件

spiders 爬虫目录

2.创建爬虫应用程序

cd houseinfo
scrapy genspider maitian maitian.com

然后就可以在spiders目录下看到我们的爬虫主程序

3.编写爬虫文件

步骤2执行完毕后,会在项目的spiders中生成一个应用名的py爬虫文件,文件源码如下:

 1 # -\*- coding: utf-8 -\*-2 import scrapy3 4 5 class MaitianSpider(scrapy.Spider): 6     name = 'maitian'                        **# 应用名称** 7     allowed\_domains = \['maitian.com'\]       **#一般注释掉,允许爬取的域名(如果遇到非该域名的url则爬取不到数据)**8     start\_urls = \['http://maitian.com/'\]    **#起始爬取的url列表,该列表中存在的url,都会被parse进行请求的发送**9     
10 #解析函数
11 def parse(self, response):
12         pass

我们可以在此基础上,根据需求进行编写

 1 # -\*- coding: utf-8 -\*-2 import scrapy3 4 class MaitianSpider(scrapy.Spider): 5     name = 'maitian'6     start\_urls = \['http://bj.maitian.cn/zfall/PG100'\]7 8 9 #解析函数
10 def parse(self, response):
11 
12         li\_list = response.xpath('//div\[@class="list\_wrap"\]/ul/li')
13         results = \[\]
14         for li in li\_list:
15             title =  li.xpath('./div\[2\]/h1/a/text()').extract\_first().strip()
16             price = li.xpath('./div\[2\]/div/ol/strong/span/text()').extract\_first().strip()
17             square = li.xpath('./div\[2\]/p\[1\]/span\[1\]/text()').extract\_first().replace('㎡','')     # 将面积的单位去掉
18             area = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().**split('\\xa0')\[0\]  # 以空格分隔**
19             adress = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[2\]
20 
21             dict = {
22                 "标题":title,
23                 "月租金":price,
24                 "面积":square,
25                 "区域":area,
26                 "地址":adress
27 }
28 results.append(dict)
29 
30 print(title,price,square,area,adress)
31         return results

须知:

  • xpath为scrapy中的解析方式
  • xpath函数返回的为列表,列表中存放的数据为Selector类型数据。解析到的内容被封装在Selector对象中,需要调用extract()函数将解析的内容从Selector中取出。
  • 如果可以保证xpath返回的列表中只有一个列表元素,则可以使用extract_first(), 否则必须使用extract()

两者等同,都是将列表中的内容提取出来

title = li.xpath(‘./div[2]/h1/a/text()’).extract_first().strip()

title = li.xpath(‘./div[2]/h1/a/text()’)[0].extract()****.strip()

4. 设置修改settings.py配置文件相关配置:

1 #伪装请求载体身份
2 USER\_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10\_12\_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
3 
4 #可以忽略或者不遵守robots协议
5 ROBOTSTXT\_OBEY = False  

5.执行爬虫程序:scrapy crawl maitain

**爬取全站数据,也就是全部页码数据。**本例中,总共100页,观察页面之间的共性,构造通用url

方式一:通过占位符,构造通用url

 1 import scrapy 2 3 class MaitianSpider(scrapy.Spider): 4     name = 'maitian'5     start\_urls = \['http://bj.maitian.cn/zfall/PG{}'.**format(page) for page in range(1,4**)\]            **#注意写法**6 7 8     #解析函数9     def parse(self, response):
10 
11         li\_list = response.xpath('//div\[@class="list\_wrap"\]/ul/li')
12         results = \[\]
13         for li in li\_list:
14             title =  li.xpath('./div\[2\]/h1/a/text()').extract\_first().strip()
15             price = li.xpath('./div\[2\]/div/ol/strong/span/text()').extract\_first().strip()
16             square = li.xpath('./div\[2\]/p\[1\]/span\[1\]/text()').extract\_first().replace('㎡','')
17    **         # 也可以通过正则匹配提取出来**
18             area = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]')..re(r'昌平|朝阳|东城|大兴|丰台|海淀|石景山|顺义|通州|西城')\[0\]           
19             adress = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[2\]
20 
21             dict = {
22                 "标题":title,
23                 "月租金":price,
24                 "面积":square,
25                 "区域":area,
26                 "地址":adress
27 }
28 results.append(dict)
29 
30         return results

如果碰到一个表达式不能包含所有情况的项目,解决方式是先分别写表达式,最后通过列表相加,将所有url合并成一个url列表,例如

start\_urls = \['http://www.guokr.com/ask/hottest/?page={}'.format(n) for n in range(1, 8)\] **+** \['http://www.guokr.com/ask/highlight/?page={}'.format(m) for m in range(1, 101)\]

方式二:通过重写start_requests方法,获取所有的起始url。(不用写start_urls

 1 import scrapy 2 3 class MaitianSpider(scrapy.Spider): 4     name = 'maitian'5 **6     def start\_requests(self): 7         pages=\[\]8         for page in range(90,100):9             url='http://bj.maitian.cn/zfall/PG{}'.format(page)
10             page=scrapy.Request(url)
11 pages.append(page)
12         return pages**
13 
14     #解析函数
15     def parse(self, response):
16 
17         li\_list = response.xpath('//div\[@class="list\_wrap"\]/ul/li')
18 
19         results = \[\]
20         for li in li\_list:
21             title =  li.xpath('./div\[2\]/h1/a/text()').extract\_first().strip(),
22             price = li.xpath('./div\[2\]/div/ol/strong/span/text()').extract\_first().strip(),
23             square = li.xpath('./div\[2\]/p\[1\]/span\[1\]/text()').extract\_first().replace('㎡',''),
24             area = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').re(r'昌平|朝阳|东城|大兴|丰台|海淀|石景山|顺义|通州|西城')\[0\],
25             adress = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[2\]
26 
27             dict = {
28                 "标题":title,
29                 "月租金":price,
30                 "面积":square,
31                 "区域":area,
32                 "地址":adress
33 }
34 results.append(dict)
35 
36         return results

四、数据****持久化存储

  • 基于终端指令的持久化存储
  • 基于管道的持久化存储

只要是数据持久化存储,parse方法必须有返回值(也就是return后的内容)

1. 基于终端指令的持久化存储

执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储,windows终端不能使用txt格式

  •   scrapy crawl 爬虫名称 -o xxx.json
    
  •   scrapy crawl 爬虫名称 -o xxx.xml
    
  • scrapy crawl 爬虫名称 -o xxx.csv

以麦田为例,spider中的代码不变,将返回值写到qiubai.csv中。本地没有,就会自己创建一个。本地有就会追加

scrapy crawl maitian   -o maitian.csv

就会在项目目录下看到,生成的文件

查看文件内容

**2.**基于管道的持久化存储

scrapy框架中已经为我们专门集成好了高效、便捷的持久化操作功能,我们直接使用即可。要想使用scrapy的持久化操作功能,我们首先来认识如下两个文件:

  • items.py数据结构模板文件。定义数据属性。
  • pipelines.py管道文件。接收数据(items),进行持久化操作。

持久化流程:

① 爬虫文件爬取到数据解析后,需要将数据封装到items对象中。

② **使用yield关键字将items对象提交给pipelines管道,**进行持久化操作。

③ 在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码**,将item对象中存储的数据进行持久化存储在管道的process_item方法中执行io操作,进行持久化存储)**

④ settings.py配置文件中开启管道

2.1****保存到本地的持久化存储

爬虫文件**:maitian.py**

 1 import scrapy2 from houseinfo.items import HouseinfoItem              **# 将item导入** 3 4 class MaitianSpider(scrapy.Spider): 5     name = 'maitian'6     start\_urls = \['http://bj.maitian.cn/zfall/PG100'\]7 8     #解析函数9 def parse(self, response):
10 
11         li\_list = response.xpath('//div\[@class="list\_wrap"\]/ul/li')
12 
13         for li in li\_list:
14             **item = HouseinfoItem(**
15                 title =  li.xpath('./div\[2\]/h1/a/text()').extract\_first().strip(),
16                 price = li.xpath('./div\[2\]/div/ol/strong/span/text()').extract\_first().strip(),
17                 square = li.xpath('./div\[2\]/p\[1\]/span\[1\]/text()').extract\_first().replace('㎡',''),
18                 area = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[0\],
19                 adress = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[2\]
20 )
21 
22             yield item                     ** # 提交给管道,然后管道定义存储方式**

items文件:items.py

1 import scrapy
2 
3 class HouseinfoItem(scrapy.Item):
4     title = scrapy.Field()          #存储标题,里面可以存储任意类型的数据
5     price = scrapy.Field()
6     square = scrapy.Field()
7     area = scrapy.Field()
8     adress = scrapy.Field()

管道文件:pipelines.py

 1 class HouseinfoPipeline(object):2     def \_\_init\_\_(self):3         self.file = None 4 5     #开始爬虫时,执行一次6     def open\_spider(self,**spider**):7         self.file = open('maitian.csv',**'a'**,encoding='utf-8')                    # 选用了追加模式8         self.file.write(",".join(\["标题","月租金","面积","区域","地址","\\n"\]))9         print("开始爬虫")
10 
11 # 因为该方法会被执行调用多次,所以文件的开启和关闭操作写在了另外两个只会各自执行一次的方法中。
12 def process\_item(self, item, spider):
13         content = \[item\["title"\], item\["price"\], item\["square"\], item\["area"\], item\["adress"\], "\\n"\]
14         self.file.write(",".join(content))
15         return item
16 
17 # 结束爬虫时,执行一次
18 def close\_spider(self,**spider**):
19 self.file.close()
20         print("结束爬虫")

配置文件:settings.py

 1 #伪装请求载体身份2 USER\_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10\_12\_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' 3 4 #可以忽略或者不遵守robots协议5 ROBOTSTXT\_OBEY = False 6 7 **#开启管道
** 8 ITEM\_PIPELINES = { 9    'houseinfo.pipelines.HouseinfoPipeline': 300,                    #数值300表示为优先级,值越小优先级越高
10 }

五、爬取多级页面

爬取多级页面,会遇到2个问题:

问题1:如何对下一层级页面发送请求?

答:在每一个解析函数的末尾,通过Request方法对下一层级的页面手动发起请求

**# 先提取二级页面url,再对二级页面发送请求。多级页面以此类推**
def parse(self, response):next\_url \= response.xpath('//div\[2\]/h2/a/@href').extract()\[0\]           # 提取二级页面urlyield scrapy.Request(url=next\_url, callback=self.next\_parse)  **# 对二级页面发送请求,注意要用yield,回调函数不带括号**

问题2:解析的数据不在同一张页面中,最终如何将数据传递

答:涉及到请求传参,可以在对下一层级页面发送请求的时候,通过meta参数进行数据传递,meta字典就会传递给回调函数的response参数。下一级的解析函数通过response获取item(先通过 response.meta返回接收到的meta字典,再获得item字典)

# 通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数
def parse(self, response):item \= Item()                                                       # 实例化item对象Item\["field1"\] = response.xpath('expression1').extract()\[0\]         # 列表中只有一个元素Item\["field2"\] = response.xpath('expression2').extract()            # 列表next\_url = response.xpath('expression3').extract()\[0\]           # 提取二级页面url# meta参数:请求传参.通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数yield scrapy.Request(url=next\_url, callback=self.next\_parse,**meta={'item':item}**)            # 对二级页面发送请求def next\_parse(self,response):# 通过response获取item. 先通过 response.meta返回接收到的meta字典,再获得item字典item = **response.meta\['item'****\]**item\['field'\] = response.xpath('expression').extract\_first()yield item                                                          #提交给管道

案例1:麦田,对所有页码发送请求。不推荐将每一个页码对应的url存放到爬虫文件的起始url列表(start_urls)中。这里我们使用Request方法手动发起请求。

# -\*- coding: utf-8 -\*-
import scrapy
from houseinfo.items import HouseinfoItem               # 将item导入class MaitianSpider(scrapy.Spider):name \= 'maitian'start\_urls \= \['http://bj.maitian.cn/zfall/PG1'\]#爬取多页page = 1url \= 'http://bj.maitian.cn/zfall/PG%d'#解析函数def parse(self, response):li\_list \= response.xpath('//div\[@class="list\_wrap"\]/ul/li')for li in li\_list:item \= HouseinfoItem(title \=  li.xpath('./div\[2\]/h1/a/text()').extract\_first().strip(),price \= li.xpath('./div\[2\]/div/ol/strong/span/text()').extract\_first().strip(),square \= li.xpath('./div\[2\]/p\[1\]/span\[1\]/text()').extract\_first().replace('㎡',''),area \= li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').re(r'昌平|朝阳|东城|大兴|丰台|海淀|石景山|顺义|通州|西城')\[0\],           # 也可以通过正则匹配提取出来adress = li.xpath('./div\[2\]/p\[2\]/span/text()\[2\]').extract\_first().strip().split('\\xa0')\[2\])\['http://bj.maitian.cn/zfall/PG{}'.format(page) for page in range(1, 4)\]yield item                                  # 提交给管道,然后管道定义存储方式if self.page < 4:self.page += 1new\_url \= format(self.url%self.page)                             # 这里的%是拼接的意思yield scrapy.Request(url=new\_url,callback=self.parse)            # 手动发起一个请求,注意一定要写yield

案例2:这个案例比较好的一点是,parse函数,既有对下一页的回调,又有对详情页的回调

import scrapyclass QuotesSpider(scrapy.Spider):name \= 'quotes\_2\_3'start\_urls \= \['http://quotes.toscrape.com',\]allowed\_domains \= \['toscrape.com',\]def parse(self,response):for quote in response.css('div.quote'):yield{'quote': quote.css('span.text::text').extract\_first(),'author': quote.css('small.author::text').extract\_first(),'tags': quote.css('div.tags a.tag::text').extract(),}author\_page \= response.css('small.author+a::attr(href)').extract\_first()authro\_full\_url \= response.urljoin(author\_page)yield scrapy.Request(**authro\_full\_url, callback=self.parse\_author**)       ** # 对详情页发送请求,回调详情页的解析函数**next\_page \= response.css(**'li.next a::attr("href")'**).extract\_first()      **# 通过css选择器定位到下一页**if next\_page is not None:next\_full\_url \= response.urljoin(next\_page)yield scrapy.Request(**next\_full\_url, callback=self.pars**e)               **# 对下一页发送请求,回调自己的解析函数**def parse\_author(self,response):yield{'author': response.css('.author-title::text').extract\_first(),'author\_born\_date': response.css('.author-born-date::text').extract\_first(),'author\_born\_location': response.css('.author-born-location::text').extract\_first(),'authro\_description': response.css('.author-born-location::text').extract\_first(),

**案例3:**爬取www.id97.com电影网,将一级页面中的电影名称,类型,评分,二级页面中的上映时间,导演,片长进行爬取。(多级页面+传参)

# -\*- coding: utf-8 -\*-
import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):name \= 'movie'allowed\_domains \= \['www.id97.com'\]start\_urls \= \['http://www.id97.com/'\]def parse(self, response):div\_list \= response.xpath('//div\[@class="col-xs-1-5 movie-item"\]')for div in div\_list:item \= MovieproItem()                    item\['name'\] = div.xpath('.//h1/a/text()').extract\_first()item\['score'\] = div.xpath('.//h1/em/text()').extract\_first()item\['kind'\] = div.xpath('.//div\[@class="otherinfo"\]').xpath('string(.)').extract\_first()item\['detail\_url'\] = div.xpath('./div/a/@href').extract\_first()#meta参数:请求传参.通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数yield scrapy.Request(url=item\['detail\_url'\],callback=self.parse\_detail,meta={'item':item})    def parse\_detail(self,response):#通过response获取item. 先通过 response.meta返回接收到的meta字典,再获得item字典item = response.meta\['item'\]item\['actor'\] = response.xpath('//div\[@class="row"\]//table/tr\[1\]/a/text()').extract\_first()item\['time'\] = response.xpath('//div\[@class="row"\]//table/tr\[7\]/td\[2\]/text()').extract\_first()item\['long'\] = response.xpath('//div\[@class="row"\]//table/tr\[8\]/td\[2\]/text()').extract\_first()yield item                                                    #提交item到管道

**案例4:稍复杂,可参考链接进行理解:**https://github.com/makcyun/web_scraping_with_python/tree/master/,https://www.cnblogs.com/sanduzxcvbnm/p/10277414.html

  1 #!/user/bin/env python2 3 """4 爬取豌豆荚网站所有分类下的全部 app5 数据爬取包括两个部分:6 一:数据指标7 1 爬取首页8 2 爬取第2页开始的 ajax 页9 二:图标10 使用class方法下载首页和 ajax 页11 分页循环两种爬取思路,12 指定页数进行for 循环,和不指定页数一直往下爬直到爬不到内容为止13 1 for 循环14 """15 16 import scrapy 17 from wandoujia.items import WandoujiaItem 18 19 import requests 20 from pyquery import PyQuery as pq 21 import re 22 import csv 23 import pandas as pd 24 import numpy as np 25 import time 26 import pymongo 27 import json 28 import os 29 from urllib.parse import urlencode 30 import random 31 import logging 32 33 logging.basicConfig(filename='wandoujia.log',filemode='w',level=logging.DEBUG,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')34 # https://juejin.im/post/5aee70105188256712786b7f35 logging.warning("warn message")36 logging.error("error message")37 38 39 class WandouSpider(scrapy.Spider): 40     name = 'wandou'41     allowed\_domains = \['www.wandoujia.com'\]42     start\_urls = \['http://www.wandoujia.com/'\]43 44     def \_\_init\_\_(self):45         self.cate\_url = 'https://www.wandoujia.com/category/app'46         # 首页url47         self.url = 'https://www.wandoujia.com/category/'48         # ajax 请求url49         self.ajax\_url = 'https://www.wandoujia.com/wdjweb/api/category/more?'50         # 实例化分类标签51         self.wandou\_category = Get\_category() 52 53     def start\_requests(self): 54         yield scrapy.Request(self.cate\_url,callback=self.get\_category)55         56     def get\_category(self,response): 57         # # num = 058         cate\_content = self.wandou\_category.parse\_category(response) 59         for item in cate\_content: 60             child\_cate = item\['child\_cate\_codes'\]61             for cate in child\_cate: 62                 cate\_code = item\['cate\_code'\]63                 cate\_name = item\['cate\_name'\]64                 child\_cate\_code = cate\['child\_cate\_code'\]65                 child\_cate\_name = cate\['child\_cate\_name'\]66 67       68         # # 单类别下载69         # cate\_code = 502970         # child\_cate\_code = 83771         # cate\_name = '通讯社交'72         # child\_cate\_name = '收音机'73         74                 # while循环75                 page = 1 # 设置爬取起始页数76                 print('\*' \* 50)77 78                 # # for 循环下一页79                 # pages = \[\]80                 # for page in range(1,3):81                 # print('正在爬取:%s-%s 第 %s 页 ' %82                 # (cate\_name, child\_cate\_name, page))83                 logging.debug('正在爬取:%s-%s 第 %s 页 ' %84                 (cate\_name, child\_cate\_name, page))85 86                 if page == 1:87                     # 构造首页url88                     category\_url = '{}{}\_{}' .format(self.url, cate\_code, child\_cate\_code) 89                 else:90                     params = { 91                     'catId': cate\_code,  # 大类别92                     'subCatId': child\_cate\_code,  # 小类别93                     'page': page,94                     }95                     category\_url = self.ajax\_url + urlencode(params) 96 97                 dict = {'page':page,'cate\_name':cate\_name,'cate\_code':cate\_code,'child\_cate\_name':child\_cate\_name,'child\_cate\_code':child\_cate\_code}98                     99                 yield scrapy.Request(category\_url,callback=self.parse,meta=dict)
100                             
101                 #     # for 循环方法
102                 #     pa = yield scrapy.Request(category\_url,callback=self.parse,meta=dict)
103                 #     pages.append(pa)
104                 # return pages
105 
106     def parse(self, response):
107         if len(response.body) >= 100:  # 判断该页是否爬完,数值定为100是因为无内容时长度是87
108             page = response.meta\['page'\]
109             cate\_name = response.meta\['cate\_name'\]
110             cate\_code = response.meta\['cate\_code'\]
111             child\_cate\_name = response.meta\['child\_cate\_name'\]
112             child\_cate\_code = response.meta\['child\_cate\_code'\]
113 
114             if page == 1:
115                 contents = response
116             else:
117                 jsonresponse = json.loads(response.body\_as\_unicode())
118                 contents = jsonresponse\['data'\]\['content'\]
119                 # response 是json,json内容是html,html 为文本不能直接使用.css 提取,要先转换
120                 contents = scrapy.Selector(text=contents, type="html")
121 
122             contents = contents.css('.card')
123             for content in contents:
124                 # num += 1
125                 item = WandoujiaItem()
126                 item\['cate\_name'\] = cate\_name
127                 item\['child\_cate\_name'\] = child\_cate\_name
128                 item\['app\_name'\] = self.clean\_name(content.css('.name::text').extract\_first())
129                 item\['install'\] = content.css('.install-count::text').extract\_first()
130                 item\['volume'\] = content.css('.meta span:last-child::text').extract\_first()
131                 item\['comment'\] = content.css('.comment::text').extract\_first().strip()
132                 item\['icon\_url'\] = self.get\_icon\_url(content.css('.icon-wrap a img'),page)
133                 yield item
134             
135             # 递归爬下一页
136             page += 1
137             params = {
138                     'catId': cate\_code,  # 大类别
139                     'subCatId': child\_cate\_code,  # 小类别
140                     'page': page,
141 }
142             ajax\_url = self.ajax\_url + urlencode(params)
143             
144             dict = {'page':page,'cate\_name':cate\_name,'cate\_code':cate\_code,'child\_cate\_name':child\_cate\_name,'child\_cate\_code':child\_cate\_code}
145             yield scrapy.Request(ajax\_url,callback=self.parse,meta=dict)
146                 
147 
148 
149         # 名称清除方法1 去除不能用于文件命名的特殊字符
150     def clean\_name(self, name):
151         rule = re.compile(r"\[\\/\\\\\\:\\\*\\?\\"\\<\\>\\|\]")  # '/ \\ : \* ? " < > |')
152         name = re.sub(rule, '', name)
153         return name
154 
155     def get\_icon\_url(self,item,page):
156         if page == 1:
157             if item.css('::attr("src")').extract\_first().startswith('https'):
158                 url = item.css('::attr("src")').extract\_first()
159             else:
160                 url = item.css('::attr("data-original")').extract\_first()
161         # ajax页url提取
162         else:
163             url = item.css('::attr("data-original")').extract\_first()
164 
165         # if url:  # 不要在这里添加url存在判断,否则空url 被过滤掉 导致编号对不上
166         return url
167 
168 
169 # 首先获取主分类和子分类的数值代码 # # # # # # # # # # # # # # # #
170 class Get\_category():
171     def parse\_category(self, response):
172         category = response.css('.parent-cate')
173         data = \[{
174             'cate\_name': item.css('.cate-link::text').extract\_first(),
175             'cate\_code': self.get\_category\_code(item),
176             'child\_cate\_codes': self.get\_child\_category(item),
177         } for item in category\]
178         return data
179 
180     # 获取所有主分类标签数值代码
181     def get\_category\_code(self, item):
182         cate\_url = item.css('.cate-link::attr("href")').extract\_first()
183 
184         pattern = re.compile(r'.\*/(\\d+)')  # 提取主类标签代码
185         cate\_code = re.search(pattern, cate\_url)
186         return cate\_code.group(1)
187 
188     # 获取所有子分类标签数值代码
189     def get\_child\_category(self, item):
190         child\_cate = item.css('.child-cate a')
191         child\_cate\_url = \[{
192             'child\_cate\_name': child.css('::text').extract\_first(),
193             'child\_cate\_code': self.get\_child\_category\_code(child)
194         } for child in child\_cate\]
195 
196         return child\_cate\_url
197 
198     # 正则提取子分类
199     def get\_child\_category\_code(self, child):
200         child\_cate\_url = child.css('::attr("href")').extract\_first()
201         pattern = re.compile(r'.\*\_(\\d+)')  # 提取小类标签编号
202         child\_cate\_code = re.search(pattern, child\_cate\_url)
203         return child\_cate\_code.group(1)
204 
205     # # 可以选择保存到txt 文件
206     # def write\_category(self,category):
207     #     with open('category.txt','a',encoding='utf\_8\_sig',newline='') as f:
208     #         w = csv.writer(f)
209     #         w.writerow(category.values())

View Code

以上4个案例都只贴出了爬虫主程序脚本,因篇幅原因,所以item、pipeline和settings等脚本未贴出,可参考上面案例进行编写。

**、Scrapy发送post请求**

**问题:**在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?

**解答:**其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:

 def start\_requests(self):for u in self.start\_urls:yield scrapy.Request(url=u,callback=self.parse)

**注意:****该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。不过,**一般情况下不用scrapy发post请求,用request模块。

例:爬取百度翻译

# -\*- coding: utf-8 -\*-
import scrapyclass PostSpider(scrapy.Spider):name \= 'post'# allowed\_domains = \['www.xxx.com'\]start\_urls = \['https://fanyi.baidu.com/sug'\]def start\_requests(self):data \= {                                                # post请求参数'kw':'dog'}for url in self.start\_urls:yield **scrapy.FormRequest**(url=url,formdata=data,callback=self.parse)        # 发送post请求def parse(self, response):print(response.text)

七、设置日志等级

- 在使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息。

- 日志信息的种类:

ERROR : 一般错误

WARNING : 警告

INFO : 一般的信息

DEBUG : 调试信息

- 设置日志信息指定输出:

settings.py配置文件中,加入

                LOG\_LEVEL = ‘指定日志信息种类’即可。LOG\_FILE = 'log.txt'则表示将日志信息写入到指定文件中进行存储。

其他常用设置:

BOT\_NAME
默认:“scrapybot”,使用startproject命令创建项目时,其被自动赋值CONCURRENT\_ITEMS
默认为100,Item Process(即Item Pipeline)同时处理(每个response的)item时最大值CONCURRENT\_REQUEST
默认为16,scrapy downloader并发请求(concurrent requests)的最大值LOG\_ENABLED
默认为True,是否启用loggingDEFAULT\_REQUEST\_HEADERS
默认如下:{'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8', 'Accept-Language': 'en',}
scrapy http request使用的默认headerLOG\_ENCODING
默认utt\-8,logging中使用的编码LOG\_LEVEL
默认“DEBUG”,log中最低级别,可选级别有:CRITICAL,ERROR,WARNING,DEBUGUSER\_AGENT
默认:“Scrapy/VERSION(....)”,爬取的默认User-Agent,除非被覆盖
COOKIES\_ENABLED\=False,禁用cookies

八、同时运行多个爬虫

实际开发中,通常在同一个项目里会有多个爬虫,多个爬虫的时候是怎么将他们运行起来呢?

运行单个爬虫

import sys
from scrapy.cmdline import executeif \_\_name\_\_ == '\_\_main\_\_':execute(\["scrapy","crawl","maitian","\--nolog"\])

然后运行py文件即可运行名为‘maitian‘的爬虫

同时运行多个爬虫

步骤如下:

\- 在spiders同级创建任意目录,如:commands  
\- 在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)  
\- 在settings.py 中添加配置 COMMANDS\_MODULE = '项目名称.目录名称'  
\- 在项目目录执行命令:scrapy crawlall   crawlall.py代码
 1 from scrapy.commands import ScrapyCommand2     from scrapy.utils.project import get\_project\_settings3  4     class Command(ScrapyCommand):5  6         requires\_project = True7  8         def syntax(self):9             return '\[options\]'
10  
11         def short\_desc(self):
12             return 'Runs all of the spiders'
13  
14         def run(self, args, opts):
15             spider\_list = self.crawler\_process.spiders.list()
16             for name in spider\_list:
17                 self.crawler\_process.crawl(name, \*\*opts.\_\_dict\_\_)
18             self.crawler\_process.start()

如有侵权,请联系删除。

相关文章:

Python爬虫学习 | Scrapy框架详解

一.Scrapy框架简介 何为框架&#xff0c;就相当于一个封装了很多功能的结构体&#xff0c;它帮我们把主要的结构给搭建好了&#xff0c;我们只需往骨架里添加内容就行。scrapy框架是一个为了爬取网站数据&#xff0c;提取数据的框架&#xff0c;我们熟知爬虫总共有四大部分&am…...

用户态协议栈05—架构优化

优化部分 添加了in和out两个环形缓冲区&#xff0c;收到数据包后添加到in队列&#xff1b;经过消费者线程处理之后&#xff0c;将需要发送的数据包添加到out队列。添加数据包解析线程&#xff08;消费者线程&#xff09;&#xff0c;架构分层 #include <rte_eal.h> #inc…...

模拟退火算法

模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种用于全局优化问题的概率搜索算法&#xff0c;其灵感来自于金属退火过程。在金属退火中&#xff0c;材料被加热到高温&#xff0c;然后缓慢冷却&#xff0c;以减少其晶格中的缺陷并达到最小能量状态。模拟退火…...

Java匿名类

Java 匿名类是一种特殊的内部类&#xff0c;它没有名字&#xff0c;并且通常用来简化代码实现&#xff0c;尤其是在实现接口或者抽象类的实例时。匿名类可以在实例化时定义其行为&#xff0c;而不需要创建单独的类文件。 匿名类的特点 没有名字&#xff1a;匿名类是没有名字的…...

G7易流赋能化工物流,实现安全、环保与效率的共赢

近日&#xff0c;中国物流与采购联合会在古都西安举办了备受瞩目的第七届化工物流安全环保发展论坛。以"坚守安全底线&#xff0c;追求绿色发展&#xff0c;智能规划化工物流未来"为主题&#xff0c;该论坛吸引了众多政府部门、行业专家和企业代表的参与。G7易流作为…...

y=sin(2x)

函数 \( y \sin(2x) \) 是一个正弦函数&#xff0c;其中 \( x \) 是自变量&#xff0c;\( y \) 是因变量。这个函数描述了一个周期性波动的波形&#xff0c;其特点是&#xff1a; 1. **振幅**&#xff1a;正弦函数的振幅是 1&#xff0c;这意味着波形在 \( y \) 轴上的最大值…...

快捷方式(lnk)--加载HTA-CS上线

免责声明:本文仅做技术交流与学习... 目录 CS: HTA文档 文件托管 借助mshta.exe突破 本地生成lnk快捷方式: 非系统图标路径不同问题: 关于lnk的上线问题: CS: HTA文档 配置监听器 有效载荷---->HTA文档--->选择监听器--->选择powershell模式----> 默认生成一…...

从同—视角理解扩散模型(Understanding Diffusion Models A Unified Perspective)

从同—视角理解扩散模型 Understanding Diffusion Models A Unified Perspective【全公式推导】【免费视频讲解】 B站视频讲解 视频的论文笔记 从同一视角理解扩散模型【视频讲解笔记】 配合视频讲解的同步笔记。 整个系列完整的论文笔记内容如下&#xff0c;仅为了不用—一回复…...

docker 基本用法及跨平台使用

一、Docker的优点 docker 主要解决的问题就是程序开发过程中编译和部署中遇到的环境配置的问题。 1.1 Docker与其他虚拟机层次结构的区别** 运行程序重点关注点在于环境。 VM虚拟机是基于Hypervisor虚拟化服务运行的。 Docker是基于内核的虚拟化技术实现的。 1.2 Docker的技…...

Vscode远程ubuntu

远程连接 到这里vscode远程到ubuntu和关闭远程连接&#xff0c;已完成 配置python环境 在远程目录下新建.vscode隐藏文件夹&#xff0c;文件夹里新建一个 settings.json 文件&#xff0c; 先远程服务器看下conda下的python虚拟环境位置 settings.json位置及内容如下 测试pyt…...

SHA256 安全散列算法加速器实验

1、SHA256 介绍 SHA256 加速器是用来计算 SHA-256 的计算单元&#xff0c; SHA256 是 SHA-2 下细分出的一种算法。 SHA-2 名称来自于安全散列算法 2 &#xff08;英语&#xff1a; Secure Hash Algorithm 2 &#xff09;的缩写&#xff0c;一种密码散列函 数算法标准…...

Elasticsearch-ES查询单字段去重

ES 语句 整体数据 GET wkl_test/_search {"query": {"match_all": {}} }结果&#xff1a; {"took" : 123,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0…...

【Apache Doris】周FAQ集锦:第 7 期

【Apache Doris】周FAQ集锦&#xff1a;第 7 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户和…...

EE trade:炒伦敦金的注意事项及交易指南

在贵金属市场中&#xff0c;伦敦金因其高流动性和全球认可度&#xff0c;成为广大投资者的首选。然而&#xff0c;在炒伦敦金的过程中&#xff0c;投资者需要注意一些关键点。南华金业小编带您一起来看看。 国际黄金报价 一般国际黄金报价会提供三个价格&#xff1a; 买价(B…...

JAVA医院绩效考核系统源码 功能特点:大型医院绩效考核系统源码

JAVA医院绩效考核系统源码 功能特点&#xff1a;大型医院绩效考核系统源码 医院绩效管理系统主要用于对科室和岗位的工作量、工作质量、服务质量进行全面考核&#xff0c;并对科室绩效工资和岗位绩效工资进行核算的系统。医院绩效管理系统开发主要用到的管理工具有RBRVS、DRGS…...

Python神经影像数据的处理和分析库之nipy使用详解

概要 神经影像学(Neuroimaging)是神经科学中一个重要的分支,主要研究通过影像技术获取和分析大脑结构和功能的信息。nipy(Neuroimaging in Python)是一个强大的 Python 库,专门用于神经影像数据的处理和分析。nipy 提供了一系列工具和方法,帮助研究人员高效地处理神经影…...

非关系型数据库NoSQL数据层解决方案 之 Mongodb 简介 下载安装 springboot整合与读写操作

MongoDB 简介 MongoDB是一个开源的面向文档的NoSQL数据库&#xff0c;它采用了分布式文件存储的数据结构&#xff0c;是当前非常流行的数据库之一。 以下是MongoDB的主要特点和优势&#xff1a; 面向文档的存储&#xff1a; MongoDB是一个面向文档的数据库管理系统&#xff0…...

使用Redis优化Java应用的性能

使用Redis优化Java应用的性能 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们来探讨如何使用Redis优化Java应用的性能。Redis是一种开源的内存数据结构…...

基于Python的数据可视化大屏的设计与实现

基于Python的数据可视化大屏的设计与实现 Design and Implementation of Python-based Data Visualization Dashboard 完整下载链接:基于Python的数据可视化大屏的设计与实现 文章目录 基于Python的数据可视化大屏的设计与实现摘要第一章 导论1.1 研究背景1.2 研究目的1.3 研…...

什么是N卡和A卡?有什么区别?

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 本篇笔记整理&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、什么是N卡和A卡&#xff1f;有什么区别&#xff1f;…...

四边形不等式优化

四边形不等式优化 应用于类似以下dp转移方程。 f i min ⁡ 1 ≤ j ≤ i ( w i , j , f i ) f_{i}\min_{1\le j\le i}(w_{i,j},f_{i}) fi​1≤j≤imin​(wi,j​,fi​) 假设 w i , j w_{i,j} wi,j​ 可以在 O ( 1 ) O(1) O(1) 的时间内进行计算。 在正常情况下&#xff0c;…...

这家民营银行起诉担保公司?暴露担保增信兜底隐患

来源 | 镭射财经&#xff08;leishecaijing&#xff09; 助贷领域中&#xff0c;各路资方依赖担保增信业务扩张数年&#xff0c;其风险积压也不容忽视。一旦助贷平台或担保公司兜不住底&#xff0c;资方就将陷入被动。 最近&#xff0c;一则民营银行起诉合作担保公司的消息引…...

vscode禅模式怎么退出

1、如何进入禅模式&#xff1a;查看--外观--禅模式 2、退出禅模式 按二次ESC&#xff0c;就可以退出。...

Java23种设计模式(四)

1、备忘录模式 备忘录模式&#xff08;Memento Pattern&#xff09;保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象&#xff0c;备忘录模式属于行为型模式。 备忘录模式允许在不破坏封装性的前提下&#xff0c;捕获和恢复对象的内部状态。 实现方式 创建备忘录…...

HTML静态网页成品作业(HTML+CSS)——故宫介绍网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…...

Zookeeper:客户端命令行操作

文章目录 一、help二、ls path三、create四、get path五、set六、stat七、delete八、deleteall 一、help 显示所有操作命令。 二、ls path 使用ls命令来查看当前znode的子节点[可监听] w&#xff1a;监听子节点变化。s&#xff1a;附加次级信息。 三、create 普通创建&am…...

区块链技术介绍和用法

区块链技术是一种分布式账本技术&#xff0c;可以记录和存储一系列交易信息&#xff0c;并通过密码学算法保证信息的安全性和不可篡改性。区块链技术的核心概念是“区块”和“链”。 每个区块包含了一部分交易信息&#xff0c;以及一个指向上一个区块的哈希值。当新的交易发生…...

Upload-Labs-Linux1 使用 一句话木马

解题步骤&#xff1a; 1.新建一个php文件&#xff0c;编写内容&#xff1a; <?php eval($_REQUEST[123]) ?> 2.将编写好的php文件上传&#xff0c;但是发现被阻止&#xff0c;网站只能上传图片文件。 3.解决方法&#xff1a; 将php文件改为图片文件&#xff08;例…...

从 Hadoop 迁移,无需淘汰和替换

我们仍然惊讶于有如此多的客户来找我们&#xff0c;希望从HDFS迁移到现代对象存储&#xff0c;如MinIO。我们现在以为每个人都已经完成了过渡&#xff0c;但每周&#xff0c;我们都会与一个决定进行过渡的主要、高技术性组织交谈。 很多时候&#xff0c;在这些讨论中&#xff…...

深度学习:从理论到应用的全面解析

引言 深度学习作为人工智能&#xff08;AI&#xff09;的核心技术之一&#xff0c;在过去的十年中取得了显著的进展&#xff0c;并在许多领域中展示了其强大的应用潜力。本文将从理论基础出发&#xff0c;探讨深度学习的最新进展及其在各领域的应用&#xff0c;旨在为读者提供全…...

【02】区块链技术应用

区块链在金融、能源、医疗、贸易、支付结算、证券等众多领域有着广泛的应用&#xff0c;但是金融依旧是区块链最大且最为重要的应用领域。 1. 区块链技术在金融领域的应用 1.2 概况 自2019年以来&#xff0c;国家互联网信息办公室已发布八批境内区块链信息服务案例清单&#…...

一篇文章搞懂残差网络算法

残差网络(Residual Network,简称ResNet)是一种深度学习架构,它在2015年由微软研究院的Kaiming He等四位作者提出。ResNet的提出是为了解决深度神经网络训练中的梯度消失和梯度爆炸问题,以及随着网络层数增加而出现的性能退化问题。本文将详细介绍残差网络算法的定义、产生…...

网络安全:Web 安全 面试题.(SQL注入)

网络安全&#xff1a;Web 安全 面试题.&#xff08;SQL注入&#xff09; 网络安全面试是指在招聘过程中,面试官会针对应聘者的网络安全相关知识和技能进行评估和考察。这种面试通常包括以下几个方面&#xff1a; &#xff08;1&#xff09;基础知识:包括网络基础知识、操作系…...

XSS学习(绕过)

学习平台&#xff1a;xss.tesla-space.com XSS学习&#xff08;绕过&#xff09; level1level2level3level4level5level6level7level8level9level10level11level12level13level14 level1 应该没有过滤 https://xss.tesla-space.com/level1.php?name<script>alert(1);&…...

深信服2024笔试

一 &#xff1a;服务器 小明是一名公司的IT运维工程师&#xff0c;负责管理公司的IT系统。公司总共有两个配置相同的服务器A和B&#xff0c;各运行了若干个服务。现在小明发现两台服务器上运行的服务占用的内存总和不相等(假设每个服务占用内存是-个恒定正整数)&#xff0c;打…...

IOS Swift 从入门到精通:闭包 第一部分

文章目录 创建基本闭包在闭包中接受参数从闭包返回值闭包作为参数尾随闭包语法 创建基本闭包 Swift 允许我们像使用字符串和整数等其他类型一样使用函数。这意味着您可以创建一个函数并将其分配给一个变量&#xff0c;使用该变量调用该函数&#xff0c;甚至可以将该函数作为参…...

解两道四年级奥数题(等差数列)玩玩

1、1&#xff5e;200这200个连续自然数的全部数字之和是________。 2、2&#xff0c;4&#xff0c;6&#xff0c;……&#xff0c;2008这些偶数的所有各位数字之和是________。 这两道题算易错吧&#xff0c;这里求数字之和&#xff0c;比如124这个数的全部数字之和是1247。 …...

深入理解Python中的并发与异步的结合使用

​ 在上一篇文章中&#xff0c;我们讨论了异步编程中的性能优化技巧&#xff0c;并简单介绍了trio和curio库。今天&#xff0c;我们将深入探讨如何将并发编程与异步编程结合使用&#xff0c;并详细讲解如何利用trio和curio库优化异步编程中的性能。 文章目录 并发与异步编程的区…...

如何将 ChatGPT 集成到你的应用中

在当今快速发展的技术环境中&#xff0c;将人工智能聊天解决方案集成到你的应用程序中可以显著提升用户体验和参与度。OpenAI 的 ChatGPT 以其对话能力和高级语言理解而闻名&#xff0c;对于希望在其应用程序中实现智能聊天功能的开发人员来说是一个绝佳的选择。那我们今天就来…...

在 Swift 中,UILabel添加点击事件的方法

在 Swift 中&#xff0c;可以使用 UITapGestureRecognizer 给 UILabel 添加点击事件。以下是一个详细的步骤和示例代码&#xff1a; 1. 创建 UILabel 并添加到视图 在 Storyboard 或代码中创建一个 UILabel 并将其添加到视图中。 2. 启用 UILabel 的用户交互 默认情况下&am…...

indexedDB---掌握浏览器内建数据库的基本用法

1.认识indexedDB IndexedDB 是一个浏览器内建的数据库&#xff0c;它可以存放对象格式的数据&#xff0c;类似本地存储localstore&#xff0c;但是相比localStore 10MB的存储量&#xff0c;indexedDB可存储的数据量远超过这个数值&#xff0c;具体是多少呢&#xff1f; 默认情…...

【css】如何修改input选中历史选项后,自动填充的蓝色背景色

自动填充前&#xff1a; 自动填充后&#xff1a; 解决办法 方法一&#xff1a;设置背景透明&#xff08;通过拉长过渡时间&#xff0c;和延迟过渡开始时间&#xff0c;掩盖input自动填充背景颜色&#xff09; PS&#xff1a;注意&#xff0c;这个过渡效果会在你的delay tim…...

红队内网攻防渗透:内网渗透之内网对抗:网络通讯篇防火墙组策略入站和出站规则单层双层C2正反向上线解决方案

红队内网攻防渗透 1. 内网网络通讯1.1 防火墙策略-入站规则&出站规则&自定义1.1.1 防火墙默认入站&出站策略1.1.2 防火墙自定义入站&出站策略1.1.3 内网域防火墙同步策略1.2 防火墙限制1.2.1 防火墙限制端口1.2.2 防火墙限制协议1.2.2.1 防火墙协议入站限制1.2…...

linux 查看进程启动方式

目录 如果是systemd管理的服务怎么快速找到对应的服务器呢 什么是CGroup 查找进程对应的systemd服务 方法一&#xff1a;查看 /proc//cgroup 文件 方法二&#xff1a;使用 ps 命令结合 --cgroup 选项 方法三&#xff1a;systemd-cgls 关于 system.slice 与 user.slice …...

基于Java实训中心管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…...

第2章 Android应用的界面编程

&#x1f308;个人主页&#xff1a;小新_- &#x1f388;个人座右铭&#xff1a;“成功者不是从不失败的人&#xff0c;而是从不放弃的人&#xff01;”&#x1f388; &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f3c6;所属专栏&#xff1…...

springboot学习-图灵课堂-最详细学习

springboot-repeat springBoot学习代码说明为什么java -jar springJar包后项目就可以启动 配置文件介绍 springBoot学习 依赖引入 <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>8</mav…...

Total CAD Converter与Total Excel Converter软件分享

1.软件介绍 Total CAD Converter Total CAD Converter 是一款功能强大的工具&#xff0c;能够将 CAD 文件转换为多种格式&#xff0c;如 PDF、TIFF、JPEG、BMP、WMF、PNG、DXF、BMP、CGM、HPGL、SVG、PS 和 SWF 等。其支持的源格式丰富多样&#xff0c;包括 dxf、dwg、dwf、d…...

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 启动多任务排序(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 启动多任务排序(200分) 🌍 评测功能需要订阅专栏后私信联系…...

【会议征稿,JPCS出版】第三届电力系统与能源技术国际学术会议(ICPSET 2024,7月5-7)

第三届电力系统与能源技术国际学术会议&#xff08;ICPSET 2024&#xff09;将于2024年7月5-7日在杭州举办。由浙江水利水电学院电机产业学院主办&#xff0c;AEIC学术交流中心承办&#xff0c;湖州市南浔创新研究院、南浔区科技局&#xff08;科协&#xff09;协办 。会议主要…...