匹配的分类
按照匹配内容进行匹配
我们在匹配的过程当中,按照要匹配的内容的类型和数量进行匹配
比如:
匹配手机号:匹配以1开头的11位数字
Re
按照匹配结构进行匹配
我们我们在匹配的过程当中,按照要匹配的内容在整个数据当中的结构进行匹配
比如:
匹配手机号:匹配phone:之后的值
Xpath
Beautifulsoup 是一个匹配的结合体,我们使用beautifulsoup可以完成内容和结构任意匹配。
内容匹配,匹配繁琐,匹配精度高
机构匹配,匹配精度不高,匹配效率高,一定是成结构的字符
re正则
正则是通过对字符串内容描述进行数据筛选的高级字符串处理方式
在学习Python爬虫的时候,初学同学会认为非正则不可,其实一部分简单的匹配结构用的最多的方法是字符串的:split、replace方法
内容的类型
#每个类型默认匹配1次
\d 匹配数字
\D 匹配非数字
\s 匹配空格
\S 匹配非空格
\w 匹配字母、数字、下划线
\W 匹配非字母、数字、下划线
[] 匹配任意字符
| 匹配任意一端
[^] 匹配非
. 匹配任意非换行的字符
^ 匹配开头
$ 匹配结尾
() 组匹配
内容的长度
长度描述居于内容描述之后
如果有多次,指尽量多的匹配
如果匹配0次,代表没有匹配到
* 匹配0到多次
+ 匹配1到多次
? 匹配0到1次
{} 匹配指定值或者范围次
贪婪匹配
匹配的方法
Findall
Search
Match
正则例子:
例1:
一个正常的正则匹配,匹配一个网页上所有的身份证号,最简单
匹配身份证号,一般是匹配数字,但是要小心最后有x
首先编写如下代码:
import re
import requests
# 请求地址
url = "http://sfz.ckd.cc/"
# 请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
}
# 获取响应
response = requests.get(url = url,headers = headers)
# 获取内容,这里返回的是字节,所以需要decode进行解析
content = response.content.decode()
# print(content)
# 利用正则粗糙的匹配
res = re.findall(r'\d+',content)
for re in res:
# 打印内容
print(re)
效果如下:
效果差强人意,所以改进,我们认真研究发现,身份证有18,但是数字个数是17-18位,因为最后一位可能是大小写的X
代码如下:
import re
import requests
# 请求地址
url = "http://sfz.ckd.cc/"
# 请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
}
# 获取响应
response = requests.get(url = url,headers = headers)
# 获取内容,这里返回的是字节,所以需要decode进行解析
content = response.content.decode()
# print(content)
# 利用正则粗糙的匹配
# res = re.findall(r'\d+',content)
res = re.findall(r"\d{17}x|\d{17}X|\d{18}",content)
for re in res:
# 打印内容
print(re)
效果如下:
其实上面的正则可以写成这样:
res = re.findall(r"\d{17}[xX]|\d{18}",content)
效果如下:
一个组匹配
需要获取完整的身份证信息
如下:
爬虫的正则匹配和其他的正则匹配区别在于,我们可以借助HTML结构进行匹配
res = re.findall(r"<td>(.*?)</td><td>(.*?)</td><td>(.*)",content,re.M)
效果如下:
Search与match
- 区别(是python 2015年度北京市爬虫面试第一频繁的面试题)
Search是从全文查找符合匹配的,match是从开头查找符合的,二者如果查找到都需要用group来解析内容,如果查找不到,都返回None
2. Group和groups
res = re.search(r"<td>(.*?)</td><td>(.*?)</td><td>(.*)",content,re.M)
print(res.group())
print(res.groups())
效果如下:
Lxml匹配
Python的xpath是通过lxml模块进行定义的,不论用beautifulsoup还是scrapy都需要我们下载lxml包。
pip install lxml
通过lxml我们进行爬虫匹配大概分为2步
- 构建HTML结构,将返回的字符串从新转换为HTML
- 进行匹配
匹配的常用语法
1.节点名称匹配
直接描述HTML节点
P div
2. 子节点描述
/Html/body/div
这样的表述必须是相邻父子关系,禁止夸层
3. 节点描述
[]
可以通过索引或者属性描述节点
Html/body/div[1] HTML下的body下的第一个div
4. 节点属性描述
[@属性=“值”]
Html/body/div[@id=“name”] HTML下的body下的第一个id属性为name的div
Xpath关注的点:
- xpath对普通字符串无效
- xpath匹配不论是匹配到一个对象,还是多个,都以列表返回
- xpath返回的是xpath对象,如果需要调用匹配到的具体内容常用一下三个属性
- text 返回文本
- attrib 返回属性
- tag 返回标签
当我们发现一下格式要注意
如果遇到以下问题,直接text匹配不到内容
<p>
Hello<span>while</span>
</p>
我写一个xpath的例子
尝试爬取代理ip
我们首先查看网站结构
这个时候我们尝试写一下我们的lxml匹配规则
from lxml import etree
import requests
url = "http://www.xicidaili.com/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
}
response = requests.get(url = url,headers = headers)
response_body = response.content.decode()
#print(response_body)
html = etree.HTML(response_body)
tr_list = html.xpath('//tr[@class="odd"]')
for tr in tr_list:
ip = tr.xpath("td[2]")[0].text
port = tr.xpath("td[3]")[0].text
address = tr.xpath("td[4]")[0].text
nm = tr.xpath("td[5]")[0].text
types = tr.xpath("td[6]")[0].text
time = tr.xpath("td[7]")[0].text
valid = tr.xpath("td[8]")[0].text
result_dict = {
"ip": ip,
"port": port,
"address": address,
"nm": nm,
"types": types,
"time": time,
"valid": valid
}
print(result_dict)
效果如下:
思考1:
完成正则例1的正则优化
思考2:
使用xpath匹配以下格式当中的hello
<p>
Hello<span>while</span>
</p>
思考3:
筛选出最新的5个透明的代理ip
用冒泡法对已有的ip按照存活的时间进行排序
请对以上3个问题进行思考,我将会在近期对以上被人进行更新,如果有好的答案,欢迎私信我
思考1中的优化:
res = re.findall(r"\d{17}.",content)
效果如下:
思考2:
from lxml import etree
text_str = '''
<p>
Hello<span>while</span>
</p>
'''
html = etree.HTML(text_str)
res = html.xpath('//p/text()')[0].strip()
print(res)
效果如下:
思考3:
import re
from lxml import etree
import requests
ip_list = []
all_list = []
url = "http://www.xicidaili.com/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
}
response = requests.get(url = url,headers = headers)
response_body = response.content.decode()
#print(response_body)
html = etree.HTML(response_body)
tr_list = html.xpath('//tr[@class="odd"] | //tr[@class=""]')
for tr in tr_list:
ip = tr.xpath("td[2]")[0].text
port = tr.xpath("td[3]")[0].text
address = tr.xpath("td[4]")[0].text
nm = tr.xpath("td[5]")[0].text
types = tr.xpath("td[6]")[0].text
time = tr.xpath("td[7]")[0].text
valid = tr.xpath("td[8]")[0].text
result_dict = {
"ip": ip,
"port": port,
"address": address,
"nm": nm,
"types": types,
"time": time,
"valid": valid
}
all_list.append(result_dict)
niming = result_dict.get("nm")
if niming == "透明":
ip_list.append(result_dict)
# 利用冒泡对存活时间进行排序
def cunhuo(ls):
for j in range(len(ls)):
for i in range(len(ls)-1-j):
if int((re.findall(r'(\d+)',ls[i]['time']))[0]) < int((re.findall(r'(\d+)',ls[i+1]['time']))[0]):
ls[i],ls[i+1] = ls[i+1],ls[i]
for j in range(len(ls)):
for i in range(len(ls) - 1 - j):
if '分' in ls[i]['time']:
ls[i], ls[i + 1] = ls[i + 1], ls[i]
for j in range(len(ls)):
for i in range(len(ls) - 1 - j):
if '时' in ls[i]['time']:
ls[i], ls[i + 1] = ls[i + 1], ls[i]
for j in range(len(ls)):
for i in range(len(ls) - 1 - j):
if '天' in ls[i]['time']:
ls[i], ls[i + 1] = ls[i + 1], ls[i]
return ls
# 最新的五个透明代理ip
def mostNew(lt):
for j in range(len(lt)):
for i in range(len(lt)-1-j):
if int((re.findall(r'(\d+)',lt[i]['valid']))[0]) < int((re.findall(r'(\d+)',lt[i+1]['valid']))[0]):
lt[i],lt[i+1] = lt[i+1],lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '不到' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '分' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '小时' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '天' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '月' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
for j in range(len(lt)):
for i in range(len(lt) - 1 - j):
if '年' in lt[i]['valid']:
lt[i], lt[i + 1] = lt[i + 1], lt[i]
return lt
if __name__ == "__main__":
print('==========' * 8)
print('最新5个透明代理ip如下:')
sort_list = mostNew(ip_list)
for i in range(1, 6):
print(sort_list[i])
print('=========='*8)
print('冒泡存活时间排序如下:')
statistics = cunhuo(all_list)
for i in statistics:
print(i)
效果如下:
以上的做法还可以继续优化,存活时间还需要比较时间单位并不只是数字的大小,希望各位小伙伴们按照我的思路再思考思考