python爬虫正则表达式

news2024/11/23 10:29:48

博主简介:博主是一个大二学生,主攻人工智能领域研究。感谢缘分让我们在CSDN相遇,博主致力于在这里分享关于人工智能,C++,python,爬虫等方面的知识分享。如果有需要的小伙伴,可以关注博主,博主会继续更新的,如果有错误之处,大家可以指正。

专栏简介:本专栏致力于研究python爬虫的实战,涉及了所有的爬虫基础知识,以及爬虫在人工智能方面的应用。文章增加了JavaScript逆向,网站加密和混淆技术,AST还原混淆代码,WebAssembly,APP自动化爬取,Android逆向等相关技术。同时,为了迎合云原生发展,同时也增加了基于Kubernetes,Docker,Prometheus,Grafana等云原生技术的爬虫管理和运维解决方案。

订阅专栏    订阅专栏

博主分享:给大家分享一句我很喜欢的话:“每天多一点努力,不为别的,值为日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!”


目录

正则表达式

匹配规则

函数进阶

1.match函数

search函数

 findall函数

sub函数

compile函数

总结

httpx的使用

基础爬虫案例实战

准备工作

实验目的

代码实现

获取详情页URL

爬取详情页 

源代码

总结



正则表达式

前面我们说了如何使用requests去获取网页的HTML代码,但是我们往往想要的是包含在这些代码中的资源,正则表达式是处理字符串的常用工具,在JAVA,C++等语言中均有他们的身影,有了正则表达式,可以很方便的处理很多事情。下面我们来看看正则表达式:

匹配规则

正则表达式中有很多的匹配规则,也就是我们说的他有他自己的语法。要想学好正则表达式,记住匹配规则是很重要的。

常用的匹配规则
                模式                                描述
\w匹配字母,数字即下划线字符
\W匹配非字母,数字即下划线的字符
\s匹配任意空白字符,等价于[\t\n\r\f]
\S匹配任意非空字符
\d匹配任意数字,等价于[0~9]
\D匹配任意非数字的字符
\A匹配字符串开头
\z匹配到字符串结尾,加上换行符
\Z匹配到字符串结尾,如果遇到换行符,则只匹配到换行符前一个字符
\G匹配最后完成匹配的位置
\n匹配换行符
\t匹配一个制表符
^匹配一行字符串的开头
$匹配一行字符串的结尾
.匹配任意字符,除了换行符,当re.DOTALL标记被指定时,可以匹配包括换行符的任意字符
[...]用来表示一组字符,单独列出,例如[amk],匹配a,m或k。
[^...]匹配不在[]中的字符,即表示除了里面的字符,其他的都可以。
*匹配0个或多个表达式
+匹配一个或多个表达式
匹配0个或1个前面你定义的正则表达式片段,非贪婪方式
{n}精确匹配n个前面的表达式
{n,m}匹配n到m次由前面正则表达式定义的片段,贪婪方式
a|b匹配a或b
()匹配括号内的表达式,也表示一个组

这个表中的东西很多,看完可能会出现记忆混乱,后买根据我提供的示例就很容易弄清了,python的强大在于它有很多库,当然,正则表达式也提供了一个库re。re中有很多的函数用来正则表达式匹配。下面我们认识一下他的函数。

函数进阶

1.match函数

match函数是一个常用的匹配方法,match方法会尝试从字符串的起始位置开始匹配正则表达式,如果匹配,就返回匹配结果,否则,返回None。

import re
str='Hello everyone,My name is chinese.My phonenumber is 1234567890 '
pattern='^Hello\s.{46}\d{10}'
result=re.match(pattern,str)
print(result.group())

运行结果:

Hello everyone,My name is chinese.My phonenumber is 1234567890

当然,匹配方式不只有这些,还有其他方式。我们在代码中使用了group()和span()方法(这里没有用,后面会使用,先介绍),group()方法是输出匹配的内容,span()则是输出匹配的范围。

前面介绍该函数的时候,博主说该函数是从头开始匹配,如果我们开始匹配错误,后面匹配正确,那能不能的到结果?下面我们来看看:


