当前位置:   article > 正文

Python爬虫基础以及示例讲解

Python爬虫基础以及示例讲解

爬虫简介

网络爬虫

爬虫指在使用程序模拟浏览器向服务端发出网络请求,以便获取服务端返回的内容。

但这些内容可能涉及到一些机密信息,所以爬虫领域目前来讲是属于灰色领域,切勿违法犯罪。

爬虫本身作为一门技术没有任何问题,关键是看人们怎么去使用它

《中华人民共和国刑法》第二百八十五条规定:非法获取计算机信息系统数据、非法控制计算机信息系统罪,是指违反国家规定,侵入国家事务、国防建设、尖端科学技术领域以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,情节严重的行为。刑法第285条第2款明确规定,犯本罪的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

《反不正当竞争法》第九条规定:以不正当手段获取他人商业秘密的行为即已经构成侵犯商业秘密。而后续如果进一步利用,或者公开该等信息,则构成对他人商业秘密的披露和使用,同样构成对权利人的商业秘密的侵犯。

《刑法》第二百八十六条规定:违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,构成犯罪,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。而违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,也构成犯罪,依照前款的规定处罚。

《网络安全法》第四十四条规定:任何个人和组织不得窃取或者以其他非法方式获取个人信息。因此,如果爬虫在未经用户同意的情况下大量抓取用户的个人信息,则有可能构成非法收集个人信息的违法行为。

《民法总则》第111条规定:任何组织和个人需要获取他人个人信息的,应当依法取得并确保信息安全。不得非法收集、使用、加工、传输他人个人信息

爬虫分类

根据爬虫的应用范畴,可有一些三种区分:

通用爬虫

搜索引擎本质就是一个巨大的爬虫,首先该爬虫会爬取整张页面,并且对该页面做备份,之后对其进行数据内容处理如抓取关键字等,然后向用户提供检索接口。

聚焦式爬虫

只关注于页面上某一部分内容,如只关注图片、链接等。

增量式爬虫

用于检索内容是否更新,如开发了一个增量式爬虫每天查看一下云崖博客有没有更新,有更新就爬下来等等…

robots协议

robots协议是爬虫领域非常出名的一种协议,由门户网站提供。

它规定了该站点哪些内容允许爬取,哪些内容不允许爬取。

如果爬取不允许的内容,可对其追究法律责任。

requests模块

requests模块是Python中发送网络请求的一款非常简洁、高效的模块。

pip install requests

  • 1
  • 2

发送请求

支持所有的请求方式:

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/")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

当请求发送成功后,会返回一个response对象。

get请求

基本的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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

post请求

基本的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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

高级参数

更多参数:

参数描述
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)


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207

session对象

如果爬取一个网站,该网站可能会返回给你一些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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

response对象

以下是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)

  • 1
  • 2
  • 3
  • 4
  • 5

下载文件

使用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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

json返回内容

如果确定返回内容是json数据,则可以通过response.json进行查看:

import requests
response = requests.get("http://127.0.0.1:5000/index")
print(response.json())

  • 1
  • 2
  • 3
  • 4

历史记录

如果访问一个地址却被重定向了,被重定向的地址会被存放到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]>]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果在请求时,指定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)
# []

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

bs4模块

request模块可以发送请求,获取HTML文档内容。

bs4模块可以解析出HTMLXML文档的内容,如快速查找标签等等。

pip3 install bs4

  • 1
  • 2

bs4模块只能在Python中使用

bs4依赖解析器,虽然有自带的解析器,但是目前使用最多的还是lxml

pip3 install lxml

  • 1
  • 2

img

基本使用

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

结果如下:

+------+--------------------------------------------------+--------------------+----------+
| 编号 |                     歌曲名称                     |        歌手        | 歌曲时长 |
+------+--------------------------------------------------+--------------------+----------+
|  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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

HTML文档

准备一个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>头像:&nbsp;&nbsp;<input type="file" name="avatar"/></p>
        <p>用户名:&nbsp; <input type="text" name="username" placeholder="请输入用户名"/></p>
        <p>密码:&nbsp;&nbsp;&nbsp;&nbsp;<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="重新填写信息"/>&nbsp;<input type="submit" value="提交注册信息">&nbsp;<input type="butoon"
                                                                                                  value="联系客服" disabled>
    </p>

</form>
</body>
</html>


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

基本选择器

基本选择器如下 :

选择器方法描述
TagName唯一选择器,根据标签名来选择
find()唯一选择器,可根据标签名、属性来做选择
select_one()唯一选择器,可根据CSS选择器语法做选择
find_all()集合选择器,可根据标签名、属性来做选择
select()集合选择器,可根据CSS选择器语法做选择

.TagName选择器只会拿出第一个匹配的内容,必须根据标签名选择:

input = soup.input
print(input)
# <input name="avatar" type="file"/>

  • 1
  • 2
  • 3
  • 4

