首先是lxml库:
#我用的火狐,安装一个try xpath插件就好了
"""
xpath路径:
1.选取节点:
首先,随便一个节点名称,这是基本
然后,单斜杠,/,如果出现在开头,那就从根节点寻找;
如果在某个节点后面,那就表示在该路径下的直接子节点
还有,双斜杠,//,找去所有可以匹配的节点,不论路径.
最后,@,根据属性查找节点/查找属性,如//div[@id]
2.谓语:
用来查找某个节点或者某个包含特定值的节点,被嵌在方括号中
方括号中的类似下标
bookstore/book[1]:bookstore下的第一个book元素,
还有last(),position()函数可供使用.
book[@price]:所有有price属性的bookyuansu,
book[@price>10]:所有price大于10的book元素.
book[@price,"10"]:模糊匹配
3.通配符:
/*某节点下的所有子节点
book[@*]:所有带有属性的book节点
4.多个路径:
使用"|"就好
5.运算符
"""
"""
lxml库,
是个高效的xml/html解析器."""
"""以下操作和html相关"""
#1,解析html代码:
from lxml import etree
import requests as re
#先准备一个html代码文件
respond=re.get("https://www.xsbiquge.com/79_79339/")
respond.encoding=respond.apparent_encoding
#核心方法1
htmlElement=etree.HTML(respond.text)
#转换字符串的方法:
print(etree.tostring(htmlElement,encoding="utf-8").decode("utf-8"))
#直接把文件转成html_Elemrent
#htmlElement=etree.parse("试一试.html")
#该方法还可以指定解析器:
HtmlParser=etree.HTMLParser(encoding="utf-8")
htmlElement=etree.parse("试一试.html",parser=HtmlParser)
#通过指定解析器,可以处理不规范的html文件(默认解析器是xml解析器)
#但在源代码为字符串的时候,从方法名称就可以看出它本来用的就是html解析器
print("----------------------------------------------------------------")
'''以下操作和xml,xpath相关'''
html=etree.HTML(respond.text)
#获取所有的div标签:
divs=html.xpath("//div")#该方法的返回值都是列表
for div in divs:
print(etree.tostring(div,encoding="utf-8").decode("utf-8"))
#获取第二个div标签:
print("---------------")
div2=html.xpath("//div[2]")
print(etree.tostring(div2[0],encoding="utf-8").decode("utf-8"))
#获取class为"footer_link"的div标签
print("---------------")
divs=html.xpath("//div[@class='footer_link']")#该方法的返回值都是列表
for div in divs:
print(etree.tostring(div,encoding="utf-8").decode("utf-8"))
#获取所有a标签的href属性:
print("---------------")
#但是这里只获得了web项目目录后面的路径,
#不是完整路径
hrefs=html.xpath("//a/@href")
for href in hrefs:
print(href)
#获取该小说的所有章节名称:
print("---------------")
#出现了鬼畜,数字下标不能用,
#本来我打算一层层的div加上id或者class检索下来
#发现太麻烦,直接看id是"list"的div就好
xpathstring="//div[@id='list']/dl/*"
namesdd=html.xpath(xpathstring)
for name in namesdd:
#第一种方法
namea=name.xpath(".//a")
#这是个双重获取,所以路径的写法要注意,"."表示相对路径
for a in namea:
print(a.text)
#或者
namea = name.xpath("a/text()")
for a in namea:
print(a)
#最简方法(对双斜杠的灵活使用):
xpathstring="//div[@id='list']//text()"
namesdd=html.xpath(xpathstring)
for name in namesdd:
print(name)
然后还有bs4库:
from bs4 import BeautifulSoup
#beautifulsoup库,功能和lxml一样,也是用来解析html/xml/....的,
#正则表达式也是做这个的,它们三个相比,lxml最均衡,难度和速度都是中等,
#正则表达式速度最快,但是难度最大,beautiful速度最慢,但是最简单.
#先准备好html字符串:
import requests as re
respond=re.get("https://www.xsbiquge.com/79_79339/")
respond.encoding=respond.apparent_encoding
html=respond.text
#第一步:
bs=BeautifulSoup(html,"lxml")
#这里注意第二个参数,解析器,
#解析器有很多种:lxml,html.parser,html5lib--最好的容错性(速度慢),xml
#一般用lxml,遇见奇葩网站用htnl5lib
#有个pretty()方法,可以规整字符串
print("**************************************************")
#print(bs.prettify())
#具体的数据提取:
#1.获取所有的a标签:
aes=bs.find_all("a")#find方法返回满足条件的第一个
for a in aes:
print(a)
#2.获取第二个a 标签:
aes=bs.find_all("a",limit=2)#limit参数是说最多提取几个参数
print(aes[1])#只能通过列表操作,没有xml快
#3.获取class为"box_con"的idv(双条件获取同理)
#方法1
divs=bs.find_all("div",class_="box_con")
for div in divs:
print(div)
#方法2:
divs=bs.find_all("div",attrs={"class":"box_con"})
for div in divs:
print(div)
#4.获取所有a标签的href属性:
aes=bs.find_all("a")
for a in aes:
#4.1通过下标的方法
href=a["href"]
print(href)
#4.2.通过attrs的属性获取:
href=a.attrs["href"]
print(href)
#5.获取所有章节名(多层获取+获取字符串):
list_div=bs.find_all("div",id="list")[0]
aes=list_div.find_all("a")
for a in aes:
print(a.string)#除了string属性,华友strings 和stripped_strings属性,获取所有(非空格)字符串
#如果标签下有多个内容(如换行符),string就无法获取了,用content
#CSS选择器:
#有标签--直接写,类名--加.,id--加#.,子孙元素--有空格(直接子元素有>),属性--中括号
#接下来我们用css选择器的方式将上述的需求再写一遍:
#1.获取所有的a标签:
aes=bs.select("a")
for a in aes:
print(a)
#2.获取第二个a 标签:
aes=bs.select("a")
print(aes[1])
#3.获取class为"box_con"的idv(双条件无法实现)
#方法1
divs=bs.select("div.box_con")
for div in divs:
print(div)
#方法2:
divs=bs.select("div[class='box_con']")
for div in divs:
print(div)
#4.获取所有a标签的href属性(这个需求主要不是靠css实现的):
aes=bs.select("a")
for a in aes:
href=a["href"]
print(href)
最后是正则表达式相关:
#这个东西真是用的广:
import re
#最基本的匹配字符串:
text="hednniedjendjhelloncjdncj:mme"
pattern="he"
#match()函数只能匹配开头
result=re.match(pattern=pattern,string=text)
print(result.group())
#2 "." :匹配任意字符(除了换行符,只能匹配一个):
text="hednniedjendjhelloncjdncj:mme"
pattern="..."
result=re.match(pattern=pattern,string=text)
print(result.group())
#3 "\d" 匹配任意数字:
#4 "\D" 匹配任意非数字
#5 "\s" 匹配空白字符串(\n \r \t 还有空格)
#6 "\w" 匹配所有a-z,A-Z,0-9,还有_,就是可以在变量名可以出现的字符.
#7 "\W" 和"\w"匹配的相反
#8 "[]" 只要符合中括号中的字符,就可以匹配
#[0-9] = \d
#[0-9 a-z A-Z _] = \w
#8.1 可见 '-' 也是个特殊字符
#8.2 "^"在中括号种就是取反
#9 'X+' 符合X条件的一个或多个
#10 "X*" 符合X条件的零到多个
#11 "^X" 匹配不符合X的字符
#12 "?" 匹配一个或者零个
#13 "{m}" 匹配m个
#14 "{m,n}" 匹配m-n个
#15 "^X" 检查整个字符串以X开头
#16 "X$" 检查整个字符串是否以X结尾,X必须是非规则表达式(正常字符串)
# 17 "|" 取或
#贪婪模式(匹配尽量多的字符,默认)和非贪婪模式(匹配尽量少的字符,加在+*等后面)
#注意原生字符串和转义字符串,要匹配的字符串里有两个//表示/,你要匹配/,正则表达式要写//,加上转移,就得写,所以给正则表达式加个r表示原生字符串就完事了.
#分组,group:
text="abaabaaabaaab"
pattern="(a+b)(a+b)(a+b)"
result=re.search(pattern=pattern,string=text)
print(result.groups())
#分组对应正则表达式中的括号的分组,第一个括号中的内容就是group(1),
#以此类推,整个正则表达式就是group(0)
#groups可以将所有的括号对应的组拿出来,并去掉group(0)
#find_all:
text="abaabaaabaaab"
pattern="(a+b)"
#返回所有可以和正则表达式匹配的字符串
result=re.findall(pattern=pattern,string=text)
print(result)
#sub() 替换:
text="abaabaaabaaab"
pattern="(a+b)"
result=re.sub(pattern=pattern,repl="找到了",string=text)
print(result)
#可以re.sub("<.+?>",repl="",string=html):可以去掉html中的所有标签<>
#split()函数:
text="ab aab aaab aaab"
pattern=" +"
result=re.split(pattern=pattern,string=text)
print(result)
#compile(),两个功能:
#1.一个正则表达式经过多次使用,可以先把这个正则表达式编译成对象,每次传这个对象
#2.设置为re.VERBOSE: 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释
text="abaabaaabaaab"
regex=re.compile("""
a#就是字母a
#是指a的个数
b#是指字母b
""",re.VERBOSE)
#re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释
result=re.search(regex,string=text)
print(result.groups())