import re
str='Hello word!I like you'
pattern='^word.\w{10}'
result=re.match(pattern,str)
print(result.span())
print(result.group())

 我们会发现匹配出了错误,虽然后面的匹配规则正确,但是开头匹配错误,所以这也是match函数的一大特性,为了,解决这一问题,re库也推出了其他的函数,后面会介绍。现在言归正传,如果说我们匹配后获得的内容不是我们想要的,也就是我们只想要其中的某个片段,我们又该如何选择?

import re
str='Hello 1234567 I like Word'
pattern='^Hello\s(\d+)'
result=re.match(pattern,str)
print(result.group())
print(result.group(1))
print(result.span())

 结果展示:

Hello 1234567

1234567

(0, 13)

根据结果我们可以知道group()是将所有的匹配结果都展现出来,group(1)则是将第一个()中的匹配法则所对应的内容展现出来。假如正则表达式中还有其他()包裹起来的内容,那么就用group(2),group(3)来输出。

通用匹配

上面我们用\d,\s等匹配法则来匹配,但是这样会显得很麻烦,不同的字符需要用不同的匹配法则,有没有什么简单的搭配?

我们这里认识以下通用匹配.*匹配。.表示匹配任意字符,*则表示无限次匹配前面的字符。所以他们在一起就可以匹配任意字符。

import re
str='Hello 1234567 I like Word'
pattern='^Hello.*Word$'
result=re.match(pattern,str)
print(result.group())
print(result.span())

Hello 1234567 I like Word

(0, 25)

贪婪与非贪婪 

 上面介绍的通用匹配虽然很简单,但是如果我们要取中间的数字该怎么办,或者说要获取其中的某个片段,那我们用括号进行分组是否可以?


import re
str='Hello 1234567 I like Word'
pattern='^Hello.*(\d+).*Word$'
result=re.match(pattern,str)
print(result.group())
print(result.group(1))
print(result.span())

Hello 1234567 I like Word

7

(0, 25)

根据运行代码结果展示,我们会发现只有一个7,这里就涉及到了贪婪和非贪婪匹配的问题,.*会一直匹配,直到最后一个或多个符合后面的匹配法则的字符。但是这样非常的不方便。如果想解决这个问题,那就使用.*?非贪婪匹配。


import re
str='Hello 1234567 I like Word'
pattern='.*?(\d+).*Word$'
result=re.match(pattern,str)
print(result.group())
print(result.group(1))


Hello 1234567 I like Word

1234567

使用非贪婪是不是就很简单,不需要写太多。非贪婪在匹配的时候只会匹配0个或1个前面的字符匹配,他是尽可能的少匹配,而贪婪匹配则是尽可能多的匹配,就上面的例子,由于贪婪匹配后面是(\d+)表示至少有一个数字,所以也就只有一个7.而非贪婪匹配只要检测到后面的字符与匹配法则符合 就停止匹配,交给后面的匹配法则匹配。

但是要注意的是,如果在字符串后面,很可能出现无法匹配。


import re
str='Hello 1234567 I like Word'
pattern1='.*?(\d+)(.*?)'
pattern2='.*?(\d+)(.*)'
result1=re.match(pattern1,str)
result2=re.match(pattern2,str)
print(result1.group(2))
print('---------------')
print(result2.group(2))

---------------

I like Word

这时我们发现只有蝶绕个结果,第一个为空,这是因为第一个为空格,他是尽可能少匹配。后面没有匹配准则,他就不匹配。

修饰符

上面的字符串我们都没有换行,只是单独一行,那如果我们是多行匹配,那么会出现什么情况?


import re
str='''Hello 1234567
 I like Word
'''
pattern2='.*?(\d+)(.*)'
result2=re.match(pattern2,str)
print('---------------')
print(result2.group(2))

 这个时候得到的结果是空,如果使用group()则会报错。而这个时候我们只需要加一个re.S就可以了。

result2=re.match(pattern2,str,re.S)

这里使用的re.S就是修饰符,除了这个还有其他的修饰符:

修饰符
修饰符描述
re.I是匹配对大小写不敏感
re.L实现本地化识别(locale-aware)匹配
re.M多行匹配,影响^,$匹配。
re.S使匹配内容包括换行符在内的所有字符
re.U根据Unicode字符集解析字符,会影响\w,\W,\b,\B
re.X该标志能够给你更灵活的格式,以便将正则表达式写得更清楚

转义匹配

我们知道正则表达式中定义了很多匹配模式,如.用于匹配除换行符以外的任意字符。但如果目标字符串里面就包含.这个字符,那该怎么办?

