赞
踩
爬虫指在使用程序模拟浏览器向服务端发出网络请求,以便获取服务端返回的内容。
但这些内容可能涉及到一些机密信息,所以爬虫领域目前来讲是属于灰色领域,切勿违法犯罪。
爬虫本身作为一门技术没有任何问题,关键是看人们怎么去使用它
《中华人民共和国刑法》第二百八十五条规定:非法获取计算机信息系统数据、非法控制计算机信息系统罪,是指违反国家规定,侵入国家事务、国防建设、尖端科学技术领域以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,情节严重的行为。刑法第285条第2款明确规定,犯本罪的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。
《反不正当竞争法》第九条规定:以不正当手段获取他人商业秘密的行为即已经构成侵犯商业秘密。而后续如果进一步利用,或者公开该等信息,则构成对他人商业秘密的披露和使用,同样构成对权利人的商业秘密的侵犯。
《刑法》第二百八十六条规定:违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,构成犯罪,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。而违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,也构成犯罪,依照前款的规定处罚。
《网络安全法》第四十四条规定:任何个人和组织不得窃取或者以其他非法方式获取个人信息。因此,如果爬虫在未经用户同意的情况下大量抓取用户的个人信息,则有可能构成非法收集个人信息的违法行为。
《民法总则》第111条规定:任何组织和个人需要获取他人个人信息的,应当依法取得并确保信息安全。不得非法收集、使用、加工、传输他人个人信息
根据爬虫的应用范畴,可有一些三种区分:
通用爬虫
搜索引擎本质就是一个巨大的爬虫,首先该爬虫会爬取整张页面,并且对该页面做备份,之后对其进行数据内容处理如抓取关键字等,然后向用户提供检索接口。
聚焦式爬虫
只关注于页面上某一部分内容,如只关注图片、链接等。
增量式爬虫
用于检索内容是否更新,如开发了一个增量式爬虫每天查看一下云崖博客有没有更新,有更新就爬下来等等…
robots
协议是爬虫领域非常出名的一种协议,由门户网站提供。
它规定了该站点哪些内容允许爬取,哪些内容不允许爬取。
如果爬取不允许的内容,可对其追究法律责任。
requests
模块是Python
中发送网络请求的一款非常简洁、高效的模块。
pip install requests
支持所有的请求方式:
import requests
requests.get("https://www.python.org/")
requests.post("https://www.python.org/")
requests.put("https://www.python.org/")
requests.patch("https://www.python.org/")
requests.delete("https://www.python.org/")
requests.head("https://www.python.org/")
requests.options("https://www.python.org/")
# 指定请求方式
requests.request("get","https://www.python.org/")
当请求发送成功后,会返回一个response
对象。
基本的get
请求参数如下:
参数 | 描述 |
---|---|
params | 字典,get请求的参数,value支持字符串、字典、字节(ASCII编码内) |
headers | 字典,本次请求携带的请求头 |
cookies | 字典,本次请求携带的cookies |
演示如下:
import requests
res = requests.get(
url="http://127.0.0.1:5000/index",
params={"key": "value"},
cookies={"key": "value"},
)
print(res.content)
基本的post
请求参数如下:
参数 | 描述 |
---|---|
data | 字典,post请求的参数,value支持文件对象、字符串、字典、字节(ASCII编码内) |
headers | 字典,本次请求携带的请求头 |
cookies | 字典,本次请求携带的cookies |
演示如下:
import requests
res = requests.post(
url="http://127.0.0.1:5000/index",
# 依旧可以携带 params
data={"key": "value"},
cookies={"key": "value"},
)
print(res.content)
更多参数:
参数 | 描述 |
---|---|
json | 字典,传入json数据,将自动进行序列化,支持get/post,请求体传递 |
files | 字典,传入文件对象,支持post |
auth | 认证,传入HTTPDigestAuth对象,一般场景是路由器弹出的两个输入框,爬虫获取不到,将用户名和密码输入后会base64加密然后放入请求头中进行交给服务端,base64(“名字:密码”),请求头名字:authorization |
timeout | 超时时间,传入float/int/tuple类型。如果传入的是tuple,则是 (链接超时、返回超时) |
allow_redirects | 是否允许重定向,传入bool值 |
proxies | 开启代理,传入一个字典 |
stream | 是否返回文件流,传入bool值 |
cert | 证书地址,这玩意儿来自于HTTPS请求,需要传入该网站的认证证书地址,通常来讲如果是大公司的网站不会要求这玩意儿 |
演示:
def param_method_url(): # requests.request(method='get', url='http://127.0.0.1:8000/test/') # requests.request(method='post', url='http://127.0.0.1:8000/test/') pass def param_param(): # - 可以是字典 # - 可以是字符串 # - 可以是字节(ascii编码以内) # requests.request(method='get', # url='http://127.0.0.1:8000/test/', # params={'k1': 'v1', 'k2': '水电费'}) # requests.request(method='get', # url='http://127.0.0.1:8000/test/', # params="k1=v1&k2=水电费&k3=v3&k3=vv3") # requests.request(method='get', # url='http://127.0.0.1:8000/test/', # params=bytes("k1=v1&k2=k2&k3=v3&k3=vv3", encoding='utf8')) # 错误 # requests.request(method='get', # url='http://127.0.0.1:8000/test/', # params=bytes("k1=v1&k2=水电费&k3=v3&k3=vv3", encoding='utf8')) pass def param_data(): # 可以是字典 # 可以是字符串 # 可以是字节 # 可以是文件对象 # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # data={'k1': 'v1', 'k2': '水电费'}) # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # data="k1=v1; k2=v2; k3=v3; k3=v4" # ) # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # data="k1=v1;k2=v2;k3=v3;k3=v4", # headers={'Content-Type': 'application/x-www-form-urlencoded'} # ) # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # data=open('data_file.py', mode='r', encoding='utf-8'), # 文件内容是:k1=v1;k2=v2;k3=v3;k3=v4 # headers={'Content-Type': 'application/x-www-form-urlencoded'} # ) pass def param_json(): # 将json中对应的数据进行序列化成一个字符串,json.dumps(...) # 然后发送到服务器端的body中,并且Content-Type是 {'Content-Type': 'application/json'} requests.request(method='POST', url='http://127.0.0.1:8000/test/', json={'k1': 'v1', 'k2': '水电费'}) def param_headers(): # 发送请求头到服务器端 requests.request(method='POST', url='http://127.0.0.1:8000/test/', json={'k1': 'v1', 'k2': '水电费'}, headers={'Content-Type': 'application/x-www-form-urlencoded'} ) def param_cookies(): # 发送Cookie到服务器端 requests.request(method='POST', url='http://127.0.0.1:8000/test/', data={'k1': 'v1', 'k2': 'v2'}, cookies={'cook1': 'value1'}, ) # 也可以使用CookieJar(字典形式就是在此基础上封装) from http.cookiejar import CookieJar from http.cookiejar import Cookie obj = CookieJar() obj.set_cookie(Cookie(version=0, name='c1', value='v1', port=None, domain='', path='/', secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False, port_specified=False, domain_specified=False, domain_initial_dot=False, path_specified=False) ) requests.request(method='POST', url='http://127.0.0.1:8000/test/', data={'k1': 'v1', 'k2': 'v2'}, cookies=obj) def param_files(): # 发送文件 # file_dict = { # 'f1': open('readme', 'rb') # } # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # files=file_dict) # 发送文件,定制文件名 # file_dict = { # 'f1': ('test.txt', open('readme', 'rb')) # } # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # files=file_dict) # 发送文件,定制文件名 # file_dict = { # 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf") # } # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # files=file_dict) # 发送文件,定制文件名 # file_dict = { # 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf", 'application/text', {'k1': '0'}) # } # requests.request(method='POST', # url='http://127.0.0.1:8000/test/', # files=file_dict) pass def param_auth(): # 认证,浏览器BOM对象弹出对话框 # 在HTML文档中是找不到该标签的,所以需要用这个对其进行传入,一般来说常见于路由器登录页面 from requests.auth import HTTPBasicAuth, HTTPDigestAuth ret = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('wupeiqi', 'sdfasdfasdf')) print(ret.text) # ret = requests.get('http://192.168.1.1', # auth=HTTPBasicAuth('admin', 'admin')) # ret.encoding = 'gbk' # print(ret.text) # ret = requests.get('http://httpbin.org/digest-auth/auth/user/pass', auth=HTTPDigestAuth('user', 'pass')) # print(ret) # def param_timeout(): # 超时时间,如果链接时间大于1秒就返回 # ret = requests.get('http://google.com/', timeout=1) # print(ret) # 如果链接时间大于5秒就返回,或者响应时间大于1秒就返回 # ret = requests.get('http://google.com/', timeout=(5, 1)) # print(ret) pass def param_allow_redirects(): ret = requests.get('http://127.0.0.1:8000/test/', allow_redirects=False) print(ret.text) def param_proxies(): # 配置代理 # proxies = { # "http": "61.172.249.96:80", # "https": "http://61.185.219.126:3128", # } # proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'} # ret = requests.get("http://www.proxy360.cn/Proxy", proxies=proxies) # print(ret.headers) # from requests.auth import HTTPProxyAuth # # proxyDict = { # 'http': '77.75.105.165', # 'https': '77.75.105.165' # } # auth = HTTPProxyAuth('username', 'mypassword') # # r = requests.get("http://www.google.com", proxies=proxyDict, auth=auth) # print(r.text) pass def param_stream(): # 文件流,直接写入文件即可 ret = requests.get('http://127.0.0.1:8000/test/', stream=True) print(ret.content) ret.close() # from contextlib import closing # with closing(requests.get('http://httpbin.org/get', stream=True)) as r: # # 在此处理响应。 # for i in r.iter_content(): # print(i)
如果爬取一个网站,该网站可能会返回给你一些cookies
,对这个网站后续的请求每次都要带上这些cookies
比较麻烦。
所以可以直接使用session
对象(自动保存cookies
)发送请求,它会携带当前对象中所有的cookies
。
def requests_session(): import requests # 使用session时,会携带该网站中所返回的所有cookies发送下一次请求。 # 生成session对象 session = requests.Session() ### 1、首先登陆任何页面,获取cookie i1 = session.get(url="http://dig.chouti.com/help/service") ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权 i2 = session.post( url="http://dig.chouti.com/login", data={ 'phone': "8615131255089", 'password': "xxxxxx", 'oneMonth': "" } ) i3 = session.post( url="http://dig.chouti.com/link/vote?linksId=8589623", ) print(i3.text)
以下是response
对象的所有参数:
参数 | 描述 |
---|---|
response.text | 返回文本响应内容 |
response.content | 返回二进制响应内容 |
response.json | 如果返回内容是json格式,则进行序列化 |
response.encoding | 返回响应内容的编码格式 |
response.status_code | 状态码 |
response.headers | 返回头 |
response.cookies | 返回的cookies对象 |
response.cookies.get_dict() | 以字典形式展示返回的cookies对象 |
response.cookies.items() | 以元组形式展示返回的cookies对象 |
response.url | 返回的url地址 |
response.history | 这是一个列表,如果请求被重定向,则将上一次被重定向的response对象添加到该列表中 |
编码问题
并非所有网页都是utf8
编码,有的网页是gbk
编码。
此时如果使用txt
查看响应内容就要指定编码格式:
import requests
response=requests.get('http://www.autohome.com/news')
response.encoding='gbk'
print(response.text)
下载文件
使用response.context
时,会将所有内容存放至内存中。
如果访问的资源是一个大文件,而需要对其进行下载时,可使用如下方式生成迭代器下载:
import requests
response=requests.get('http://bangimg1.dahe.cn/forum/201612/10/200447p36yk96im76vatyk.jpg')
with open("res.png","wb") as f:
for line in response.iter_content():
f.write(line)
json返回内容
如果确定返回内容是json
数据,则可以通过response.json
进行查看:
import requests
response = requests.get("http://127.0.0.1:5000/index")
print(response.json())
历史记录
如果访问一个地址却被重定向了,被重定向的地址会被存放到response.history
这个列表中:
import requests
r = requests.get('http://127.0.0.1:5000/index') # 被重定向了
print(r.status_code) # 200
print(r.url) # http://127.0.0.1:5000/new # 重定向的地址
print(r.history)
# [<Response [302]>]
如果在请求时,指定allow_redirects
参数为False
,则禁止重定向:
import requests
r = requests.get('http://127.0.0.1:5000/index',allow_redirects=False) # 禁止重定向
print(r.status_code) # 302
print(r.url) # http://127.0.0.1:5000/index
print(r.history)
# []
request
模块可以发送请求,获取HTML
文档内容。
而bs4
模块可以解析出HTML
与XML
文档的内容,如快速查找标签等等。
pip3 install bs4
bs4模块只能在Python中使用
bs4
依赖解析器,虽然有自带的解析器,但是目前使用最多的还是lxml
:
pip3 install lxml
将request
模块请求回来的HTML
文档内容转换为bs4
对象,使用其下的方法进行查找:
如下示例,解析出虾米音乐中的歌曲,歌手,歌曲时长:
import requests from bs4 import BeautifulSoup from prettytable import PrettyTable # 实例化表格 table = PrettyTable(['编号', '歌曲名称', '歌手', '歌曲时长']) url = r"https://www.xiami.com/list?page=1&query=%7B%22genreType%22%3A1%2C%22genreId%22%3A%2220%22%7D&scene=genre&type=song" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", } response = requests.get(url=url, headers=headers) # step01: 将文本内容实例化出bs对象 soup_obj = BeautifulSoup(response.text, "lxml") # step02: 查找标签 main = soup_obj.find("div", attrs={"class": "table idle song-table list-song"}) # step03: 查找存放歌曲信息的tbody标签 tbody = main.select(".table-container>table>tbody")[0] # step04: tbody标签中的每个tr都是一首歌曲 tr = tbody.find_all("tr") # step04: 每个tr里都存放有歌曲信息,所以直接循环即可 for music in tr: name = music.select(".song-name>a")[0].text singer = music.select(".COMPACT>a")[0].text time_len = music.select(".duration")[0].text table.add_row([tr.index(music) + 1, name, singer, time_len]) # step05: 打印信息 print(table)
结果如下:
+------+--------------------------------------------------+--------------------+----------+ | 编号 | 歌曲名称 | 歌手 | 歌曲时长 | +------+--------------------------------------------------+--------------------+----------+ | 1 | Love Story (Live from BBC 1's Radio Live Lounge) | Taylor Swift | 04:25 | | 2 | Five Hundred Miles | Jove | 03:27 | | 3 | I'm Gonna Getcha Good! (Red Album Version) | Shania Twain | 04:30 | | 4 | Your Man | Josh Turner | 03:45 | | 5 | Am I That Easy To Forget | Jim Reeves | 02:22 | | 6 | Set for Life | Trent Dabbs | 04:23 | | 7 | Blue Jeans | Justin Rutledge | 04:25 | | 8 | Blind Tom | Grant-Lee Phillips | 02:59 | | 9 | Dreams | Slaid Cleaves | 04:14 | | 10 | Remember When | Alan Jackson | 04:31 | | 11 | Crying in the Rain | Don Williams | 03:04 | | 12 | Only Worse | Randy Travis | 02:53 | | 13 | Vincent | The Sunny Cowgirls | 04:22 | | 14 | When Your Lips Are so Close | Gord Bamford | 03:02 | | 15 | Let It Be You | Ricky Skaggs | 02:42 | | 16 | Steal a Heart | Tenille Arts | 03:09 | | 17 | Rylynn | Andy McKee | 05:13 | | 18 | Rockin' Around The Christmas Tree | Brenda Lee | 02:06 | | 19 | Love You Like a Love Song | Megan & Liz | 03:17 | | 20 | Tonight I Wanna Cry | Keith Urban | 04:18 | | 21 | If a Song Could Be President | Over the Rhine | 03:09 | | 22 | Shut'er Down | Doug Supernaw | 04:12 | | 23 | Falling | Jamestown Story | 03:08 | | 24 | Jim Cain | Bill Callahan | 04:40 | | 25 | Parallel Line | Keith Urban | 04:14 | | 26 | Jingle Bell Rock | Bobby Helms | 04:06 | | 27 | Unsettled | Justin Rutledge | 04:01 | | 28 | Bummin' Cigarettes | Maren Morris | 03:07 | | 29 | Cheatin' on Her Heart | Jeff Carson | 03:18 | | 30 | If My Heart Had a Heart | Cassadee Pope | 03:21 | +------+--------------------------------------------------+--------------------+----------+ Process finished with exit code 0
准备一个HTML
文档,对他进行解析:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <form action="#" method="post" enctype="multipart/form-data"> <fieldset> <legend><h1>欢迎注册</h1></legend> <p>头像: <input type="file" name="avatar"/></p> <p>用户名: <input type="text" name="username" placeholder="请输入用户名"/></p> <p>密码: <input type="text" name="pwd" placeholder="请输入密码"/></p> <p>性别: 男<input type="radio" name="gender" value="male"/>女<input type="radio" name="gender" value="female"/></p> <p>爱好: 篮球<input type="checkbox" name="hobby" value="basketball" checked/>足球<input type="checkbox" name="hobby" value="football"/></p> 居住地 <select name="addr"> <optgroup label="中国"> <option value="bejing" selected>北京</option> <option value="shanghai">上海</option> <option value="guangzhou">广州</option> <option value="shenzhen">深圳</option> <option value="other">其他</option> </optgroup> <optgroup label="海外"> <option value="America">美国</option> <option value="Japanese">日本</option> <option value="England">英国</option> <option value="Germany">德国</option> <option value="Canada">加拿大</option> </optgroup> </select> </fieldset> <fieldset> <legend>请填写注册理由</legend> <p><textarea name="register_reason" cols="30" rows="10" placeholder="请填写充分理由"></textarea></p> </fieldset> <p><input type="reset" value="重新填写信息"/> <input type="submit" value="提交注册信息"> <input type="butoon" value="联系客服" disabled> </p> </form> </body> </html>
基本选择器如下 :
选择器方法 | 描述 |
---|---|
TagName | 唯一选择器,根据标签名来选择 |
find() | 唯一选择器,可根据标签名、属性来做选择 |
select_one() | 唯一选择器,可根据CSS选择器语法做选择 |
find_all() | 集合选择器,可根据标签名、属性来做选择 |
select() | 集合选择器,可根据CSS选择器语法做选择 |
.TagName
选择器只会拿出第一个匹配的内容,必须根据标签名选择:
input = soup.input
print(input)
# <input name="avatar" type="file"/>
.find()
选择器只会拿出第一个匹配的内容,可根据标签名、属性来做选择
input= soup.find("input",attrs={"name":"username","type":"text"}) # attrs指定属性
print(input)
# <input name="username" placeholder="请输入用户名" type="text"/>
.select_one()
根据css
选择器来查找标签,只获取第一个:
input = soup.select_one("input[type=text]")
print(input)
# <input name="username" placeholder="请输入用户名" type="text"/>
.find_all()
可获取所有匹配的标签,返回一个list
,可根据标签名、属性来做选择
input_list = soup.find_all("input",attrs={"type":"text"})
print(input_list)
# [<input name="username" placeholder="请输入用户名" type="text"/>, <input name="pwd" placeholder="请输入密码" type="text"/>]
.select()
根据css
选择器获取所有匹配的标签,返回一个list
input_list = soup.select("input[type=text]")
print(input_list)
# [<input name="username" placeholder="请输入用户名" type="text"/>, <input name="pwd" placeholder="请输入密码" type="text"/>]
使用较少,选读:
属性/方法 | 描述 |
---|---|
children | 获取所有的后代标签,返回迭代器 |
descendants | 获取所有的后代标签,返回生成器 |
index() | 检查某个标签在当前标签中的索引值 |
clear() | 删除后代标签,保留本标签,相当于清空 |
decompose() | 删除标签本身(包括所有后代标签) |
extract() | 同.decomponse()效果相同,但会返回被删除的标签 |
decode() | 将当前标签与后代标签转换字符串 |
decode_contents() | 将当前标签的后代标签转换为字符串 |
encode() | 将当前标签与后代标签转换字节串 |
encode_contents() | 将当前标签的后代标签转换为字节串 |
append() | 在当前标签内部追加一个标签(无示例) |
insert() | 在当前标签内部指定位置插入一个标签(无示例) |
insert_before() | 在当前标签前面插入一个标签(无示例) |
insert_after() | 在当前标签后面插入一个标签(无示例) |
replace_with() | 将当前标签替换为指定标签(无示例) |
.children
获取所有的后代标签,返回迭代器
form = soup.find("form")
print(form.children)
# <list_iterator object at 0x0000025665D5BDD8>
.descendants
获取所有的后代标签,返回生成器
form = soup.find("form")
print(form.descendants)
# <generator object descendants at 0x00000271C8F0ACA8>
.index()
检查某个标签在当前标签中的索引值
body = soup.find("body")
form = soup.find("form")
print(body.index(form))
# 3
.clear()
删除后代标签,保留本标签,相当于清空
form = soup.find("form")
form.clear()
print(form) # None
print(soup)
# 清空了form
.decompose()
删除标签本身(包括所有后代标签)
form = soup.find("form")
form..decompose()
print(form) # None
print(soup)
# 删除了form
.extract()
同.decomponse()
效果相同,但会返回被删除的标签
form = soup.find("form")
form..extract()
print(form) # 被删除的内容
print(soup)
# 被删除了form
.decode()
将当前标签与后代标签转换字符串,.decode_contents()
将当前标签的后代标签转换为字符串
form = soup.find("form")
print(form.decode()) # 包含form
print(form.decode_contents()) # 不包含form
.encode()
将当前标签与后代标签转换字节串,.encode_contents()
将当前标签的后代标签转换为字节串
form = soup.find("form")
print(form.encode()) # 包含form
print(form.encode_contents()) # 不包含form
以下方法都比较常用:
属性/方法 | 描述 |
---|---|
name | 获取标签名称 |
attrs | 获取标签属性 |
text | 获取该标签下的所有文本内容(包括后代) |
string | 获取该标签下的直系文本内容 |
is_empty_element | 判断是否是空标签或者自闭合标签 |
get_text() | 获取该标签下的所有文本内容(包括后代) |
has_attr() | 检查标签是否具有该属性 |
.name
获取标签名称
form = soup.find("form")
print(form.name)
# form
.attrs
获取标签属性
form = soup.find("form")
print(form.attrs)
# {'action': '#', 'method': 'post', 'enctype': 'multipart/form-data'}
.is_empty_element
判断是否是空标签或者自闭合标签
input = soup.find("input")
print(input.is_empty_element)
# True
.get_text()
与text
获取该标签下的所有文本内容(包括后代)
form = soup.find("form")
print(form.get_text())
print(form.text)
string
获取该标签下的直系文本内容
form = soup.find("form")
print(form.get_text())
print(form.string)
.has_attr()
检查标签是否具有该属性
form = soup.find("form")
print(form.has_attr("action"))
# True
xPath
模块的作用与bs4
相同,都是查找标签。
但是xPath
模块的通用性更强,它的语法规则并不限于仅在Python
中使用。
作为一门小型的专业化查找语言,xPath
在Python
中被集成在了lxml
模块中,所以直接下载安装就可以开始使用了。
pip3 install lxml
加载文档:
from lxml import etree
# 解析网络爬取的html源代码
root = etree.HTML(response.text,,etree.HTMLParser()) # 加载整个HTML文档,并且返回根节点<html>
# 解析本地的html文件
root = etree.parse(fileName,etree.HTMLParser())
基本选取符:
符号 | 描述 |
---|---|
/ | 从根节点开始选取 |
// | 不考虑层级关系的选取节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 属性检测 |
[num] | 选取第n个标签元素,从1开始 |
/@attrName | 选取当前元素的某一属性 |
* | 通配符 |
/text() | 选取当前节点下的直系文本内容 |
//text() | 选取当前文本下的所有文本内容 |
以下是示例:
注意:xPath选择完成后,返回的始终是一个list,与jQuery类似,可以通过Index取出Element对象
from lxml import etree root = etree.parse("./testDataDocument.html",etree.HTMLParser()) # 从根节点开始找 / form_list = root.xpath("/html/body/form") print(form_list) # [<Element form at 0x203bd29c188>] # 不考虑层级关系的选择节点 // input_list = root.xpath("//input") print(input_list) # 从当前的节点开始选择 即第一个form表单 ./ select_list = form_list[0].xpath("./fieldset/select") print(select_list) # 选择当前节点的父节点 .. form_parent_list = form_list[0].xpath("..") print(form_parent_list) # [<Element body at 0x1c946e4c548>] # 属性检测 @ 选取具有name属性的input框 input_username_list = root.xpath("//input[@name='username']") print(input_username_list) # 属性选取 @ 获取元素的属性 attrs_list = root.xpath("//p/@title") print(attrs_list) # 选取第n个元素,从1开始 p_text_list = root.xpath("//p[2]/text()") print(p_text_list) # 通配符 * 选取所有带有属性的标签 have_attrs_ele_list = root.xpath("//*[@*]") print(have_attrs_ele_list) # 获取文本内容-直系 print(root.xpath("//form/text()")) # 结果:一堆\r\n # 获取文本内容-非直系 print(root.xpath("//form//text()")) # 结果:本身和后代的text # 返回所有input与p标签 ele_list = root.xpath("//input|//p") print(ele_list)
你可以指定逻辑运算符,大于小于等。
from lxml import etree
root = etree.parse("./testDataDocument.html",etree.HTMLParser())
# 返回属性值price大于或等于20的标签
price_ele_list = root.xpath("//*[@price>=20]")
print(price_ele_list)
xPath
中拥有轴这一概念,不过相对来说使用较少,它就是做关系用的。了解即可:
轴 | 示例 | 说明 |
---|---|---|
ancestor | xpath(‘./ancestor: 声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/761001 推荐阅读 相关标签 Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。 |