.find()选择器只会拿出第一个匹配的内容,可根据标签名、属性来做选择

input= soup.find("input",attrs={"name":"username","type":"text"})  # attrs指定属性
print(input)

# <input name="username" placeholder="请输入用户名" type="text"/>

  • 1
  • 2
  • 3
  • 4
  • 5

.select_one()根据css选择器来查找标签,只获取第一个:

input = soup.select_one("input[type=text]")
print(input)
# <input name="username" placeholder="请输入用户名" type="text"/>

  • 1
  • 2
  • 3
  • 4

.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"/>]


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

.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"/>]

  • 1
  • 2
  • 3
  • 4
  • 5

关系与操作

使用较少,选读:

属性/方法描述
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>

  • 1
  • 2
  • 3
  • 4
  • 5

.descendants获取所有的后代标签,返回生成器

form = soup.find("form")
print(form.descendants)

# <generator object descendants at 0x00000271C8F0ACA8>

  • 1
  • 2
  • 3
  • 4
  • 5

.index()检查某个标签在当前标签中的索引值

body = soup.find("body")
form = soup.find("form")
print(body.index(form)) 
# 3

  • 1
  • 2
  • 3
  • 4
  • 5

.clear()删除后代标签,保留本标签,相当于清空

form = soup.find("form")
form.clear()
print(form)  # None
print(soup)
# 清空了form

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

.decompose()删除标签本身(包括所有后代标签)

form = soup.find("form")
form..decompose()
print(form)  # None
print(soup)
# 删除了form

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

.extract().decomponse()效果相同,但会返回被删除的标签

form = soup.find("form")
form..extract()
print(form)  # 被删除的内容
print(soup)
# 被删除了form

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

.decode()将当前标签与后代标签转换字符串,.decode_contents()将当前标签的后代标签转换为字符串

form = soup.find("form")
print(form.decode())  # 包含form
print(form.decode_contents())  # 不包含form

  • 1
  • 2
  • 3
  • 4

.encode()将当前标签与后代标签转换字节串,.encode_contents()将当前标签的后代标签转换为字节串

form = soup.find("form")
print(form.encode())  # 包含form
print(form.encode_contents())  # 不包含form

  • 1
  • 2
  • 3
  • 4

标签内容

以下方法都比较常用:

属性/方法描述
name获取标签名称
attrs获取标签属性
text获取该标签下的所有文本内容(包括后代)
string获取该标签下的直系文本内容
is_empty_element判断是否是空标签或者自闭合标签
get_text()获取该标签下的所有文本内容(包括后代)
has_attr()检查标签是否具有该属性

.name获取标签名称

form = soup.find("form")
print(form.name)

# form

  • 1
  • 2
  • 3
  • 4
  • 5

.attrs获取标签属性

form = soup.find("form")
print(form.attrs)

# {'action': '#', 'method': 'post', 'enctype': 'multipart/form-data'}

  • 1
  • 2
  • 3
  • 4
  • 5

.is_empty_element判断是否是空标签或者自闭合标签

input = soup.find("input")
print(input.is_empty_element)
# True

  • 1
  • 2
  • 3
  • 4

.get_text()text获取该标签下的所有文本内容(包括后代)

form = soup.find("form")
print(form.get_text())
print(form.text)

  • 1
  • 2
  • 3
  • 4

string获取该标签下的直系文本内容

form = soup.find("form")
print(form.get_text())
print(form.string)

  • 1
  • 2
  • 3
  • 4

.has_attr()检查标签是否具有该属性

form = soup.find("form")
print(form.has_attr("action"))
# True

  • 1
  • 2
  • 3
  • 4

xPath模块

xPath模块的作用与bs4相同,都是查找标签。

但是xPath模块的通用性更强,它的语法规则并不限于仅在Python中使用。

作为一门小型的专业化查找语言,xPathPython中被集成在了lxml模块中,所以直接下载安装就可以开始使用了。

pip3 install lxml

  • 1
  • 2

加载文档:

from lxml import etree

# 解析网络爬取的html源代码
root = etree.HTML(response.text,,etree.HTMLParser())  # 加载整个HTML文档,并且返回根节点<html>

# 解析本地的html文件
root = etree.parse(fileName,etree.HTMLParser())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

基本选取符

基本选取符:

符号描述
/从根节点开始选取
//不考虑层级关系的选取节点
.选取当前节点
选取当前节点的父节点
@属性检测
[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)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

表达式形式

你可以指定逻辑运算符,大于小于等。

from lxml import etree

root = etree.parse("./testDataDocument.html",etree.HTMLParser())

# 返回属性值price大于或等于20的标签
price_ele_list = root.xpath("//*[@price>=20]")
print(price_ele_list)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

xPath轴关系

xPath中拥有轴这一概念,不过相对来说使用较少,它就是做关系用的。了解即可:

示例说明
ancestorxpath(‘./ancestor:
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/761001
推荐阅读
相关标签