import re
content='(百度)www.baidu.com'
result=re.match('\(百度\)www\.baidu\.com',content)
print(result)

当在目标字符串中遇到用作正则匹配模式的特殊字符时,在此字符前面加反斜杠\转义一下即可。例如\.就可以用来匹配.。

search函数

不知道看到这里的小伙伴是否还记得match函数的一个特性就是match函数的匹配是从第一个开始,也就是从头开始,也就是意味着一旦开头不匹配就会失败,前面已经做过实验了。有时候这样会很复杂,所以,python推出了search函数。

search函数是从字符串开头扫描,直到和匹配规则重合的字符串出现,然后返回第一个符合条件的字符串。


import re
str='Hello Word 123 Word 123 hujuijcbv'
content='\d+'
print(re.search(content,str).group())

此时代码的返回结果就是123,但是字符串中有两个123,这里却只显示一个,原因就是search函数只返回第一个匹配的内容。由于HTML代码中有很多的节点,所以爬虫建议使用search函数,使用match函数工作量会非常大,消耗的时间也会多。综合以上,使用search函数较好。但是也有各例,具体情况具体分析。

 findall函数

前面的search函数只能返回第一个匹配的字符串,如果我们要获取多个相同规则的字符串又该如何?这个时候就要用到findall函数。findall函数的返回值为列表类型,所以需要使用遍历的方式获取里面的内容:

import re
str='Hello Word 123 Word 123 hujuijcbv,123 456!'
content='\d+'
result=re.findall(content,str)
for x in result:
    print(x)

123

123

123

456

sub函数

除了使用正则表达式提取信息,有时候还需要他来修改文本,虽然python中提供了replace函数,但是,这样很繁琐,re库中的sub函数就能很简单的实现:

例:将Hello Word 123 Word 123 hujuijcbv,123 456!字符串中的数字转换为感叹号或者去掉,我们怎么去操作?


import re
str='Hello Word 123 Word 123 hujuijcbv,123 456!'
content='\d+'
result=re.sub(content,'!',str)
print(result)

Hello Word ! Word ! hujuijcbv,! !! 

显然使用sub函数就很简单。sub函数的第一个参数是要改变的字符串或字符,第二个参数是用来替换要改变的的字符或字符串 ,第三个参数是原始字符串。

compile函数

前面讲的函数都是用来处理字符串的,compile函数则是用来处理正则表达式的,将字符串编译成正则表达式对象,以便在后面的匹配中重复使用。如下:


import re
content1='2002 03 29 13:45'
content2='2003 05 31 14:55'
content3='2022 04 04 21:07'
pattern='\d{2}:\d{2}'
result1=re.sub(pattern,'',content1)
result2=re.sub(pattern,'',content2)
result3=re.sub(pattern,'',content3)
print(result1,'\n',result2,'\n',result3)

2002 03 29

2003 05 31

2022 04 04

利用compile函数可以构造一个正则表达式对象,这样会减少工作量,同时,也可以在构造对象的时候添加修饰符,这样就不用在函数中写了。后面的代码基本上就会这样去写。

总结

到此为止,正则表达式部分就介绍完毕,后面博主会写一些实例来巩固这些方法。

httpx的使用

前面我们介绍了urllib库和requests库,现在来说,掌握这两个库就可以爬很多的网站了,但是对于一些网站还是无能为力,这是为什么?我们前面讲过HTTP存在http1和HTTP2两个版本,目前urllib和requests库只支持爬取http1.1版本,不支持HTTP2版本。目前来说有hyper和httpx两个库支持爬取http2版本,相对来说,后者的功能更强大,博主也更喜欢后者,所以就介绍一下httpx的使用。

httpx模块不是python的内置模块,需要下载,如果直接使用命令安装,则也不支持http2版本。可以这样安装:

pip install "httpx[http2]" -i https://pypi.tuna.tsinghua.edu.cn/simple/

 下载好后是不是就可以使用了?我们来验证一下:

import httpx
print(httpx.get('https://spa16.scrape.center').text)

我们发现报错了,这是由于httpx默认支持http1.1版本,这个时候需要我们手动打开http2版本:


#httpx的使用
import httpx
client=httpx.Client(http2=True)
response=client.get('https://spa16.scrape.center')
print(response.text)

   client对象

