1. requests
1.1. 发送请求
1.1.1. Get
GET请求是http最简单请求模式
requests对应的get请求用法如下
# 一般写法
url = 'http://httpbin.org/get?k1=v1&k2=v20&k2=v21'
r = requests.get(url)
print(r.text)
"""
{
"args": {
"k1": "v1",
"k2": [
"v20",
"v21"
]
},
...
}
"""
# requests提供下面更加优雅的写法
url = 'http://httpbin.org/get'
params = {'k1': 'v1', 'k2': ['v20', 'v21']}
r = requests.get(url, params=params)
print(r.text)
"""
{
"args": {
"k1": "v1",
"k2": [
"v20",
"v21"
]
},
...
}
"""
1.1.2. Post
GET请求参数的长度最大为2048个字节,对于超过长度限制的请求,则需要使用POST请求方式
POST请求有以下3种编码方式
- application/x-www-form-urlencoded 最常见的post提交数据的方式,以form表单形式提交数据,而且post的数据必须用 urlencoded 进行编码
- application/json 以json格式提交数据
- multipart/form-data 一般用于上传文件(较少用)
3种方式requests对应的post请求用法如下
url = 'http://httpbin.org/post'
# application/x-www-form-urlencoded
# 该方法会自动将dict类型转换成string类型并进行urlencoded编码
data = {'k1': 'v1', 'k2': 'v2'}
r = requests.post(url, data=data)
print(r.text)
"""
{
...
"form": {
"k1": "v1",
"k2": "v2"
},
"headers": {
...
"Content-Type": "application/x-www-form-urlencoded",
...
},
...
}
"""
# application/json
json = {'k3': ['v31', 'v32'], 'k4': ['v41', 'v42']}
r = requests.post(url, json=json)
print(r.text)
"""
{
...
"data": "{\"k3\": [\"v31\", \"v32\"], \"k4\": [\"v41\", \"v42\"]}",
"headers": {
...
"Content-Type": "application/json",
...
},
"json": {
"k3": [
"v31",
"v32"
],
"k4": [
"v41",
"v42"
]
},
...
}
"""
# multipart/form-data
files = {'file': open('url.txt', 'rb')}
r = requests.post(url, files=files)
print(r.text)
"""
{
...
"files": {
"file": "http://httpbin.org/post"
},
"headers": {
...
"Content-Type": "multipart/form-data; boundary=3aaf6d863f058f8152a511c05c50e203",
...
},
...
}
"""
下面是几个比较特殊的用法,需要有专门的服务端配套,一般用不上
# 流式上传
with open(file, 'rb') as f:
requests.post(url, data=f)
# 块编码请求
def gen():
yield 'hi'
yield 'there'
requests.post(url, data=gen())
附:urlencoded 编码和 urldecode 解码
这是一种专门在互联网传输时,用于对非英文字符和非数字的编码和解码
编码时将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
python中的编码与解码如下:
>>> import urllib.parse # urlencode >>> params = 'k1=你好&k2=["world"]' >>> urlencode = urllib.parse.quote(params) >>> urlencode 'k1%3D%E4%BD%A0%E5%A5%BD%26k2%3D%5B%22world%22%5D' # urldecode >>> urldecode = urllib.parse.unquote(urlencode) >>> urldecode 'k1=你好&k2=["world"]' # 另一种更加优雅的urlencode方式 >>> params = {'k1': '你好', 'k2': '["world"]'} >>> urllib.parse.urlencode(params) 'k1=%E4%BD%A0%E5%A5%BD&k2=%5B%22world%22%5D'
1.1.3. 其他请求
所有的http请求如下:
方法 | 描述 |
---|---|
GET | 请求指定的页面信息,并返回实体主体。 |
HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
对于所有的请求,requests中都有相应的方法进行实现,但对于爬虫任务,用的比较少,这里就不具体展开说明。
1.2. 通用参数设置
1.2.1. 请求头
请求头用于说明是谁或什么在发送请求、请求源于何处,或者客户端的喜好及能力。服务器可以根据请求头部给出的客户端信息,试着为客户端提供更好的响应。
特别地,请求头对大小写不敏感
headers = {'user-agent': 'my-app/0.0.1'}
url = 'http://httpbin.org/headers'
r = requests.get(url, headers=headers)
print(r.text)
"""
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "my-app/0.0.1",
"X-Amzn-Trace-Id": "Root=1-5f687699-fdd5cb437210b6af788f1410"
}
}
"""
附:请求头参数说明
Header 解释 示例 Accept 指定客户端能够接收的内容类型 Accept: text/plain, text/html Accept-Charset 浏览器可以接受的字符编码集。 Accept-Charset: iso-8859-5 Accept-Encoding 指定浏览器可以支持的web服务器返回内容压缩编码类型。 Accept-Encoding: compress, gzip Accept-Language 浏览器可接受的语言 Accept-Language: en,zh Accept-Ranges 可以请求网页实体的一个或者多个子范围字段 Accept-Ranges: bytes Authorization HTTP授权的授权证书 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Cache-Control 指定请求和响应遵循的缓存机制 Cache-Control: no-cache Connection 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) Connection: close Cookie HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 Cookie: $Version=1; Skin=new; Content-Length 请求的内容长度 Content-Length: 348 Content-Type 请求的与实体对应的MIME信息 Content-Type: application/x-www-form-urlencoded Date 请求发送的日期和时间 Date: Tue, 15 Nov 2010 08:12:31 GMT Expect 请求的特定的服务器行为 Expect: 100-continue From 发出请求的用户的Email From: user@email.com Host 指定请求的服务器的域名和端口号 Host: www.zcmhi.com If-Match 只有请求内容与实体相匹配才有效 If-Match: “737060cd8c284d8af7ad3082f209582d” If-Modified-Since 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT If-None-Match 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 If-None-Match: “737060cd8c284d8af7ad3082f209582d” If-Range 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag If-Range: “737060cd8c284d8af7ad3082f209582d” If-Unmodified-Since 只在实体在指定时间之后未被修改才请求成功 If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT Max-Forwards 限制信息通过代理和网关传送的时间 Max-Forwards: 10 Pragma 用来包含实现特定的指令 Pragma: no-cache Proxy-Authorization 连接到代理的授权证书 Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Range 只请求实体的一部分,指定范围 Range: bytes=500-999 Referer 先前网页的地址,当前请求网页紧随其后,即来路 Referer: http://www.zcmhi.com/archives/71.html TE 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 TE: trailers,deflate;q=0.5 Upgrade 向服务器指定某种传输协议以便服务器进行转换(如果支持) Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 User-Agent User-Agent的内容包含发出请求的用户信息 User-Agent: Mozilla/5.0 (Linux; X11) Via 通知中间网关或代理服务器地址,通信协议 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) Warning 关于消息实体的警告信息 Warn: 199 Miscellaneous warning
1.2.2. cookies
Cookies为“小型文本文件”,是将用户的身份信息加密后暂时或永久保存在用户的计算机中,其作用是为了优化用户访问网页体验,避免用户每次访问都要输入登录名密码。一般cookies都会有一个有效时间,过了有效期,就要重新登陆获取新的cookies
url = 'http://httpbin.org/cookies'
cookies = {'cookies_are': 'working'}
r = requests.get(url, cookies=cookies)
print(r.text)
"""
{
"cookies": {
"cookies_are": "working"
}
}
"""
# 或者使用下面这种方式进行更加细致地配置
jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
url = 'http://httpbin.org/cookies'
r = requests.get(url, cookies=jar)
print(r.text)
"""
{
"cookies": {
"tasty_cookie": "yum"
}
}
"""
1.2.3. 超时
# 设置10秒等待响应时间,如果超时就会抛出异常
requests.get(url, timeout=10)
特别地,timeout仅对连接过程有效,与响应体的下载无关。简单来说就是,如果timeout秒内,如果没有收到任何字节的数据,才会抛出异常,但如果timeout时间内收到哪怕1字节的数据,但网页的加载还没完成,也不会抛出异常,而且requests不提供任何形式的非阻塞 IO,所以会造成程序永久阻塞的“假卡死”状态,可以使用下面这种方式,传入一个二元组,分别表示connect和read的timeout值
r = requests.get(url, timeout=(10, 27))
1.2.4. 返回流文件
一般用于下载文件或者爬取视频流文件
r = requests.get(url, stream=True)
with open(filename, 'wb') as fd:
for chunk in r.iter_content(chunk_size):
fd.write(chunk)
1.2.5. 重定向
重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置
默认允许重定向,取消重定向的设置如下
requests.get(url, allow_redirects=False)
1.2.6. 代理
1.2.6.1. http协议代理设置
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
1.2.6.2. socks协议代理
这个功能需要先安装依赖包
$ pip install requests[socks]
socks代理设置
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
requests.get(url, proxies=proxies)
1.2.7. https证书设置
https请求需要验证 SSL 证书,就像 web 浏览器一样。SSL 验证默认是开启的,一般用不到这个配置
# 忽略SSL证书验证
requests.get(url, verify=False)
# 自定义传入可信任 CA 证书文件的文件夹路径
requests.get(url, verify=certfile_path)
也可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:
requests.get(url, cert=(cert_file, cert_key_file))
1.2.8. 全局设置
一般创建一个会话,对会话进行设置,你可以理解为这是一种全局的设置,所有由该会话发起的请求,都会附带该会话的配置。对于批量爬虫任务,推荐使用这种方法替换直接使用requests发起请求。会话的使用方法和返回对象和直接使用requests的完全一样。
请求的参数都可以在会话中进行显式的设置,或者使用update的方法,具体设置的例子如下:
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
发起请求如下:
r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
print(r.text)
"""
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Basic dXNlcjpwYXNz",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0",
"X-Amzn-Trace-Id": "Root=1-5f687aa2-f15b63aaf37d83f76c466939",
"X-Test": "true",
"X-Test2": "true"
}
}
"""
特别地,单独的请求还是可以传入参数,你可以理解为传入的是本地变量,该参数只会作用于当前请求,对其他的请求不起作用。如果全局和本地变量都被设置,就会对两者进行合并,如果参数重复了,则以本地变量优先。
1.3. 内容响应
# 二进制类型的响应内容
r.content
# 将原网页的二进制编码成文本的响应内容,官网说会自动推测网页的编码格式,但其实并没有用
r.text
# 二进制转换成文本的编码格式,默认是unicode,可以显式修改,修改后的text会根据新的编码格式编码输出
r.encoding
# 响应状态码
r.status_code
r.status_code == requests.codes.ok # 响应成功判断,小于400为响应成功,不小于400为响应失败
r.raise_for_status() # 遇到4xx或5xx响应失败状态码时,会抛出异常
# 返回响应的请求头,这里注意和发送请求的请求头相区别
# 返回一个类字典的对象
r.headers
r.headers['Content-Type'] # 获取请求头中的某个参数值
r.headers.get('content-type')
# 返回响应的cookies
# 返回一个requests.cookies.RequestsCookieJar()对象
r.cookies
r.cookies['example_cookie_name']
# 重定向历史
r.history
# requests自带的json解码器,一般用于api接口请求,正常网页调用这个方法一般都会报错
r.json()
附:http状态码表
状态码 状态码英文名称 中文描述 1xx 信息,服务器收到请求,需要请求者继续执行操作 100 Continue 继续。客户端应继续其请求 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 2xx 成功,操作被成功接收并处理 200 OK 请求成功。一般用于GET与POST请求 201 Created 已创建。成功请求并创建了新的资源 202 Accepted 已接受。已经接受请求,但未处理完成 203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 206 Partial Content 部分内容。服务器成功处理了部分GET请求 3xx 重定向,需要进一步的操作以完成请求 300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI 303 See Other 查看其它地址。与301类似。使用GET和POST请求查看 304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 305 Use Proxy 使用代理。所请求的资源必须通过代理访问 306 Unused 已经被废弃的HTTP状态码 307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向 4xx 客户端错误,请求包含语法错误或无法完成请求 400 Bad Request 客户端请求的语法错误,服务器无法理解 401 Unauthorized 请求要求用户的身份认证 402 Payment Required 保留,将来使用 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 405 Method Not Allowed 客户端请求中的方法被禁止 406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求 407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 408 Request Time-out 服务器等待客户端发送的请求时间过长,超时 409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 410 Gone 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息 412 Precondition Failed 客户端请求信息的先决条件错误 413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理 415 Unsupported Media Type 服务器无法处理请求附带的媒体格式 416 Requested range not satisfiable 客户端请求的范围无效 417 Expectation Failed 服务器无法满足Expect的请求头信息 5xx 服务器错误,服务器在处理请求的过程中发生了错误 500 Internal Server Error 服务器内部错误,无法完成请求 501 Not Implemented 服务器不支持请求的功能,无法完成请求 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求 505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理
2. BeautifulSoup
html或xml解析工具
2.1. 创建文档树
2.1.1. 输入
可以传入一个字符串、一个字节流或一个文件句柄来获取一个根节点
soup = BeautifulSoup(open("index.html", 'rb'))
soup = BeautifulSoup("<html>data</html>")
2.1.2. 指定文档解析器
BeautifulSoup支持的文档解析器如下
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup, "html.parser") |
Python的内置标准库执行速度适中文档容错能力强 | Python 2.7.3 or 3.2.2 前的版本中文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") |
速度快文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup, ["lxml-xml"]) 或BeautifulSoup(markup, "xml") |
速度快唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup, "html5lib") |
最好的容错性以浏览器的方式解析文档生成HTML5格式的文档 | 速度慢不依赖外部扩展 |
默认使用 html.parser
作为解析器,但推荐使用lxml
作为解析器,因为效率更高
2.1.3. 编码
如果传入的是一个字节流或一个文件句柄,会自动识别当前文档编码并转换成Unicode编码输出。soup.original_encoding
属性记录了自动识别编码的结果
大多数时候,自动探测的结果还是可靠的
可以通过下面方式指定或排除编码方式
soup = BeautifulSoup(markup, from_encoding="iso-8859-8") # 指定
soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"]) # 自动探测是排除这种编码格式
2.2. 文档树的节点
Beautiful Soup将复杂HTML文档转换成一棵DOM文档树,每个节点都是Python对象,所有对象可以归纳为4种: BeautifulSoup
, Tag
, NavigableString
, Comment
对于一棵DOM文档树,根节点的类型为BeautifulSoup
,叶子节点的类型为 NavigableString
,其他节点的类型为 Tag
后面以下面为例,介绍节点的通用特性
# 这里去掉换行符是为了让后面展示的效果更加好看,否则换行符会被当做一个节点进行输出
>>> html = '''<bookstore>
... <book>
... <title lang="en">Harry Potter</title>
... <author>J K. Rowling</author>
... <year>2005</year>
... <price>29.99</price>
... </book>
... </bookstore>'''.replace('\n', '')
>>> soup = BeautifulSoup(html, 'lxml')
>>> tag = soup.bookstore.book
>>> ns = tag.author.string
2.2.1. 打印输出
>>> tag
<book><title lang="en">Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book>
# 虽然输出只能是Unicode编码格式的,但可以使用下面的方法重新解码成字节流输出,而且还会增加缩进效果,如果不指定编码格式,则只会缩进输出
>>> tag.prettify('gbk') # bytes
b'<book>\n <title lang="en">\n Harry Potter\n </title>\n <author>\n J K. Rowling\n </author>\n <year>\n 2005\n </year>\n <price>\n 29.99\n </price>\n</book>'
>>> print(tag.prettify()) # str
<book>
<title lang="en">
Harry Potter
</title>
<author>
J K. Rowling
</author>
<year>
2005
</year>
<price>
29.99
</price>
</book>
>>> tag.get_text() # str
'Harry PotterJ K. Rowling200529.99'
2.2.2. 相等比较
>>> import copy
>>> soup_ = copy.copy(soup)
>>> soup_ == soup # 只需要值和属性相同就可以了
True
>>> soup_ is soup # 严格相等,即是否指向同一个内存空间
False
2.2.3. BeautifulSoup对象和Tag对象共有属性
特别地,有些方法会有复数形式,表示返回的都是一个迭代器,会以注释形式标示出来
# 当前节点的名字
# 可修改
>>> tag.name # str
'book'
# 当前节点的属性
# 可增删改
>>> tag.title.attrs # dict
{'lang': 'en'}
>>> tag.title.get('lang') # 这种取值是比较安全的
'en'
>>> tag.title['lang']
'en'
# 直接 `.` + 子节点的 name 获取节点
# 特别地,如果输入不是以html和body标签开头,bs会默认添加`.html` 和`.body` 节点,所以根节点一定存在 `.html` 和`.body` 节点
>>> tag.title
<title lang="en">Harry Potter</title>
# 列出当前节点的所有儿子节点的值
# 注意,没有 .content 属性
>>> tag.contents # list
[<title lang="en">Harry Potter</title>, <author>J K. Rowling</author>, <year>2005</year>, <price>29.99</price>]
# 等价于 iter(tag.contents)
>>> tag.children
<list_iterator object at 0x7fcf94099e80>
# 递归查找当前节点的所有子孙节点
# 等价于先查询 contents[0] ,然后 .next_element 直到 .next_sibling.previous_element
>>> for child in tag.descendants: child
<title lang="en">Harry Potter</title>
'Harry Potter'
<author>J K. Rowling</author>
'J K. Rowling'
<year>2005</year>
'2005'
<price>29.99</price>
'29.99'
# 获取当前节点的叶子节点,
# 如果tag非叶子节点,但只有一个子节点,也可以使用这个方法
# NavigableString 对象的值可以修改
>>> tag.title.string # NavigableString
'Harry Potter'
# 获取当前节点的所有叶子节点
# 等价于调用 .descendants 后,再循环调用每个节点的 .string 方法
# .stripped_strings 等价过滤掉空行和空白符
>>> for s in tag.strings: s
'Harry Potter'
'J K. Rowling'
'2005'
'29.99'
2.2.4. Tag对象特有属性
# 下一个兄弟节点
# .next_siblings
>>> tag.author.next_sibling
<year>2005</year>
# 上一个兄弟节点
# .previous_siblings
>>> tag.author.previous_sibling
<title lang="en">Harry Potter</title>
2.2.5. Tag对象和NavigableString对象共有属性
# 下一个元素节点,下一个节点有可能是兄弟节点,也有可能是子节点
# .next_elements
>>> tag.next_element
<title lang="en">Harry Potter</title>
# 上一个元素节点
# .previous_elements
>>> tag.previous_element
<bookstore><book><title lang="en">Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book></bookstore>
# 父亲节点
# .parents
>>> tag.parent
<bookstore><book><title lang="en">Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book></bookstore>
2.3. 节点定位
非叶子节点都有节点定位的方法
2.3.1. 节点方式查询
find(name=None, attrs={}, recursive=True, text=None, **kwargs)
find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
find
返回的是一个节点,find_all
返回的是一个节点集合
特别地,x.find_all()
等价于 x()
2.3.1.1. name 参数查询
# 字符串格式
>>> soup.find('title')
<title lang="en">Harry Potter</title>
# 列表格式
>>> soup.find(['titl', 'author'])
<author>J K. Rowling</author>
# 正则表达式
>>> soup.find(re.compile('ti.*'))
<title lang="en">Harry Potter</title>
# 方法
>>> soup.find(lambda x: x and x.has_attr('lang'))
<title lang="en">Harry Potter</title>
# 等价于 soup.find()
>>> soup.find(True)
<html><body><bookstore><book><title lang="en">Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book></bookstore></body></html>
2.3.1.2. attrs 参数查询
可传入的类型和name
的一样
>>> soup.find(attrs={'lang': 'en'})
<title lang="en">Harry Potter</title>
# 也可以使用显示参数传入方法,与上面的方法是等价的
# 属性值与预留字发生冲突时,需要加下划线区分,例如 class -> class_, name -> name_
# 如果属性值包含特殊符号,则只能使用上面的方法,例如 data-foo
>>> soup.find(lang='en')
<title lang="en">Harry Potter</title>
特别地,当属性为 class
时,会出现用空格隔开的多属性值的情况,例如 <title class="en ch">Harry Potter</title>
,此时,只写一个属性值即可,即 soup.find(class_='ch')
2.3.1.3. text 参数查询
如果只用这种方式查询,则只会返回一个叶子节点
>>> soup.find(text='Harry Potter')
'Harry Potter'
2.3.1.4. 其他参数设置
recursive=False
表示只查询当前节点的子节点
limit
参数用于限制返回的条数
2.3.1.5. 更多扩展的 find 方法
- find_parents() 和 find_parent()
- find_next_siblings() 和 find_next_sibling()
- find_previous_siblings() 和 find_previous_sibling()
- find_all_next() 和 find_next()
- find_all_previous() 和 find_previous()
2.3.2. css方式查询
对css了解不是很深入,所以用得比较少
# 通过标签名查找:
soup.select('a') # 返回所有的a标签 list
# 通过类名查找:
soup.select('.sister') # 返回所有包含class="sister"的标签 list
soup.select('a.sister')
# 通过 id 名查找:
soup.select('#link1') # 返回所有包含 id="link1"的标签 list
soup.select("p#link1")
# 组合查找:
soup.select('p #link1') # 查找所有 p 标签中,id 等于 link1的标签 注意区分soup.select('p#link1')
soup.select("html head title") or soup.select("body a") # 逐层查找
soup.select("p > a") # 直接子标签查找 注意是子标签不是子孙标签
# 通过是否存在某个属性和属性的值来查找:
soup.select('a[href]')
soup.select('a[href="http://example.com/elsie"]')
soup.select('a[href^="http://example.com/"]') # ^= 开头
soup.select('a[href$="tillie"]') # $= 结尾
soup.select('a[href*=".com/el"]') # *= 包含
3. references
https://requests.readthedocs.io/zh_CN/latest/api.html
https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/