目录
2.1 re.match(pattern,string,flags=0)
2.2 re.search(pattern,string,flags=0)
2.3 re.findall(pattern,string,flags=0)
2.4 re.finditer(pattern,string,flags=0)
2.5 re.split(pattern,string,maxsplit=0,flags=0)
2.6 re.sub(pattern,repl,string,count=0,flags=0)
2.7 re.compile(pattern,flags=0)
枯燥学习好几天,今天就拿即将学习的知识中的案例来提升一下学习的热情,百度资讯搜一波热词“罗志祥”,参考《Python金融大数据挖掘与分析》书中的案例:
# -*- coding: utf-8 -*-
# @Time : 2020/4/23 16:37
# @Author : ChengYu
# @File : requests_get.py
import requests
import re
# 加上headers用来告诉网站这是通过一个浏览器进行的访问
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/81.0.4044.122 Safari/537.36'}
url = 'https://www.baidu.com/s?tn=news&rtt=1&bsst=1&cl=2&wd=罗志祥'
res = requests.get(url, headers=headers).text
print(res) # 输出网页源码
p_info = '<p class="c-author">(.*?)</p>'
# 查找符合匹配规则的文本,re.S是用于匹配换行的修饰符,如果源文本有换行需要它来准确获取匹配内容
info = re.findall(p_info, res, re.S)
p_href = '<h3 class="c-title">.*?<a href="(.*?)"'
href = re.findall(p_href, res, re.S)
p_title = '<h3 class="c-title">.*?>(.*?)</a>'
title = re.findall(p_title, res, re.S)
# 先创建两个空列表来储存等会分割后的来源和日期
source = []
date = []
for i in range(len(info)):
title[i] = title[i].strip() # 去除字符串中多余的空格和换行符
title[i] = re.sub('<.*?>', '', title[i])
info[i] = re.sub('<.*?>', '', info[i])
source.append(info[i].split(' ')[0]) # append()整理分割后的来源和日期
date.append(info[i].split(' ')[1])
source[i] = source[i].strip()
date[i] = date[i].strip()
print(str(i + 1) + '.' + title[i] + '(' + date[i] + '-' + source[i] + ')')
print(href[i])
-
爬取的部分网页源码
-
提取新闻标题、网址、时间及来源
虽一惯秉承合格吃瓜别较真,未知全貌不予置评;但生而为人皆“眼里有尺,心中有秤”,“朱碧石”的滤镜也救不了令人咋舌的作:贵圈真乱。
1. Requests库
关于Requests库的常用方法:https://requests.readthedocs.io/en/latest/user/quickstart/
高级用法:https://requests.readthedocs.io/en/latest/user/advanced/
1.1 GET请求
构建简单的GET请求,返回请求信息:
>>> import requests
>>> r = requests.get('https://httpbin.org/get')
>>> print(r.text)
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.23.0",
"X-Amzn-Trace-Id": "Root=1-5ea5010f-b5e6a06af401bcbd34004025"
},
"origin": "120.234.135.254",
"url": "https://httpbin.org/get"
}
1.2 POST请求
构建POST请求,返回请求信息:
>>> import requests
>>> r = requests.post('https://httpbin.org/post', data = {'name': 'chengyu', 'age': '18'})
>>> print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"age": "18",
"name": "chengyu"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "19",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.23.0",
"X-Amzn-Trace-Id": "Root=1-5ea50411-d0733e172c7cbc49985b1dc6"
},
"json": null,
"origin": "120.234.135.254",
"url": "https://httpbin.org/post"
}
其他HTTP请求类型:PUT,DELETE,HEAD和OPTIONS:
>>> r = requests.put('https://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('https://httpbin.org/delete')
>>> r = requests.head('https://httpbin.org/get')
>>> r = requests.options('https://httpbin.org/get')
1.3 Session
通过Session对象,可以在请求中保留某些参数,它还会在来自Session实例的所有请求中保留cookie,并将使用urllib3
的连接池。
>>> import requests
>>> s = requests.Session()
>>> s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
<Response [200]>
>>> r = s.get('https://httpbin.org/cookies')
>>> print(r.text)
{
"cookies": {
"sessioncookie": "123456789"
}
}
1.4 代理
某些网站对大规模且频繁的请求,会通过验证或者登录认证或者封掉 IP来禁止访问,就需要使用代理解决这个限制,如果需要使用代理,则可以使用proxies
任何请求方法的参数来配置单个请求 :
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)
1.5 超时设置
如果服务器未及时响应或者本地网络状况不好,收到网页响应需要更长的时间,就需要设置服务器的超时时间。默认情况下,除非明确设置超时值,否则请求不会超时。没有超时,代码可能会挂起几分钟或更长时间。
客户端连接到服务器并发送HTTP请求后, 读取超时就是客户端将等待服务器发送响应的秒数,(具体来说,这是客户端在从服务器发送的字节之间等待的秒数,在99.9%的情况下,这是服务器发送第一个字节之前的时间)。
如果为超时指定单个值,则如下所示:
r = requests.get('https://github.com', timeout=5)
超时值将同时应用于connect
和read
,如果要单独设置值,请指定一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
如果远程服务器非常慢,则可以通过将None传递为超时值,然后获取一杯咖啡,从而使Requests永远等待响应:
r = requests.get('https://github.com', timeout=None)
2. 正则表达式re库
re库(Regular Expression 正则表达式)是Python自带库,提供正则表达式引擎的接口,可实现字符串的检索、替换、匹配提取等,官文指路:https://docs.python.org/3/library/re.html或者参看Python官文中的howto-regex.pdf。
re库常用函数:
函数 | 功能说明 |
re.match(pattern,string,flags=0) | 从一个字符串的开始位置起匹配正则表达式,返回match对象。 |
re.search(pattern,string,flags=0) | 在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象。 |
re.findall(pattern,string,flags=0) | 搜索字符串,以列表类型返回全部能匹配的子串。 |
re.finditer(pattern,string,flags=0) | 搜索字符串,返回一个匹配结果的迭代类型,每个迭代元素是match对象。 |
re.split(pattern,string,maxsplit=0,flags=0) | 将一个字符串按照正则表达式匹配结果进行分割,返回列表类型。 |
re.sub(pattern,repl,string,count=0,flags=0) | 在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串。 |
re.compile(pattern,flags=0) | 生成一个正则表达式(Pattern)对象,供match()和search()这两个函数使用。 |
- Pattern:匹配的正则表达式;
- String:要匹配的字符串;
- Flags:标志位,用于控制正则表达式;的匹配方式,如:是否区分大小写,多行匹配等等。
2.1 re.match(pattern,string,flags=0)
如果string开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的匹配对象;如果没有匹配,就返回None;注意它跟零长度匹配是不同的,match()对象有几种方法:
- group():返回正则匹配的字符串;
- start():返回匹配的开始位置;
- end():返回匹配的结束位置;
- span():返回包含匹配,(start, end) 位置的元组。
>>> import re
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay'))
<re.Match object; span=(0, 3), match='123'>
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').span())
(0, 3)
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').start())
0
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').end())
3
>>> print(re.match(r'[1-9]\d{2}', '123 Nothing gold can stay').group())
123
# 起始位置没有匹配返回none
>>> print(re.match(r'[1-9]\d{2}', 'Nothing gold can stay 123'))
None
# 匹配返回none,则start()/end()/span()/group()返回如下的结果。
>>> print(re.match(r'[1-9]\d{2}', 'Nothing gold can stay 123')).start()
None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'start'
2.2 re.search(pattern,string,flags=0)
扫描整个字符串找到匹配样式的第一个位置,并返回一个相应的匹配对象;如果没有匹配,就返回一个None;注意这和找到一个零长度匹配是不同的。
>>> print(re.match(r'[1-9]\d{2}', 'Nothing 123 gold can stay'))
None
>>> print(re.match(r'[1-9]\d{2}', 'Nothing 123 gold can stay').start())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'start'
>>> print(re.search(r'[1-9]\d{2}', 'Nothing 123 gold can stay'))
<re.Match object; span=(8, 11), match='123'>
>>> print(re.search(r'[1-9]\d{2}', 'Nothing 123 gold can stay').start())
8
2.3 re.findall(pattern,string,flags=0)
对string返回一个不重复的pattern的匹配列表,string从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话),空匹配也会包含在结果里。
>>> print(re.findall(r'[1-9]\d{2}', 'Nothing 123 gold can 456stay'))
['123', '456']
>>> print(re.findall(r'[1-9]\d{2}', 'Nothing gold can stay'))
[]
2.4 re.finditer(pattern,string,flags=0)
pattern在string里所有的非重复匹配,findall类似,它在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回;string从左到右扫描,匹配按顺序排列,空匹配也包含在结果里。
>>> print(re.finditer(r'[1-9]\d{2}', 'Nothing 123 gold can 456stay'))
<callable_iterator object at 0x7f4289b4d970>
>>> print(re.finditer(r'[1-9]\d{2}', 'Nothing gold can stay'))
<callable_iterator object at 0x7f4289b4d970>
2.5 re.split(pattern,string,maxsplit=0,flags=0)
用pattern分开string,也就是将字符串与模式匹配的子字符串都作为分隔符来分隔这个字符串,如果在pattern中捕获到括号,那么所有的组里的文字也会包含在列表里;如果maxsplit非零,最多进行maxsplit次分隔,剩下的字符全部返回到列表的最后一个元素。
>>> print(re.split(r'[1-9]\d{1}', 'Nothing 456gold 123can stay'))
['Nothing ', '6gold ', '3can stay']
>>> print(re.split(r'[1-9]\d{2}', 'Nothing 456gold 123can stay'))
['Nothing ', 'gold ', 'can stay']
2.6 re.sub(pattern,repl,string,count=0,flags=0)
返回通过使用repl替换在string最左边非重叠出现的pattern而获得的字符串;如果样式没有找到,则不加改变地返回string。
- pattern:正则中的模式字符串;
- repl:替换的字符串,也可为一个函数;
- string:要被查找替换的原始字符串;
- count:模式匹配后替换的最大次数,默认0表示替换所有的匹配。
# sub()实现搜索和替换的功能。
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay'))
Nothing 8gold 8can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',0))
Nothing 8gold 8can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',1))
Nothing 8gold 123can stay
>>> print(re.sub(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',2))
Nothing 8gold 8can stay
# subn()与sub()功能类似,返回值多了替换的次数。
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay'))
('Nothing 8gold 8can stay', 2)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',1))
('Nothing 8gold 123can stay', 1)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',2))
('Nothing 8gold 8can stay', 2)
>>> print(re.subn(r'[1-9]\d{2}', '8','Nothing 456gold 123can stay',3))
('Nothing 8gold 8can stay', 2)
2.7 re.compile(pattern,flags=0)
compile函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()和search()这两个函数使用。
- pattern:一个字符串形式的正则表达式;
- flags:可选,表示匹配模式,比如忽略大小写,多行模式等。
>>> import re
>>> pattern = re.compile(r'[1-9]\d{2}')
>>> print(pattern.match('Nothing 456gold 123can stay'))
None
>>> print(pattern.search('Nothing 456gold 123can stay'))
<re.Match object; span=(8, 11), match='456'>
2.8 正则表达式常用操作符
操作符 | 说明 | 实例 |
. | 表示任何单个字符,换行符除外 |
|
[ ] | 字符集,对单个字符给出取值范围 | [abc]表示a、b、c,[a‐z]表示a到z单个字符 |
[^ ] | 非字符集,对单个字符给出排除范围 | [^abc]表示非a或b或c的单个字符 |
* | 前一个字符0次或无限次扩展 | abc* 表示ab、abc、abcc、abccc等 |
+ | 前一个字符1次或无限次扩展 | abc+ 表示abc、abcc、abccc等 |
? | 前一个字符0次或1次扩展,非贪婪限定符,常与. 和*联合使用 | abc? 表示ab、abc |
| | 左右表达式任意一个 | abc|def表示abc、def |
{m} | 扩展前一个字符m次 | ab{2}c表示abbc |
{m,n} | 扩展前一个字符m至n次(含n) | ab{1,2}c表示abc、abbc |
^ | 匹配字符串开头 | ^abc表示abc且在一个字符串的开头 |
$ | 匹配字符串结尾 | abc$表示abc且在一个字符串的结尾 |
( ) | 分组标记,内部只能使用 | 操作符|(abc)表示abc,(abc|def)表示abc、def |
\b | 匹配单词边界,也就是指单词和空格间的位置 | er\b 可以匹配never中的 er,但不能匹配verb中的er
|
\B | 匹配非单词边界 | er\B 能匹配verb中的er,但不能匹配never中的er |
\d | 数字,等价于[0‐9] |
|
\D | 非数字字符,等价于 [^0-9] |
|
\w | 匹配数字、字母、下划线,等价于[A-Za-z0-9_] |
|
\w | 匹配非字母、数字、下划线,等价于[^A‐Za‐z0‐9_] |
|
正则匹配模式分两种:
- 贪婪匹配:尽可能匹配最长的字符串
- 非贪婪匹配: 尽可能匹配最短的字符串
>>> import re
>>> re.match(r'someone(\d+?)','someone2343you')
<re.Match object; span=(0, 8), match='someone2'>
>>> re.match(r'someone(\d+)','someone2343you').group(1)
'2343'
>>> re.match(r'someone(\d+)','someone2343you')
<re.Match object; span=(0, 11), match='someone2343'>
>>> re.match(r'someone(\d+?)','someone2343you').group(1)
'2'
默认情况下是贪婪模式,如要改成非贪婪模式,只需要在“*”,“?”,“+”,“{m,n}”量词后面加上一个问号“?”。在金融数据的挖掘分析中,常用的匹配正则式是非贪婪匹配:(.*?)和.*?。
-
.*?:文本A.*?文本B,用于获取文本A和文本B之间的内容,文本A和文本B之间的我内容经常变动或者没有规律或者内容较多,无法写入匹配规则。
-
(.*?):文本C(.*?)文本D,用于获取文本C和文本D之间的内容,不需指定长度及格式,需指定两端的内容。
>>> import re
>>> w = 'There is no royal2233 road2233 to learning'
>>> print(re.findall('.*\d+', w))
['There is no royal2233 road2233']
>>> print(re.findall('.*?\d+', w))
['There is no royal2233', ' road2233']
>>> print(re.findall('(.*?)\d+', w))
['There is no royal', ' road']
正则表达式可以包含一些可选标志修饰符来控制匹配的模式(用于函数的flag参数),修饰符被指定为一个可选的标志,多个标志可以通过按位 OR(|) 来指定,如 re.I | re.M 被设置成 I 和 M 标志,修饰符如下:
- re.I:使匹配对大小写不敏感;
- re.L:做本地化识别(locale-aware)匹配;
- re.M:多行匹配,影响 ^ 和 $;
- re.S:使 . 匹配包括换行在内的所有字符;
- re.U:根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.;
- re.X:该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
3. heads参数
heads参数提供的事网站访问者的信息,heads中的User-Agent表示用什么浏览器访问,在地址栏输入“about:version”获取User-Agent:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'}
使用方式:requests.get()中添加headers=headers参数。