httpx中的很多的API都和requests中的相似,但是有一些requests中没有,比如client对象,可以和requests中的session类比学习。


import httpx
with httpx.Client() as client:
    response=client.get('https://www.baidu.com')
print(response)
#等价于
import httpx
client=httpx.Client()
try:
    response=client.get('https://www.baidu.com')
finally:
    client.close()

注意:在客户端上启用的httpx支持http2版本并不意味着请求和响应都将通过http2.0版本传输,还得客户端和服务器都支持,如果客户端京仅连接到支持http1.1版本的服务器,那么相应的,httpx也必须改为http1.1。

 支持异步请求

httpx还支持异步客户端请求(AsyncClient),支持python的async请求模式。写法如下:


import asyncio
import httpx
import nest_asyncio
nest_asyncio.apply()
async def fetch(url):
    async with httpx.AsyncClient(http2=True) as client:
        response=await client.get(url)
        print(response.text)
if __name__=='__main__':
    asyncio.get_event_loop().run_until_complete(fetch('https://www.httpbin.org/get'))

由于博主使用的是VScode编译器,使用的式python内核,所以,这里直接使用异步请求是无法通过的,因为python本身就是不能进行事件循环的。所以有些编译器可以使用,博主这里不可以,就不展示运行结果了。


基础爬虫案例实战

准备工作

1.安装好python,以及涉及到的库。

2.了解python的多线程进程原理

此次要爬取的网页的地址:‘https://ssr1.scrape.center/’

实验目的

对前面所学的知识进行一个总结,巩固所学知识,为后面的学习做好铺垫。

代码实现

获取详情页URL

这里我们引入requests库来爬取页面,logging用来输出信息,re库用来实现正则表达式的解析。urljoin用来做URL拼接。

第一步,我们先定义日志输出级别和输出格式,以及BASE_URL为当前站点的根URL;TOTAL_PACE表示爬取的页面数量。

import logging
import requests
import re
from urllib.parse import urljoin
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s:%(message)s')
BASE_URL='https://ssr1.scrape.center'
TOTAL_PACE=10

第二步,我们实现页面爬取的方法


def scrape_page(url):
    logging.info('scraping %s...',url)
    try:
        response=requests.get(url)
        if response.status_code==200:
            return response.text
        logging.error('get invalid status code %s while scraping %s',response.status_code,url)
    except requests.RequestException:
        logging.error('error occurred while scraping %s',url,exc_info=True)

由于不仅要爬取列表页,还要爬取详情页,所以我们这里定义了一个较通用的防范,最终得到的是HTML源代码。

第三步,定义列表页爬取方法


#列表页爬取
def scrape_index(page):
    index_url=f'{BASE_URL}/page/{page}'
    return scrape_page(index_url)

 获取列表页后,下一步就是解析列表页:

第四步,解析列表页:


#解析列表
def parse_index(html):
    pattern=re.compile('<a.*?href="(.*?)".*?class="name">')
    items=re.findall(pattern,html)
    if not items:
        return[]
    for item in items:
        detail_url=urljoin(BASE_URL,item)
        logging.info('get detail url %s',detail_url)
        yield detail_url  #生成器

现在,所有的函数都已经实现,现在就是串用:

#主函数
def main():
    for page in range(1,TOTAL_PACE):
        index_html=scrape_index(page)
        detail_urls=parse_index(index_html)
        logging.info('detail urls %s',list(detail_urls))
if __name__=='__main__':
    main()

 第五步,运行

由于结果太多,这里就只展示一小部分,现在我们获得了每一页的详情页的URL,那么我们接下来要做的就是解析这些URL。

爬取详情页 

观察每个子页面的代码,我们可以找出他们的特点,由于每个人的观察点不一样,所以这里就以博主的观察点为主:

定义一个详情页的爬取方法:


#定义一个详情页的爬取方法
def scrape_detail(url):
    return scrape_page(url)

然后就是对详情页进行解析:


