requests
安装
用途
用于模拟请求网页链接,从而获得数据。
相关文档
基本用法
Response
复制 import requests
r = requests.get('https://www.baidu.com/')
print(type(r.status_code), r.status_code)
print(type(r.headers), r.headers)
print(type(r.cookies), r.cookies)
print(type(r.url), r.url)
print(type(r.history), r.history)
response.cookies:网页的cookies
response.status_code:网页的状态码
运行结果:
复制 <class 'int'> 200
...
<class 'str'> https://www.baidu.com/
<class 'list'> []
Status Code 常用来判断请求是否成功,Requests 还提供了一个内置的 Status Code 查询对象 requests.codes。
复制 import requests
r = requests.get('http://www.jianshu.com')
exit() if not r.status_code == requests.codes.ok else print("请求成功")
返回码和相应的查询条件:
复制 # Informational.
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),
# Redirection.
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0
# Client Error.
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),
# Server Error.
500: ('internal_server_error', 'server_error', '/o\\', '✗'),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication')# Informational.
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),
# Redirection.
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0
# Client Error.
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),
# Server Error.
500: ('internal_server_error', 'server_error', '/o\\', '✗'),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication')
get请求
没有参数
复制 import requests
response = requests.get("http://httpbin.org/get")
print(response.text)
运行结果:
复制 {
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0",
"X-Amzn-Trace-Id": "Root=1-5f5f0e95-cd68f9cadd5bdc14a9bd5868"
},
"origin": "111.122.73.198",
"url": "http://httpbin.org/get"
}
如果返回的内容为json数据可以,通过respons.json()
进行打印数据,这样返回的格式为json格式。
有参数
可以通过在url里面添加参数,但是这样有点麻烦,而requests里面可以通过params进行处理。
复制 import requests
params = {
"name":"code"
}
response = requests.get("http://httpbin.org/get",params=params)
print(response.json())
运行结果:
复制 {'args': {'name': 'code'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-5f5f0fdc-d0f3dc64548f4f0896b8b747'}, 'origin': '111.122.73.198', 'url': 'http://httpbin.org/get?name=code'}
获取二进制数据
图片、音频、视频这些文件都是本质上由二进制码组成的,由于有特定的保存格式和对应的解析方式,才可以看到这些形形色色的多媒体。所以想要抓取,那就需要拿到二进制码。
在爬取二进制数据时,应该使用content属性。
复制 import requests
response = requests.get("https://github.com/favicon.ico")
print(response.text)
print((response.content))
运行结果:
接下来将二进制数据,保存为图片
复制 import requests
response = requests.get("https://github.com/favicon.ico")
with open('favicon.ico','wb') as f:
f.write(response.content)
运行后,会在同一目录下生产图标
添加Headers
通过requests
里面的headers参数,就可以添加headers了。
复制 import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
response = requests.get("http://httpbin.org/get",headers=headers)
print(response.json())
实际情况下,添加headers有时候需要添加很多
如下:
可以观察到,请求头的格式为A:B
,由于粘贴复制过来后,还是需要进行修改为字典形式,有点麻烦。接下来就动手弄一个方便转化headers的python代码。
复制 import requests
headers = """
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Cache-Control: no-cache
Host: httpbin.org
Pragma: no-cache
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
"""
def str_to_dict(headers):
return dict({header[0].strip(): header[1].strip() for header in
[header.split(":") for header in headers.split('\n') if header]})
url = "http://httpbin.org/get"
response = requests.get(url=url,headers=str_to_dict(headers))
print(response.status_code)
运行结果:
解析headers的代码,如下:
复制 def str_to_dict(headers):
return dict({header[0].strip(): header[1].strip() for header in
[header.split(":") for header in headers.split('\n') if header]})
这样就能够直接粘贴复制headers过来,自动解析为字典形式了。
post请求
post方法与get方法基本用法一样,只是请求方式不同。
没有表单数据
复制 import requests
r = requests.post("http://httpbin.org/post")
print(r.text)
有表单数据
通过data传递表单数据。
复制 import requests
data = {'name': 'germey', 'age': '22'}
r = requests.post("http://httpbin.org/post", data=data)
print(r.text)
其他请求方式
复制 r = requests.put('http://httpbin.org/put')
r = requests.delete('http://httpbin.org/delete')
r = requests.head('http://httpbin.org/get')
r = requests.options('http://httpbin.org/get')
高级用法
上传文件
通过files
关键字参数进行上传二进制数据,即文件。
复制 import requests
files = {
'file':open('185494.jpg','rb')
}
response = requests.post('http://httpbin.org/post',files=files)
print(response.text)
Cookies
复制 import requests
response = requests.get('https://www.baidu.com')
print(response.cookies)
for key,value in response.cookies.items():
print(key+"="+value)
运行结果:
复制 <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
BDORZ=27315
cookies在请求头里面进行设置的。
复制 import requests
headers = {
'Cookie': 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0',
'Host': 'www.zhihu.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36',
}
r = requests.get('https://www.zhihu.com', headers=headers)
print(r.text)
当然也可以通过 cookies 参数来设置,不过这样就需要构造 RequestsCookieJar 对象,而且需要分割一下 Cookies ,相对繁琐,不过效果是相同的,实例如下:
复制 import requests
cookies = 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0'
jar = requests.cookies.RequestsCookieJar()
headers = {
'Host': 'www.zhihu.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
}
for cookie in cookies.split(';'):
key, value = cookie.split('=', 1)
jar.set(key, value)
r = requests.get('http://www.zhihu.com', cookies=jar, headers=headers)
print(r.text)
新建了一个 RequestCookieJar 对象
将复制下来的 Cookies 利用 split() 方法分割,利用 set() 方法设置好每一个 Cookie 的 key 和 value
通过调用 Requests 的 get() 方法并传递给 cookies 参数即可,当然由于知乎本身的限制, headers 参数也不能少,只不过不需要在原来的 headers 参数里面设置 Cookie 字段了
会话维持
测试网址:http://httpbin.org/cookies/set/number/123456789
解决这个问题的主要方法就是维持同一个会话,也就是相当于打开一个新的浏览器选项卡而不是新开一个浏览器。但是又不想每次设置 Cookies,那该怎么办?这时候就有了新的利器 Session对象。
利用它,可以方便地维护一个会话,而且不用担心 Cookies 的问题,它会自动处理好
复制 import requests
requests.get("http://httpbin.org/cookies/set/number/123456789")
r = requests.get("http://httpbin.org/cookies")
print(r.text)
运行结果:
不能成功获取到设置的 Cookies
使用session测试
复制 import requests
s = requests.Session()
s.get("http://httpbin.org/cookies/set/number/123456789")
r = s.get("http://httpbin.org/cookies")
print(r.text)
运行结果:
复制 {
"cookies": {
"number": "123456789"
}
}
利用 Session 可以做到模拟同一个会话,而且不用担心 Cookies 的问题,通常用于模拟登录成功之后再进行下一步的操作。
SSL证书验证
Requests 提供了证书验证的功能,当发送 HTTP 请求的时候,它会检查 SSL 证书,可以使用 verify 这个参数来控制是否检查此证书,其实如果不加的话默认是 True,会自动验证。
复制 import requests
response = requests.get("https://www.12306.cn")
print(response.status_code)
运行结果:
复制 requests.exceptions.SSLError:...
提示一个错误,叫做 SSLError,证书验证错误。所以如果请求一个 HTTPS 站点,但是证书验证错误的页面时,就会报这样的错误
如何避免这个错误,只需把 verify 这个参数设置为 False 即可。
复制 import requests
response = requests.get("https://www.12306.cn",verify=False)
print(response.status_code)
运行结果:
复制 InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning)
200
不过发现报了一个警告,它提示建议给它指定证书。
可以通过设置忽略警告的方式来屏蔽这个警告:
复制 import requests
import urllib3
urllib3.disable_warnings()
response = requests.get("https://www.12306.cn",verify=False)
print(response.status_code)
运行结果:
或者通过捕获警告到日志的方式忽略警告:
复制 import requests
import logging
logging.captureWarnings(True)
response = requests.get("https://www.12306.cn",verify=False)
print(response.status_code)
可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组
复制 import requests
response = requests.get('https://www.12306.cn',verify="E:/SRCA.crt")
print(response.status_code)
代理设置
设置代理,在 Requests 中需要用到 proxies
这个参数
复制 import requests
# 代理池
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
r = requests.get('https://www.taobao.com',proxies=proxies)
print(r.status_code)
若代理需要使用 HTTP Basic Auth
,可以使用类似http://user:password@host:port
这样的语法来设置代理
除了基本的 HTTP 代理,Requests 还支持 SOCKS 协议
的代理。
需要安装 Socks 这个库,命令如下:
复制 pip install "requests[socks]"
然后就可以使用 SOCKS 协议代理了
复制 socks5://user:password@host:port
复制 import requests
proxies = {
'http': 'socks5://user:password@host:port',
'https': 'socks5://user:password@host:port'
}
requests.get('https://www.taobao.com', proxies=proxies)
超时设置
设置超时时间需要用到 timeout 参数。这个时间的计算是发出 Request 到服务器返回 Response 的时间
复制 import requests
r = requests.get('https://www.taobao.com', timeout=1)
print(r.status_code)
通过这样的方式,可以将超时时间设置为 1 秒,如果 1 秒内没有响应,那就抛出异常。
实际上请求分为两个阶段,即 connect(连接)和 read(读取)。
上面的设置 timeout 值将会用作 connect 和 read 二者的 timeout 总和。
如果要分别指定,就可以传入一个元组:
复制 r = requests.get('https://www.taobao.com', timeout=(5, 11))
如果想永久等待,那么可以直接将 timeout 设置为 None,或者不设置直接留空,因为默认是 None。这样的话,如果服务器还在运行,但是响应特别慢,那就慢慢等吧,它永远不会返回超时错误的。
复制 r = requests.get('https://www.taobao.com', timeout=None)
或者
r = requests.get('https://www.taobao.com')
身份认证
在访问网页的时候,有可能遇到这样的页面认证,可以使用Requests字典的身份认证功能。
复制 import requests
from requests.auth import HTTPBasicAuth
r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))
print(r.status_code)
更简便的认证,可以直接传一个元组,它会默认使用 HTTPBasicAuth 这个类来认证
复制 import requests
r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)
Requests 还提供了其他的认证方式,如 OAuth 认证,不过需要安装 oauth 包
复制 pip install requests_oauthlib
使用OAuth1方法认证
复制 import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url,auth=auth)
Prepared Request
当从 API 或者会话调用中收到一个 Response 对象时,request 属性其实是使用了 PreparedRequest。有时在发送请求之前,需要对 body 或者 header (或者别的什么东西)做一些额外处理。
复制 from requests import Request,Session
url = 'http://httpbin.org/post'
data = {
'name':'angle',
}
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
}
s = Session()
req = Request('POST',url,data=data,headers=headers)
prepped = s.prepare_request(req)
r = s.send(prepped)
print(r.text)
引入 Request,然后用 url、data、headers 参数构造了一个 Request 对象,需要再调用 Session 的 prepare_request() 方法将其转换为一个 Prepared Request 对象,然后调用 send() 方法发送即可
有了 Request 这个对象,就可以将一个个请求当做一个独立的对象来看待,这样在进行队列调度的时候会非常方便
正则表达式
实战-抓取猫眼电影排行
任务
提取出猫眼电影 TOP100 榜的电影名称、时间、评分、图片等信息,提取的结果以文件形式保存下来。
分析
在图中可以清楚地看到影片的信息,页面中显示的信息有影片名称、主演、上映时间、上映地区、评分、图片等。
之后点击下一页,观察url的变化。
可以发现http://maoyan.com/board/4?offset=10
比之前的url多了一个参数offset,参数值为10,目前显示的排行数据11~20,可以判断offset是一个偏移量的参数再点击下一页,发现页面的 URL 变成了:http://maoyan.com/board/4?offset=20
,参数 offset 变成了 20,而显示的结果是排行 21-30 的电影。
可以判断offset是一个偏移量,如果偏移值为n,则当前页面的序号为n+1到n+10,这样就可以更根据offset获取top100的所有电影信息了。
抓取
抓取首页
定义一个get_frist_page()函数方法,实现抓取首页,代码实现,并通过str_to_dict()函数方法,解析headers。
复制 import requests,re,time
headers = """
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Cookie: __mta=213316523.1600064586376.1600071552350.1600071714950.12; uuid_n_v=v1; uuid=BF1A8980F65211EA91B4F5F3486B36AE5527F4FDA8BA451BBFAF2262F89F4D1E; _csrf=6262310ffe9bccdefebbb75223f0a636294a6118f10201d6207cff27cfa8c309; mojo-uuid=f688f38edd40a41769114ca40701dd6e; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1600064586; _lx_utm=utm_source%3Dgoogle%26utm_medium%3Dorganic; _lxsdk_cuid=1748b48012ac8-0c9d0a9c72ac5c-3b634404-144000-1748b48012bc8; _lxsdk=BF1A8980F65211EA91B4F5F3486B36AE5527F4FDA8BA451BBFAF2262F89F4D1E; mojo-session-id={"id":"1e4cb65fc0ee3717c231a3e0ff9cb3fa","time":1600071190284}; __mta=213316523.1600064586376.1600071198156.1600071412671.4; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1600071715; mojo-trace-id=18; _lxsdk_s=1748bacc849-451-484-5fb%7C%7C27
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
"""
def str_to_dict(headers):
return dict({header[0].strip(): header[1].strip() for header in
[header.split(":") for header in headers.split('\n') if header]})
def get_frist_page(url):
response = requests.get(url=url,headers=str_to_dict(headers))
if response.status_code == requests.codes.ok:
return response.text
return None
def run(url):
response = get_frist_page(url=url)
print(response)
if __name__ == "__main__":
url = "https://maoyan.com/board/4"
run(url=url)
一定要加上请求头,不然猫眼网站会禁止访问
使用正则提取数据
现在抓取了首页,需要进一步抓取相关信息,接下来回到网页看一下页面的真实源码,在开发者工具中 Network 监听,然后查看一下源代码,注意这里不要在 Elements 选项卡直接查看源码,此处的源码可能经过 JavaScript 的操作而和原始请求的不同,需要从Network选项卡部分查看原始请求得到的源码。
先看下个电影的相关内容
复制 <dd>
<i class="board-index board-index-1">1</i>
<a href="/films/13824" title="射雕英雄传之东成西就" class="image-link" data-act="boarditem-click" data-val="{movieId:13824}">
<img src="//s3plus.meituan.net/v1/mss_e2821d7f0cfe4ac1bf9202ecf9590e67/cdn-prod/file:5788b470/image/loading_2.e3d934bf.png"
alt="" class="poster-default" />
<img data-src="https://p0.meituan.net/movie/53d18b640f8af0c47d7c95488450c265388754.jpg@160w_220h_1e_1c" alt="射雕英雄传之东成西就"
class="board-img" />
</a>
<div class="board-item-main">
<div class="board-item-content">
<div class="movie-item-info">
<p class="name"><a href="/films/13824" title="射雕英雄传之东成西就" data-act="boarditem-click" data-val="{movieId:13824}">射雕英雄传之东成西就</a></p>
<p class="star">
主演:张国荣,梁朝伟,张学友
</p>
<p class="releasetime">上映时间:1993-02-05(中国香港)</p>
</div>
<div class="movie-item-number score-num">
<p class="score"><i class="integer">8.</i><i class="fraction">8</i></p>
</div>
</div>
</div>
</dd>
现在需要从这段里面提取需要的信息,书写正则表达式
复制 排名信息:<i.*?board-index.*?>(.*?)</i>
进一步编写:
封面图片:<img.*?data-src="(.*?)"
继续:
电影名称:<p.*?name.*?title="(.*?)"
依次类推:
所有信息:<i.*?board-index.*?>(.*?)</i>.*?<img.*?data-src="(.*?)".*?<p.*?name.*?title="(.*?)".*?/p>.*?<p.*?releasetime">(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>
这样就可以提取第一页的内容,代码如下:
复制 def parse_first_page(response):
pattern = '<i.*?board-index.*?>(.*?)</i>.*?<img.*?data-src="(.*?)".*?<p.*?name.*?title="(.*?)".*?/p>.*?<p.*?releasetime">(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>'
pattern = re.compile(pattern,re.S)
items = re.findall(pattern,response)
for item in items:
print(item)
def run(url):
response = get_frist_page(url=url)
parse_first_page(response)
运行结果:
复制 ('1', 'https://p0.meituan.net/movie/53d18b640f8af0c47d7c95488450c265388754.jpg@160w_220h_1e_1c', '射雕英雄传之东成西就', '上映时间:1993-02-05(中国香港)', '8.', '8')
('2', 'https://p0.meituan.net/movie/df15efd261060d3094a73ef679888d4f238149.jpg@160w_220h_1e_1c', '十二怒汉', '上映时间:1957-04-13(美国)', '9.', '1')
('3', 'https://p1.meituan.net/movie/6a964e9cee699267053bd6a4bf6f2671195394.jpg@160w_220h_1e_1c', '剪刀手爱德华', '上映时间:1990-12-06(美国)', '8.', '8')
('4', 'https://p0.meituan.net/movie/223c3e186db3ab4ea3bb14508c709400427933.jpg@160w_220h_1e_1c', '乱世佳人', '上映时间:1939-12-15(美国)', '9.', '1')
('5', 'https://p1.meituan.net/movie/226f647bd2bf6f45d8d503c0ae8d920c341293.jpg@160w_220h_1e_1c', '初恋这件小事', '上映时间:2012-06-05', '8.', '8')
('6', 'https://p1.meituan.net/movie/b607fba7513e7f15eab170aac1e1400d878112.jpg@160w_220h_1e_1c', '泰坦尼克号', '上映时间:1998-04-03', '9.', '4')
('7', 'https://p0.meituan.net/movie/b3defc07dfaa1b6f5b74852ce38a3f8f242792.jpg@160w_220h_1e_1c', '搏击俱乐部', '上映时间:1999-09-10(意大利)', '8.', '8')
('8', 'https://p0.meituan.net/movie/609e45bd40346eb8b927381be8fb27a61760914.jpg@160w_220h_1e_1c', '海上钢琴师', '上映时间:2019-11-15', '9.', '3')
('9', 'https://p1.meituan.net/movie/0b0d45b58946078dd24d4945dd6be3b51329411.jpg@160w_220h_1e_1c', '甜蜜蜜', '上映时间:2015-02-13', '9.', '2')
('10', 'https://p0.meituan.net/movie/8112a8345d7f1d807d026282f2371008602126.jpg@160w_220h_1e_1c', '肖申克的救赎', '上映时间:1994-09-10(加拿大)', '9.', '5')
由于数据看起来比较杂乱,需要处理一下,遍历生成为字典
复制 def parse_first_page(response):
pattern = '<i.*?board-index.*?>(.*?)</i>.*?<img.*?data-src="(.*?)".*?<p.*?name.*?title="(.*?)".*?/p>.*?<p.*?releasetime">(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>'
pattern = re.compile(pattern,re.S)
items = re.findall(pattern,response)
for item in items:
yield {
'index': item[0],
'image': item[1],
'title': item[2].strip(),
'time': item[3].strip()[5:] if len(item[3]) > 5 else '',
'score': item[4].strip() + item[5].strip()
}
def run(url):
response = get_frist_page(url=url)
items = parse_first_page(response)
for item in items:
print(item)
存储数据
把数据存储到文本文件中,在这里直接写入到一个文本文件中,通过 json 库的 dumps() 方法实现字典的序列化,并指定 ensure_ascii 参数为 False,这样可以保证输出的结果是中文形式而不是 Unicode 编码。
复制 # 存储数据
def save_to_json(content):
with open('data.txt','a',encoding='utf-8') as f:
# print(type(json.dumps(content)))
f.write(json.dumps(content,ensure_ascii=False,))
def run(url):
response = get_frist_page(url=url)
items = parse_first_page(response)
for item in items:
save_to_json(item)
实战-分析ajax爬取今日头条街拍美图
什么是Ajax
Ajax,全称为 Asynchronous JavaScript and XML,即异步的 JavaScript 和 XML。
Ajax 请求:数据的加载是一种异步加载方式,原始的页面最初不会包含某些数据,原始页面加载完后会会再向服务器请求某个接口获取数据,然后数据再被处理才呈现到网页上。
Ajax分析方法
查看请求
开发者工具(F12) > Network
过滤请求
点击XHR,筛选ajax请求
Ajax结果提取
分析请求
打开开发者工具 > Network面板>选中XHR,筛选ajax请求
分析url参数
可以后面多了一个offset参数在变化,偏移量为20。其中keyword为搜索关键字,count为显示的数据条数。
抓取
获取数据
复制 headers = """
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
"""
def str_to_dict(headers):
return dict({header[0].strip(): header[1].strip() for header in
[header.split(":") for header in headers.split('\n') if header]})
def get_page(offset=0):
params = {
'offset': offset,
'format': 'json',
'keyword': '街拍',
'autoload': 'true',
'count': '20',
'cur_tab': '1',
'from': 'search_tab',
}
try:
response = requests.get(url,params=params,headers=str_to_dict(headers))
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
print(e.args)
获取图片
复制 def get_images(response):
if response:
items = response.get("data")
if items:
for item in items:
title = item.get("title")
images = item.get("image_list")
if title and images:
for image in images:
yield {
'image': image.get("url"),
'title': title,
}
存储图片
复制 def save_images(item):
title = item.get("title")
if not os.path.exists(title):
os.mkdir(title)
try:
image = item.get("image")
response = requests.get("http:"+image)
if response.status_code == 200:
# 读取二进制流数据
content = response.content
# 利用md5函数判断重复
filepath = "{0}/{1}.{2}".format(title,md5(content).hexdigest(),'jpg')
if not os.path.exists(filepath):
with open(filepath,'wb' ) as f:
f.write(response.content)
else:
print("Already Download {}".format(filepath))
except requests.ConnectionError as e:
print("Failed to Save Image")
主函数
复制 def main(offset=0):
response = get_page(offset)
for item in get_images(response):
print(item)
save_images(item)
启动
复制 import requests,os
from urllib.parse import urlencode
from multiprocessing import Pool
from hashlib import md5
...
GROUP_START = 1
GROUP_END = 20
def get_page(offset=0):
...
def get_images(response):
...
def save_images(item):
...
def main(offset=0):
...
if __name__ == "__main__":
# 开启进程池
pool = Pool()
groups = [x*20 for x in range(GROUP_START,GROUP_END+1)]
print(groups)
pool.map(main,groups)
pool.close()
pool.join()
selenium
安装
但是需要用浏览器来配合selenium使用,需要进行一些配置,才可以使用selenium
ChromeDriver
下载
selenium对chrome浏览器操作,需要进行以下配置
根据chrome版本进行下载ChromeDriver。
根据版本号,找到对应下载版本
配置环境变量
之后将下载后,解压得到exe文件进行配置相关的环境变量。
在windows平台下,直接把可执行文件拖入python的Scripts目录下,这样就配置到环境变量中了
在 Linux、Mac 下,需要将可执行文件配置到环境变量或将文件移动到属于环境变量的目录里。
例如移动文件到 /usr/bin 目录,首先命令行进入其所在路径,然后将其移动到 /usr/bin:
复制 sudo mv chromedriver /usr/bin
当然也可以将 ChromeDriver 配置到 $PATH
,首先可以将可执行文件放到某一目录,目录可以任意选择,例如将当前可执行文件放在 /usr/local/chromedriver
目录下,接下来可以修改 ~/.profile
文件,命令如下:
复制 export PATH="$PATH:/usr/local/chromedriver"
保存然后执行:
验证
在命令行下输入chromedriver
命令进行验证
然后在程序中测试
复制 from selenium import webdriver
browser = webdriver.Chrome()
如果有chrome浏览器自动打开,就代表配置成功。
GeckoDriver
下载
selenium对firefox浏览器操作,需要进行以下配置
配置环境变量
下载完成后将geckodriver可执行文件配置到环境变量中
在windows平台下,直接把可执行文件拖入python的Scripts目录下,这样就配置到环境变量中了
在 Linux、Mac 下,需要将可执行文件配置到环境变量或将文件移动到属于环境变量的目录里。
例如移动文件到 /usr/bin
目录,首先命令行进入其所在路径,然后将其移动到 /usr/bin:
复制 sudo mv geckodriver /usr/bin
当然也可以将 GeckoDriver
配置到 $PATH
,首先可以将可执行文件放到某一目录,目录可以任意选择,例如将当前可执行文件放在/usr/local/geckodriver
目录下,接下来可以修改 ~/.profile
文件,命令如下:
添加如下一句配置:
复制 export PATH="$PATH:/usr/local/geckodriver"
保存然后执行如下命令即可完成配置:
验证
在命令行下输入GeckoDriver命令进行验证
然后在程序中测试
复制 from selenium import webdriver
browser = webdriver.Firefox()
如果有Firefox浏览器自动打开,就代表配置成功
PhantomJS
下载
PhantomJS是一个无界面的浏览器.selenium支持PhantomJS,这样在运行的时候就不会弹出一个浏览器。
配置环境变量
配置环境和前面一样
验证
在命令行下输入PhantomJS命令进行验证有类似结果输出就代表配置环境成功
然后在程序中测试
复制 from selenium import webdriver
browser = webdriver.PhantomJS()
browser.get('https://www.baidu.com')
print(browser.current_url)
运行后会发现没有浏览器打开,但实际上phantomjs已经运行起来了
这里访问了百度,如果将当前url打印出来了就代表PhantomJS配置成功了
用途
selenium能够模拟用户打开浏览器,从而获取数据,能够解决动态渲染页面(ajax)的问题。
文档
用法
基本使用
复制 from selenium import webdriver
from selenium.webdriver.common.keys import Keys
复制 driver = webdriver.Chrome()
driver.get 方法将打开URL中填写的地址,WebDriver 将等待, 直到页面完全加载完毕(其实是等到”onload” 方法执行完毕),然后返回继续执行脚本。
复制 driver.get("http://www.baidu.com/")
复制 search = driver.find_element_by_id("kw")
复制 search.clear() # 清除
search.send_keys("python") # 输入
search.send_keys(Keys.RETURN) # enter键
如果想要浏览器一直显示,就不要输入这句。
复制 search.click() # 模拟鼠标的点击
填写表单
测试网站
导入模块
复制 from selenium.webdriver.support.ui import Select
Select类方法
通过 value 值来获取 select 列表的标签名
deselect_by_visible_text(text)
select框
复制 <select id="s1Id">
<option></option>
<option value="o1" id="id1">o1</option>
<option value="o2" id="id2">o2</option>
<option value="o3" id="id3">o3</option>
</select>
实例
复制 driver.get("http://sahitest.com/demo/selectTest.htm")
select1 = driver.find_element_by_id("s1Id")
s1 = Select(select1)
s1.select_by_index(1) # 选择第二项选项:o1
s1.select_by_value("o2") # 选择value="o2"的项
s1.select_by_visible_text("o3") # 选择text="o3"的值,即在下拉时我们可以看到的文本
拖拽
复制 from selenium import webdriver
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
url = "http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable"
driver.get(url)
source = driver.find_element_by_css_selector("#draggable") # 拖动的物品
target = driver.find_element_by_css_selector("#droppable") # 需要拖到的位置
# 创建动作链的实例
actions = ActionChains(driver)
# 调用拖拽方法
actions.drag_and_drop(source,target)
# 执行动作
actions.perform()
切换窗口、框架
复制 import time
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait # 等待页面加载某些元素
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# 获取driver
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)
# 发请求
driver.get('https://www.baidu.com')
# 定位标签并输入值然后点击
driver.find_element_by_id('kw').send_keys('python.org')
time.sleep(1)
driver.find_element_by_id('su').click()
# 在新窗口中,点击结果标签
wait.until(EC.presence_of_element_located((By.LINK_TEXT, 'Welcome to Python.org'))).click()
# driver.find_element_by_link_text('Welcome to Python.org').click()
# 所有打开的窗口都存在这个数组中
# print(driver.window_handles)
# 根据数组下标索引切换窗口
time.sleep(3)
driver.switch_to.window(driver.window_handles[1])
time.sleep(3)
driver.switch_to.window(driver.window_handles[0])
复制 from selenium import webdriver
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
driver.get("https://qzone.qq.com/")
# 根据id切换iframe
driver.switch_to.frame("login_frame")
# 切换输入方式
driver.find_element_by_id("switcher_plogin").click()
# 用户、密码、提交按钮
username = driver.find_element_by_id("u")
password = driver.find_element_by_id("p")
button = driver.find_element_by_id("login_button")
username.clear()
password.clear()
username.send_keys("username")
password.send_keys("password")
button.click()
返回父frame:
复制 driver.switch_to_default_content()
访问浏览器历史记录
在浏览历史中前进和后退你可以使用:
复制 driver.forward()
driver.back()
操作cookies
添加cookies
复制 cookie = {'name' : 'foo', 'value' : 'bar'}
# 发请求
driver.get('https://www.baidu.com')
driver.add_cookie(cookie)
获取所有当前URL下可获得的Cookies
复制 print(driver.get_cookies())
查找元素
driver.find_element_by_id()
driver.find_element_by_name()
driver.find_element_by_xpath()
driver.find_element_by_link_text()
driver.find_element_by_tag_name()
driver.find_element_by_class_name()
driver.find_element_by_css_selector()
其中xpath用法
等待页面加载完成
现在的大多数的Web应用程序是使用Ajax技术。当一个页面被加载到浏览器时, 该页面内的元素可以在不同的时间点被加载。这使得定位元素变得困难, 如果元素不再页面之中,会抛出 ElementNotVisibleException 异常。 使用 waits, 可以解决这个问题。waits提供了一些操作之间的时间间隔- 主要是定位元素或针对该元素的任何其他操作。
Selenium Webdriver 提供两种类型的waits - 隐式和显式。 显式等待会让WebDriver等待满足一定的条件以后再进一步的执行。 而隐式等待让Webdriver等待一定的时间后再才是查找某元素。
显式等待
复制 from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")
try:
# presence_of_element_located为加载条件
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "kw"))
)
finally:
driver.close()
// pass
所有的加载条件
presence_of_element_located
节点加载出,传入定位元组,如(By.ID, 'p')
visibility_of_element_located
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it frame
invisibility_of_element_located
判断一个节点是否仍在DOM,可判断页面是否已经刷新
element_located_to_be_selected
element_selection_state_to_be
传入节点对象以及状态,相等返回True,否则返回False
element_located_selection_state_to_be
传入定位元组以及状态,相等返回True,否则返回False
隐式等待
如果某些元素不是立即可用的,隐式等待是告诉WebDriver去等待一定的时间后去查找元素。 默认等待时间是0秒,一旦设置该值,隐式等待是设置该WebDriver的实例的生命周期。
复制 from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("https://www.baidu.com/")
search = driver.find_element_by_id("kw")
执行javascript
模拟执行javascript,使用execute_script()方法实现
复制 from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.zhihu.com/explore")
# 将进度条下拉到最底部
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
# 弹出提示框
browser.execute_script('alert("To Bottom")')
获取节点信息
获取属性
使用get_attribute()方法来获取节点的属性
复制 from selenium import webdriver
browser = webdriver.Chrome()
url = "https://www.zhihu.com/explore"
browser.get(url)
logo = browser.find_element_by_id("zh-top-link-logo")
print(logo)
print(logo.get_attribute("class"))
运行结果:
复制 <selenium.webdriver.remote.webelement.WebElement (session="036f1edccab08bbc5ca2b8f02accbb04", element="0.40649588367097245-1")>
zu-top-link-logo
获取文本值
通过text属性获取文本信息
复制 from selenium import webdriver
browser = webdriver.Chrome()
url = "https://www.zhihu.com/explore"
browser.get(url)
input = browser.find_element_by_id("zu-top-add-question")
print(input.text)
运行结果:
获取ID、位置、标签名、大小
复制 from selenium import webdriver
browser = webdriver.Chrome()
url = "https://www.zhihu.com/explore"
browser.get(url)
input = browser.find_element_by_id("zu-top-add-question")
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)
运行结果:
复制 0.4994837841126192-1
{'x': 683, 'y': 7}
button
{'height': 32, 'width': 66}
异常处理
复制 from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu.com')
except TimeoutException:
print('Time Out')
try:
browser.find_element_by_id('hello')
except NoSuchElementException:
print('No Element')
finally:
browser.close()
运行结果:
设置代理
复制 from selenium import webdriver
chromeOptions = webdriver.ChromeOptions()
chromeOptions.add_argument('--proxy-server=http://ip:port')
driver = webdriver.Chrome(chrome_options=chromeOptions)
复制 import string
import zipfile
from selenium import webdriver
# 代理服务器(ip+port)
proxyHost = "ip"
proxyPort = "port"
# 代理隧道验证信息(账号+密码)
proxyUser = "user"
proxyPass = "password"
def create_proxy_auth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_path=None):
if plugin_path is None:
plugin_path = r'{}_{}@http-dyn.dobel.com_9020.zip'.format(proxy_username, proxy_password)
manifest_json = """
{
"version": "1.0.0",
"manifest_version": 2,
"name": "Dobel Proxy",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}
"""
background_js = string.Template(
"""
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "${scheme}",
host: "${host}",
port: parseInt(${port})
},
bypassList: ["foobar.com"]
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
return {
authCredentials: {
username: "${username}",
password: "${password}"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
"""
).substitute(
host=proxy_host,
port=proxy_port,
username=proxy_username,
password=proxy_password,
scheme=scheme,
)
with zipfile.ZipFile(plugin_path, 'w') as zp:
zp.writestr("manifest.json", manifest_json)
zp.writestr("background.js", background_js)
return plugin_path
proxy_auth_plugin_path = create_proxy_auth_extension(
proxy_host=proxyHost,
proxy_port=proxyPort,
proxy_username=proxyUser,
proxy_password=proxyPass)
option = webdriver.ChromeOptions()
# option.add_argument('--no-sandbox')
# option.add_argument('--disable-gpu')
# option.add_argument("--start-maximized")
option.add_extension(proxy_auth_plugin_path)
drive = webdriver.Chrome(chrome_options=option)
drive.get("https://www.baidu.com/")
print(drive.page_source)
# drive.close()
设置phantomjs请求头
复制 from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = (
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36"
)
driver = webdriver.PhantomJS(desired_capabilities=dcap)
driver.get("https://www.baidu.com/")
driver.quit()
设置chrome请求头
复制 from selenium import webdriver
# 进入浏览器设置
options = webdriver.ChromeOptions()
# 设置中文
options.add_argument('lang=zh_CN.UTF-8')
# 更换头部
options.add_argument('user-agent="Mozilla/5.0 (iPod; U; CPU iPhone OS 2_1 like Mac OS X; ja-jp) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F137 Safari/525.20"')
browser = webdriver.Chrome(chrome_options=options)
url = "https://www.baidu.com/"
browser.get(url)
browser.quit()
细节
设置phantomjs-图片不加载
复制 from selenium import webdriver
options = webdriver.ChromeOptions()
prefs = {
'profile.default_content_setting_values': {
'images': 2
}
}
options.add_experimental_option('prefs', prefs)
browser = webdriver.Chrome(chrome_options=options)
# browser = webdriver.Chrome()
url = "http://image.baidu.com/"
browser.get(url)
browser.quit()
设置chrome为无界面浏览
复制 import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
#这个是一个用来控制chrome以无界面模式打开的浏览器
#创建一个参数对象,用来控制chrome以无界面的方式打开
chrome_options = Options()
#后面的两个是固定写法 必须这么写
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
#创建浏览器对象
browser = webdriver.Chrome(chrome_options=chrome_options)
url ='http://www.baidu.com/'
browser.get(url)
time.sleep(3)
# 将页面进行截图保存
browser.save_screenshot('baid.png')
browser.quit()
browser.save_screenshot('baid.png')
:保存当前页面