#对详情页进行解析
def parse_detail(html):
    cover_pattern=re.compile('class="item.*?<img.*?src="(.8?)".*?class="cover">',re.S)
    name_pattern=re.compile('<h2.*?>(.*?)</h2>')
    categories_pattern=re.compile('<button.*?category.*?<span>(.*?)\
                                  </span>.*?</button>',re.S)
    published_at_pattern=re.compile('(\d{4}-\d{2}-\d{2})\s?上映')
    drama_pattern=re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)
    score_pattern=re.compile('<p.*?score.*?>(.*?)</p>',re.S)
    cover=re.search(cover_pattern,html).group(1).strip() if re.search(cover_pattern,html) else None
    name=re.search(name_pattern,html).group(1).strip() if re.search(name_pattern,html) else None
    categories=re.findall(categories_pattern,html) if re.findall(categories_pattern,html) else []
    published_at=re.search(published_at_pattern,html).group(1) if re.search(published_at_pattern,html) else None
    drama=re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
    score=float(re.search(score_pattern,html).group(1).strip() if re.search(score_pattern,html) else None)
    return{
        'cover':cover,
        'name':name,
        'categories':categories,
        'published_at':published_at,
        'drama':drama,
        'score':score

    }

最后一步,保存数据。

#保存数据
RESULTS_DIR='results'
exists(RESULTS_DIR) or makedirs(RESULTS_DIR)
def save_data(data):
    name=data.get('name')
    data_path=f'{RESULTS_DIR}/{name}.json'
    json.dump(data,open(data_path,'w',encoding='utf-8'),ensure_ascii=False,indent=2)

 到这里,所有的函数均已完成,现在就是组合起来使用:

源代码

#爬虫案例实战
import json
from os import makedirs
from os.path import exists
import logging
import requests
import re
from urllib.parse import urljoin
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s:%(message)s')
BASE_URL='https://ssr1.scrape.center'
TOTAL_PACE=10
#页面爬取
def scrape_page(url):
    logging.info('scraping %s...',url)
    try:
        response=requests.get(url)
        if response.status_code==200:
            return response.text
        logging.error('get invalid status code %s while scraping %s',response.status_code,url)
    except requests.RequestException:
        logging.error('error occurred while scraping %s',url,exc_info=True)


#列表页爬取
def scrape_index(page):
    index_url=f'{BASE_URL}/page/{page}'
    return scrape_page(index_url)

#解析列表
def parse_index(html):
    pattern=re.compile('<a.*?href="(.*?)".*?class="name">')
    items=re.findall(pattern,html)
    if not items:
        return[]
    for item in items:
        detail_url=urljoin(BASE_URL,item)
        logging.info('get detail url %s',detail_url)
        yield detail_url  #生成器

#定义一个详情页的爬取方法
def scrape_detail(url):
    return scrape_page(url)

#对详情页进行解析
def parse_detail(html):
    cover_pattern=re.compile('class="item.*?<img.*?src="(.8?)".*?class="cover">',re.S)
    name_pattern=re.compile('<h2.*?>(.*?)</h2>')
    categories_pattern=re.compile('<button.*?category.*?<span>(.*?)\
                                  </span>.*?</button>',re.S)
    published_at_pattern=re.compile('(\d{4}-\d{2}-\d{2})\s?上映')
    drama_pattern=re.compile('<div.*?drama.*?>.*?<p.*?>(.*?)</p>',re.S)
    score_pattern=re.compile('<p.*?score.*?>(.*?)</p>',re.S)
    cover=re.search(cover_pattern,html).group(1).strip() if re.search(cover_pattern,html) else None
    name=re.search(name_pattern,html).group(1).strip() if re.search(name_pattern,html) else None
    categories=re.findall(categories_pattern,html) if re.findall(categories_pattern,html) else []
    published_at=re.search(published_at_pattern,html).group(1) if re.search(published_at_pattern,html) else None
    drama=re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
    score=float(re.search(score_pattern,html).group(1).strip() if re.search(score_pattern,html) else None)
    return{
        'cover':cover,
        'name':name,
        'categories':categories,
        'published_at':published_at,
        'drama':drama,
        'score':score

    }
#保存数据
RESULTS_DIR='results'
exists(RESULTS_DIR) or makedirs(RESULTS_DIR)
def save_data(data):
    name=data.get('name')
    data_path=f'{RESULTS_DIR}/{name}.json'
    json.dump(data,open(data_path,'w',encoding='utf-8'),ensure_ascii=False,indent=2)
#主函数
def main():
    for page in range(1,TOTAL_PACE):
        index_html=scrape_index(page)
        detail_urls=parse_index(index_html)
        for detail_url in detail_urls:
            detail_html=scrape_detail(detail_url)
            data=parse_detail(detail_html)
            logging.info('get detail data%s',data)
            logging.info('saving data to json file')
            save_data(data)
            logging.info('data saved successfully')

if __name__=='__main__':
    main()

结果展示:

总结

本篇文章讲解了正则表达式,以及如何去爬取网页上的有用信息,文末增加了一个示例,目的就是让大家更能理解知识。也希望这个案例能够让大家对爬虫有所认知和体会。

点赞加关注不迷路

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零碎的算法笔记(1)

From算法竞赛入门经典 第2版1.判断 n 是否为完全平方数2. 比较大的数组应尽量声明在 main 函数外&#xff0c;否则程序可能无法运行 3.开灯问题1.判断 n 是否为完全平方数 可以先求出其平方根&#xff0c;然后看它是否为整数&#xff0c;即用一个 int 型变量 m 存储 sqrt(n&am…

C++入门(二)

C入门&#xff08;二&#xff09;1.引用1.1引用概念1.2引用特性1.3常应用1.4使用场景1.5传值、传引用效率比较1.6引用和指针的区别2.内联函数2.1概念2.2内联函数的特性3.auto关键字&#xff08;C11&#xff09;3.1auto使用细则4.基于范围的for循环&#xff08;C11&#xff09;5…

题库——“C”

由于小雅兰的C语言程序设计考试的时间实在是越来越近了&#xff0c;那么&#xff0c;这篇博客也就产生了&#xff0c;这篇博客的主要内容是一些C语言程序设计的练习题&#xff0c;现在&#xff0c;就让我们一起进入C语言的世界吧。 1.函数fun的功能是&#xff1a;将形参n中&…

Java Agent 内存马攻防

前言 在 jdk 1.5 之后引入了 java.lang.instrument 包&#xff0c;该包提供了检测 java 程序的 Api&#xff0c;用于监控、收集性能信息、诊断问题等。通过 java.lang.instrument 实现的工具我们称之为 Java Agent &#xff0c;Java Agent 能够在不影响正常编译的情况下来修改…

黑马主播单场带货千万,抖音直播市场还有哪些新机遇?

1月受春节假期影响&#xff0c;主播带货场次降低&#xff0c;抖音直播电商市场略显低迷&#xff0c;据新抖『直播带货风向』大盘数据显示&#xff0c;1月平台直播销量与去年12月相比下降39.55%&#xff0c;直播销售额下降也较为明显。了解更多行业带货风向&#xff0c;可点此处…

【C++11】可变参数模板

目录 可变参数模板 函数递归展开参数包 逗号表达式展开参数包 emplace_back 可变参数模板 其实C语言中我们就一直在使用可变参数列表。 C11 的新特性可变参数模板能够让我们创建可变参数的函数模板和类模板&#xff0c;相比C98和C03&#xff0c;类模板和函数模板中只能传入…

19《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》中文分享

《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》 本人能力有限&#xff0c;如果错误欢迎批评指正。 第四章&#xff1a;Protein Binding Leads to Biological Actions &#xff08;蛋白质的结合会产生生物作用&#xff09; -布朗棘轮在耦合结合的过…

独角兽大牛纯手肛出的一份多线程实战文档,莫把它丢进收藏夹吃灰

为什么用多线程&#xff1f; 在操作系统级别上&#xff0c;程序的执行都是以进程为单位&#xff0c;而每一个进程中通常都会有多个线程线程互不影响地并发执行&#xff0c;那么为什么要使用多线程呢&#xff1f;其实&#xff0c;多线程的使用为程序研发带来了巨大的便利&#…

删除重复数字的三种方法(详解)

前言&#xff1a;本期是关于去重数字的三种方法详解&#xff0c;今天你c了吗&#xff1f; 输入描述&#xff1a; 第一行&#xff0c;输入一个整数n&#xff0c;表示序列有n个整数。 第二行输入n个整数&#xff08;每个整数大于等于1&#xff0c;小于等于1000&#xff09;&…

关于模板字面量,我有点好奇它的内部结构

前言 最近翻看源码的时候&#xff0c;发现一些有趣的 JS 的知识点&#xff0c;基于日常的开发经验&#xff0c;我做了一些联想和对比。整个过程充满了乐趣。 于是我想&#xff0c;是不是可以延续这种创意带来的学习的乐趣。 带的富含创造力夜晚的 buff&#xff0c;确实让我拥…

一、系统编程常用api

一、文件io文件io是linux系统提供的接口&#xff0c;针对文件和磁盘进行操作&#xff0c;不带缓存机制标准io是c语言函数库里的标准io模型&#xff0c;在stdio.h中定义&#xff0c;通过缓冲区操作文件&#xff0c;带有缓存机制标准 IO 和文件 IO 的区别如下图所示&#xff1a;文…

SSD(固态) VS HDD(机械),购买指南

大多数人买电脑的时候纠结买固态硬盘&#xff08;SSD&#xff09;还是机械硬盘(HDD)。哪个是最佳选择呢&#xff0c;是固态硬盘还是机械硬盘呢&#xff1f;这个问题没有直接的答案&#xff0c;每个购买者有不同的需求&#xff0c; 需要根据需求做选择。这些需求像是性能、和预算…

DS18B20测量温度液晶1602显示

DS18B20温度传感器简介DS18B20是一种数字温度传感器。它输出的是数字信号&#xff0c;同时具有体积小&#xff0c;硬件资源耗费少&#xff0c;抗干扰能力强&#xff0c;精度高等特点。DS18B20温度传感器特点1、单线接口&#xff1a;DS18B20仅需一条线可实现与微处理器双向通信。…

泰山服务器板载 HNS3 网卡绑核无法充分利用 CPU 的解决思路

文章目录前言解决方案排查过程应用程序运行环境与方式检查是否存在 irqbalance 进程检查中断号对应的 CPU 亲和尝试其他绑核方式尝试调整队列数量:核心数量为 2:1前言 前段时间在泰山服务器上进行性能测试&#xff0c;预期是应用进程能够占满机器大部分 CPU。但实际上&#xf…

书城第二阶段——用户注册和登陆

目录0.0 JavaEE 项目的三层架构0.1 项目阶段二&#xff1a;用户注册和登陆的实现。1、先创建书城需要的数据库和表。2、编写数据库表对应的JavaBean对象。3、编写工具类 JdbcUtils3.1、导入需要的 jar 包&#xff08;数据库和连接池需要&#xff09;&#xff1a;3.2、在 src 源…

钉钉一键登录第三方网站

钉钉一键登录第三方网站序钉钉开发者后台H5微应用应用代码开发登录页面login.html登录实现LoginController.javapom.xml增加一键登录效果展示序 企业内部系统已经做过了钉钉扫码登录&#xff0c;现在需要添加钉钉一键登录第三方网站功能&#xff0c;这里主要记录一键登录整个实…

物联网终端设备的工作原理和功能讲解

物联网终端设备是实现数据采集和数据传输的设备&#xff0c;它连接了传感网络层和传输网络层&#xff0c;起到了数据采集、数据处理、数据加密和传输的功能。 物联网终端设备由传感器、外部传感接口、CPU和外部通讯组成&#xff0c;工作原理是通过外部传感接口与传感设备连接&a…

和平精英军需精打细算天花板,330拿下一整套军需

和平精英军需精打细算天花板&#xff0c;330拿下一整套军需&#xff01; #和平精英 #这游戏不花钱 #游戏凡星计划 军需精打细算天花板&#xff0c;一个月时间花 110 块获得 436 个军需币。拿个新军需的副套问题不大。要知道和平小店的暖弄大礼包&#xff0c; 100 左右&#…

一次在 classpath 使用通配符导致的偶发问题排查与建议

说起 Classpath&#xff0c;使用 Java 的同学应该都不会陌生。不过&#xff0c;目前的项目基本都会使用 Maven 等构建工具管理&#xff0c;开发过程中也会使用高度智能化的 IDE&#xff0c;在日常使用中直接涉及 Classpath 操作可能不多。前段时间遇到一个跟 Classpath 相关的偶…

【My Electronic Notes系列——触发器】

目录 序言&#xff1a; &#x1f3c6;&#x1f3c6;人生在世&#xff0c;成功并非易事&#xff0c;他需要破茧而出的决心&#xff0c;他需要永不放弃的信念&#xff0c;他需要水滴石穿的坚持&#xff0c;他需要自强不息的勇气&#xff0c;他需要无畏无惧的凛然。要想成功&…