1、Request库
HTTP测试工具:http://httpbin.org,以下的示例会以此为URL
属于第三方库,需要手动安装
pip install requests
基本用法
import requests
r = requests.get('http://www.baidu.com')
# 查看响应类型
print(type(r))
# 查看响应状态码
print(r.status_code)
# 查看响应内容的类型
print(type(r.text))
# 查看响应的内容
print(r.text)
# 查看cookies
print(r.cookies)
返回一个响应对象,然后分别输出响应对象类型、状态码、响应体内容的类型、 响应体的内容、Cookies。通过运行结果可以得知:响应对象的类型是requests.models.Response,响应体内容的类型是str,Cookies 的类型是RequestCookieJar。如果要发送其他类型的请求直接调用其对应的方法即可:
r = requests.post('https://www.baidu.com')
r = requests.put('https://www.baidu.com')
r = requests.delete('https://www.baidu.com')
r = requests.head('https://www.baidu.com')
r = requests.options('https://www.baidu.com')
GET请求
构建一个GET请求,请求http://httpbin.org/get(该网站会判断如果客户端发起的是GET请求的话,它返回相应的信息)
import requests
r = requests.get('http://httpbin.org/get')
print(r.text)
输出结果
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.28.1",
"X-Amzn-Trace-Id": "Root=1-64f57ec6-49a5af517b0166dd7889d751"
},
"origin": "123.138.216.82",
"url": "http://httpbin.org/get"
}
如果要添加请求参数,比如添加两个请求参数,其中name值是germey,age值是20。虽然可以写成如下形式:
r = requests.get('http://httpbin.org/get?name=germey&age=20')
但较好的写法是下面这种写法:
import requests
data = {
'name': 'germey',
'age': 22
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)
输出
import requests
data = {
'name': 'germey',
'age': 22
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)
请求的URL最终被构造成了“http://httpbin.org/get?name=germey&age=20“
网页的返回内容的类型是str类型的,如果它符合JSON格式,则可以使用json( )方法将其转换为字典类型,以方便解析
import requests
r = requests.get('http://httpbin.org/get')
#str类型
print(type(r.text))
#返回响应内容的字典形式
print(r.json())
#dict类型
print(type(r.json()))
如果返回的内容不是JSON格式,调用json( )方法便会出现错误,抛出json.decoder.JSONDecodeError异常
Post请求
1)发送POST请求。
import requests
r = requests.post('http://httpbin.org/post')
print(r.text)
2)发送带有请求参数的POST请求。
import requests
data = {
"name":"germey",
"age":"22"
}
r = requests.post('http://httpbin.org/post',data=data)
print(r.text)
结果
{
"args": {},
"data": "",
"files": {},
"form": { # 在POST请求方法中,form部分就是请求参数。
"age": "22",
"name": "germey"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "18",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.28.1",
"X-Amzn-Trace-Id": "Root=1-64f58064-3dc36bbb311e04a42a265c37"
},
"json": null,
"origin": "123.138.216.82",
"url": "http://httpbin.org/post"
}
3)设置请求头
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
'my-test':'Hello'
}
r = requests.get('http://httpbin.org/get',headers=headers)
print(r.text)
结果
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"My-Test": "Hello", # 请求头参数
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0",
"X-Amzn-Trace-Id": "Root=1-64f580a8-0188cb2528fd544a0f3ae23d"
},
"origin": "123.138.216.82",
"url": "http://httpbin.org/get"
}
响应
1)发送请求后,返回一个响应,它具有很多属性,通过它的属性来获取状态码、响应头、Cookies、响应内容等。如下
import requests
r = requests.get('https://www.baidu.com/')
# 响应内容(str类型)
print(type(r.text), r.text)
# 响应内容(bytes类型)
print(type(r.content), r.content)
# 状态码
print(type(r.status_code), r.status_code)
# 响应头
print(type(r.headers), r.headers)
# Cookies
print(type(r.cookies), r.cookies)
# URL
print(type(r.url), r.url)
# 请求历史
print(type(r.history), r.history)
2)响应码
状态码常用来判断请求是否成功,除了可以使用HTTP提供的状态码,requests库中也提供了一个内置的状态码查询对象,叫做 requests.codes,实际上两者都是等价的。示例如下:
import requests
r = requests.get('https://www.baidu.com/')
if not r.status_code==requests.codes.ok:
print('Request Fail')
else:
print('Request Successfully')
requests.codes对象拥有的状态码如下:
#信息性状态码
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',),
#重定向状态码
300:('multiple_choices',),
301:('moved_permanently','moved','\\o-'),
302:('found',),
303:('see_other','other'),
304:('not_modified',),
305:('user_proxy',),
306:('switch_proxy',),
307:('temporary_redirect','temporary_moved','temporary'),
308:('permanent_redirect',),
#客户端请求错误
400:('bad_request','bad'),
401:('unauthorized',),
402:('payment_required','payment'),
403:('forbiddent',),
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:('request_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',),
#服务端错误状态码
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','bandwith'),
510:('not_extended',),
511:('network_authentication_required','network_auth','network_authentication')
爬取二进制数据
图片、音频、视频这些文件本质上都是由二进制码组成的,所以想要爬取它们,就要拿到它们的二进制码。以爬取百度的站点图标(选项卡上的小图标)为例:
import requests
#向资源URL发送一个GET请求
r = requests.get('https://www.baidu.com/favicon.ico')
with open('favicon.ico','wb') as f:
f.write(r.content)
#使用open( )方法,它的第一个参数是要保存文件名(可带路径),第二个参数表示以二进制的形式写入数据。运行结束之后,可以在当前目录下发现保存的名为favicon.ico的图标。同样的,音频和视频也可以用这种方法获取。
文件上传
requests可以模拟提交一些数据。假如某网站需要上传文件,我们也可以实现。
1)
import requests
#以二进制方式读取当前目录下的favicon.ico文件,并将其赋给file
files = {'file':open('favicon.ico','rb')}
#进行上传
r = requests.post('http://httpbin.org/post',files=files)
print(r.text)
2)Typora上传图片
import requests
import json
from sys import argv
uploadUrl = 'https://upload.cnblogs.com/imageuploader/processupload?host=www.cnblogs.com'
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
"cookie":"" #此处填自己的cookie
}
# 类型映射
mimeMapping = {".png": 'image/png', '.gif': 'image/gif', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg'}
for i in argv[1:]:
# 图片地址参数
imgPath = i
# 对应的mime
mime = imgPath[imgPath.rindex("."):]
file = [
("",("fileName", open(imgPath, "rb"), mimeMapping[mime]))
]
response = requests.post(uploadUrl,headers = headers,files = file)
data = json.loads(response.text)
print(data['message'])
处理Cookies
使用urllib处理Cookies比较复杂,而使用requests处理Cookies非常简单。
1)获取Cookies。
import requests
r = requests.get('https://www.baidu.com')
#打印Cookies对象
print(r.cookies)
#遍历Cookies
for key,value in r.cookies.items():
print(key+'='+value)
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
BDORZ=27315
通过响应对象调用cookies属性即可获得Cookies,它是一个RequestCookiesJar类型的对象,然后用items( )方法将其转换为元组组成 的列表,遍历输出每一个Cookies的名称和值。
2)使用Cookies来维持登录状态。以知乎为例,首先登录知乎,进入一个登录之后才可以访问的页面,在浏览器开发者工具中将Headers 中的Cookies复制下来(有时候这样直接复制的Cookies中包含省略号,会导致程序出错,此时可以在Console项下输document.cookie 即可得到完整的Cookie),在程序中使用它,将其设置到Headers里面,然后发送请求。
import requests
headers = {
'Cookie':'', # 自己的cookie信息
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
'Host':'www.zhihu.com'
}
r = requests.get('https://www.zhihu.com/people/xing-fu-shi-fen-dou-chu-lai-de-65-18',headers=headers)
print(r.text)
运行之后,结果中包含了登录后的内容,说明获取登录状态成功。
3)也可以通过cookies参数来设置,不过这样就需要构造RequestCookieJar对象,而且需要分割以下cookies,相对繁琐,但效果是一 样。
import requests
cookies =''
jar = requests.cookies.RequestsCookieJar()
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
'Host':'www.zhihu.com'
}
for cookie in cookies.split(';'):
key,value = cookie.split('=',1)
jar.set(key,value)
r = requests.get('https://www.zhihu.com/people/xing-fu-shi-fen-dou-chu-lai-de-65-18',headers=headers)
print(r.text)
会话维持
通过调用get( )或post( )等方法可以做到模拟网页的请求,但是这实际上是相当于不同的会话,也就是说相当于你用了两个浏览器打开不同的页面。如果第一个请求利用post( )方法登录了网站,第二次想获取登录成功后的自己的个人信息,又使用了一次get( )方法取请求个人信息,实际上,这相当于打开了两个浏览器,所以是不能成功的获取到个人信息的。为此,需要会话维持,你可以在两次请求时设置一样的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)
首先通过requests打开一个会话,然后通过该会话发送了一个get请求,该请求用于向cookies中设置参数number,参数值为123456789;接着又使用该发起了一个get请求,用于获取Cookies,然后打印获取的内容。
{
"cookies": {
"number": "123456789"
}
}
SSL证书验证
requests还提供了证书验证功能,当发送HTTP请求的时候,它会检查SSL证书,我们可以使用verify参数控制是否检查SSL证书。
1)请求一个HTTPS网站时,如果该网站的证书没有被CA机构信任,程序将会出错,提示SSL证书验证错误。对此,只需要将verify参数 设置为False即可。如下:
import requests
resposne = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)
也可以指定一个本地证书用作客户端证书,它可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组。
import requests
#本地需要有crt和key文件(key必须是解密状态,加密状态的key是不支持的),并指定它们的路径,
response = requests.get('https://www.12306.cn',cert('/path/server.crt','/path/key'))
print(response.status_code)
在请求SSL证书不被CA机构认可的HTTPS网站时,虽然设置了verify参数为False,但程序运行可能会产生警告,警告中建议我们给它 指定证书,可以通过设置忽略警告的方式来屏蔽这个警告
import requests
from requests.packages import urllib3
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)
或者通过捕获警告到日志的方式忽略警告
import logging
import requests
logging.captureWarnings(True)
response = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)
代理设置
对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模、频繁地爬取,网站可能会弹出验证码,或者跳转到登录验证页面,更有甚者可能会直接封禁客户端的IP,导致一定时间内无法访问。为了防止这种情况,我们需要使用代理来解决这个问题,这就需要用到proxies参数。
1)设置代理
import requests
proxies = {
# 该代理服务器在免费代理网站上得到的,这样的网站有很多
'http': 'http://123.57.1.16:80',
'https': 'https://123.57.1.16:80'
}
try:
response = requests.get('http://httpbin.org/get', proxies=proxies)
print(response.text)
except requests.exceptions.ConnectionError as e:
print('Error', e.args)
{
"args": {
"show_env": ""
},
"headers": {
"Accept-Encoding": "",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.28.1",
"X-Amzn-Trace-Id": "Root=1-63156d33-528b16b838892ff15c5a4d2f",
"X-Forwarded-For": "123.57.1.16",
"X-Forwarded-Port": "80",
"X-Forwarded-Proto": "http"
},
"origin": "123.57.1.16", # 可以看到是由代理发起请求
"url": "http://httpbin.org/get?show_env"
}
2)如果代理需要使用HTTP Basic Auth,可以使用类似http://user:password@host:port这样的语法来设置代理。
import requests
proxies = {
"http":"http://user:password@161.35.4.201:80"
}
r = requests.get("https://www.taobao.com",proxies=proxies)
print(r.text)
3)除了基本的HTTP代理外,requests还支持SOCKS协议的代理。首先需要安装socks这个库:
pip3 install 'requests[socks]'
import requests
proxies = {
'http':'socks5://user:password@host:port',
'https':'socks5://user:password@host:port'
}
request.get('https://www.taobao.com',proxies=proxies)
超时设置
在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才可能收到响应,甚至到最后收不到响应而报错。为了应对这种情况,应设置一个超时时间,这个时间是计算机发出请求到服务器返回响应的时间,如果请求超过了这个超时时间还没有得到响应,就抛出错误。这就需要使用timeout参数实现,单位为秒。
1)指定请求总的超时时间
import requests
#向淘宝发出请求,如果1秒内没有得到响应,则抛出错误
r = requests.get('https://www.taobao.com',timeout=1)
print(r.status_code)
2)分别指定超时时间。实际上,请求分为两个阶段:连接(connect)和读取(read)。如果给timeout参数指定一个整数值,则超时时 间是这两个阶段的总和;如果要分别指定,就可以传入一个元组,连接超时时间和读取超时时间:
import requests
#向淘宝发出请求,如果连接阶段5秒内没有得到响应或读取阶段30秒内没有得到响应,则抛出错误
r = requests.get('https://www.taobao.com',timeout=(5,30))
print(r.status_code)
3)如果想永久等待,可以直接timeout设置为None,或者不设置timeout参数,因为它的默认值就是None。
import requests
#向淘宝发出请求,如果连接阶段5秒内没有得到响应或读取阶段30秒内没有得到响应,则抛出错误
r = requests.get('https://www.taobao.com',timeout=None))
print(r.status_code)
身份验证
使用requests自带的身份验证功能,通过HTTPBasicAuth类实现。
import requests
from requests.auth import HTTPBasicAuth
s = requests.Session()
r = s.get('http://192.168.2.93:8481/#/login?redirect=%2F', auth=HTTPBasicAuth('admin', 'Hbis@123'))
print(r.status_code)
如果用户名和密码正确的话,返回200状态码;如果不正确,则返回401状态码。也可以不使用HTTPBasicAuth类,而是直接传入一个 元组,它会默认使用HTTPBasicAuth这个类来验证。
import requests
s = requests.Session()
r = s.get('http://192.168.2.93:8481/#/login?redirect=%2F', auth=('admin', 'Hbis@123'))
print(r.status_code)
requests还提供了其他验证方式,如OAuth验证,不过需要安装oauth包,安装命令如下:
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
在学习urllib库时,发送请求如果需要设置请求头,需要通过一个Request对象来表示。在requests库中,存在一个与之类似的类,称为Prepared Request。
from requests import Request,Session
url = 'http://httpbin.org/post'
data = {
'name':'germey'
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0'
}
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将请求当作独立的对象来看待,这样在进行队列调度时会非常方便,后面会用它来构造一个Request队列
示例
爬取豆瓣Top250
"""
爬取豆瓣电影
"""
import requests
import re
file = 'movie.csv'
f = open(file, 'w', encoding='utf-8')
f.write('电影名称,导演,年份,电影评分,评论数\n')
for page in range(0, 250, 25):
url = f'https://movie.douban.com/top250?start={page}'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
}
response = requests.get(url, headers=headers)
text = response.text
# pattern1 = re.compile(
# r'<div class="item">.*?<span class="title">(?P<item>.*?)</span>.*?<p class="">.*?(?P<auth>.*?) ', re.S)
pattern = re.compile(
r'<div class="item">.*?<span class="title">(?P<item>.*?)</span>.*?<p class="">.*?导演: (?P<auth>.*?) .*?<br>'
r'(?P<year>.*?) .*?<span class="rating_num" property="v:average">(?P<core>.*?)</span>.*?<span>(?P<comment>.*?)人评价</span>',
re.S)
result = pattern.finditer(text)
for x in result:
item = x.group('item').strip()
auth = x.group('auth').strip()
year = x.group('year').strip()
core = x.group('core').strip()
comment = x.group('comment').strip()
list = [item, auth, year, core, comment]
context = ','.join(list) + '\n'
f.write(context)
print(f"链接:{url}爬取完毕\n")
f.close()
爬取数据
import json
import requests
url = 'http://192.168.2.93:8481/api/v5/login'
data = {
"username": "sun",
"password": "NsWf7IeojknkNmBenjyZIHfE2eKJKge4LYF66lhVuGCGhpg1pWEer6IjlOWvk0mG+at5ksNfgk5R2d/DtZZPSkQ53U727AznwLeHZ6sDPuSMITSCJUoBzj9dYSDXUJXjNeY992UBl16sMtVC9IkSp5bRyNhii8te3az1lZt1zaA="
}
headers = {
'Referer': 'http://192.168.2.93:8481/',
'Content-Type': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJpcCI6IjE5Mi4xNjguMy40MiIsIm5hbWUiOiLnrqHnkIblkZgiLCJleHAiOjE2OTM0NzEyNjYsInVzZXJJZCI6IjEiLCJqdGkiOiIwNDhmM2YyNy00ZmUyLTRjYTAtODExOC1lNWI0NmNmNTlkMzIiLCJjbGllbnRfaWQiOiJhZG1pbiIsInVzZXJuYW1lIjoiYWRtaW4ifQ.loNAyIl0FRb-qMGNK_WJsDrqJdmOfSXXGygaEmYOwrU'
}
with requests.Session() as session:
session.headers.update(headers)
response = session.post(url, data=json.dumps(data))
if response.status_code == 200:
print('登陆成功')
post = session.get('http://192.168.2.93:8481/api/v5/cluster/findById?clusterId=141')
print(post.json())
# 查看日志
get = session.get(
'http://192.168.2.93:8481/api/v5/log/findLongPage?startTime=2023-07-21+17:51:09&endTime=2023-08-21+17:51:09&username=&text=')
print(get.json())
else:
print("登录失败")
爬取二进制图片
import requests
import os
def spider(page):
global pic_name
url = f'https://www.pexels.com/zh-cn/api/v3/users/320381804/media/recent?per_page=12&page={page}&seo_tags=true'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'Referer': 'https://www.pexels.com/zh-cn/@emre-akyol-320381804/gallery/',
'Secret-Key': 'H2jk9uKnhRmL6WPwh89zBezWvr', # 关键
'Cookie': '__cf_bm=GKFhiRbFocgTe4tZBxPJ0k85nil.L39sq6J3XkU.rhA-1692671105-0-Acp7DNkzsbV1Wo3Q8D1x3ohwYIsEJunyaBuVtQ3AFdNjokPHMj7AejJ9HfS251NSaaBHd8Nn+JTcJDOYXyejwWs=; _gid=GA1.2.1719033680.1692671105; _sp_ses.9ec1=*; _hjFirstSeen=1; _hjIncludedInSessionSample_171201=0; _hjSession_171201=eyJpZCI6IjFjOGIzOWZkLTA0NWUtNDY4Ni1hY2I3LThlYjE3MWQzMGNjYiIsImNyZWF0ZWQiOjE2OTI2NzExMTM3NjAsImluU2FtcGxlIjpmYWxzZX0=; _hjAbsoluteSessionInProgress=0; OptanonAlertBoxClosed=2023-08-22T02:25:14.309Z; _hjSessionUser_171201=eyJpZCI6IjQ1ODU2MDlmLTdmNDktNTFkMi05MGNiLWQxNmNjMjFhYjE0YSIsImNyZWF0ZWQiOjE2OTI2NzExMTM3NTQsImV4aXN0aW5nIjp0cnVlfQ==; cf_clearance=EyheRqx2Tumh.M2FaKkSr5E.MGrbCiKg5ANP0imlLls-1692671123-0-1-5d1dd351.73fb3ba.47546780-0.2.1692671123; _sp_id.9ec1=17087147-0866-420b-a564-8e85edd3c920.1692671112.1.1692671172..6f3c33d1-1697-4d0b-9a66-8f80467e82f5..4ecd1c0e-4bc7-4552-8ad5-cbe471c11a6c.1692671121004.6; OptanonConsent=isGpcEnabled=0&datestamp=Tue+Aug+22+2023+10%3A26%3A12+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=202301.1.0&isIABGlobal=false&hosts=&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A1%2CC0003%3A1&geolocation=CN%3BSN&AwaitingReconsent=false; _ga=GA1.1.1140262214.1692671105; _ga_8JE65Q40S6=GS1.1.1692671113.1.1.1692671173.0.0.0'
}
response = requests.get(url, headers=headers)
data = response.json()['data']
for item in data:
try:
attributes = item['attributes']
last_name = attributes['user']['last_name']
download_url = attributes['image']['download_link']
pic_url = str.split(download_url, '?')[0]
print(f'用户:{last_name},图片地址:{pic_url}')
content = requests.get(pic_url, headers=headers).content
pic_name = os.path.basename(pic_url)
with open(f'image\\{pic_name}', 'wb') as f:
f.write(content)
print(f'图片:{pic_name}下载成功!')
except Exception as e:
print(f'图片:{pic_name}下载失败,{str(e)}')
if __name__ == '__main__':
for i in range(1, 11):
spider(i)
代理
import re
import requests
def get_proxy_Ip():
# url = 'https://www.89ip.cn/index_1.html'
api_url = 'http://api.89ip.cn/tqdl.html?api=1&num=1000&port=&address=&isp='
html = requests.get(api_url).text
re_compile = re.compile(r'\d+.\d+.\d+.\d+:\d+')
return re_compile.findall(html)
def get_request(type, url, proxy):
return requests.request(method=type, url=url, proxies=proxy)
def spider(url, ips):
while 1:
for ip in ips:
try:
proxy = {
'http': f'http://{ip}',
'https': f'https://{ip}'
}
response = get_request('get', url, proxy)
if response.status_code == '200':
return response
except Exception as e:
print(f'代理:{ip} 可能失效:{str(e)}')
if __name__ == '__main__':
# url = 'https://movie.douban.com/top250'
url = 'http://www.baidu.com'
res = spider(url, get_proxy_Ip())
print(res.text)
2、BS4
安装
from bs4 import BeautifulSoup
获取网页信息
可以使用urlib或者request获取
import requests
url = "https://www.baidu.com"
r1 = requests.get(url,'lxml')
r1.encoding='utf-8'
#print (r1.text)
bs_1=BeautifulSoup(r1.text,'lxml')
#print(bs_1)
第一个参数为html信息,第二个是bs4自带的解析器,如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的
然后通过这个对象来实现对获取到的源码进行筛选和处理
print(bs_1.prettify()) #格式化输出全部内容
print(bs_1.title) # #标签名有html,head,title,meta,body,script,style等等
实例1:
from bs4 import BeautifulSoup
html = """
<ul>
<li><a type='sun' href='zhangwuji.com'>张无忌</a></li>
<li><a type='sun' href='zhangwuji1.com'>张无忌1</a></li>
<li><a href='zhangwuji2.com'>张无忌2</a></li>
<li><a href='zhangwuji3.com'>张无忌3</a></li>
</ul>
"""
soup = BeautifulSoup(html, 'html.parser')
# 查找所有的'a'标签
all_a = soup.find_all('a')
print(all_a)
# 查找第一个'a'标签
single_a = soup.find('a')
print(single_a)
# 查找第一个'a'标签,筛选
single_a_ = soup.find('a', attrs={'type': 'sun'})
print(single_a_)
# 遍历所有的'a'标签列表
for a in all_a:
context = a.text
link = a.get('href')
print(context, link)
示例2:
index.html
<!DOCTYPE html>
<html lang="en" xmlns="">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<ul>
<li id="l1">张三</li>
<li id="l2">李四</li>
<li>王五</li>
<a href="" id="" class="a1">尚硅谷</a>
<span>嘿嘿嘿</span>
</ul>
</div>
<a href="www.baidu.com" title="a2">百度</a>
<div id="d1">
<span>
哈哈哈
</span>
</div>
<p id="p1" class="p1">呵呵呵</p>
</body>
</html>
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# 根据标签名查找节点
# 找到的是第一个符合条件的数据
print(soup.a)
# 获取标签的属性和属性值
print(soup.a.attrs)
<a class="a1" href="" id="">尚硅谷</a>
{'href': '', 'id': '', 'class': ['a1']}
find示例
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# 获取第一个值
print(soup.find('a'))
# 根据title的值来找到对应的标签对象
print(soup.find('a', title='a2'))
# 根据class的值来找到对应的标签对象 注意的是class需要添加下划线
print(soup.find('a', class_='a1'))
# 根据指定的属性找对应的标签对象
print(soup.find('a', attrs={'href': 'www.baidu.com'}))
find_all 示例
# find_all 返回的是一个列表 并且返回了所有的a标签
print(soup.find_all('a'))
# 如果想获取的是多个标签的数据 那么需要在find_all的参数中添加的是列表的数据
print(soup.find_all(['a','span']))
# limit的作用是查找前几个数据
print(soup.find_all('li',limit=2))
select(推荐)
select方法返回的是一个列表(符合条件的所有数据)
# select方法返回的是一个列表 并且会返回多个数据
print(soup.select('a'))
# 可以通过.代表class 我们把这种操作叫做类选择器
print(soup.select('.a1'))
# 可以通过#代表id 我们把这种操作叫做id选择器
print(soup.select('#l1'))
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# 属性选择器---通过属性来寻找对应的标签
# 查找到li标签中有id的标签
print(soup.select('li[id]'))
# 查找到li标签中id为l2的标签
print(soup.select('li[id="l2"]'))
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# (3)select(推荐)
# 层级选择器
# 后代选择器
# 找到的是div下面的li
print(soup.select('div li'))
# 子代选择器
# 某标签的第一级子标签
# 注意:很多的计算机编程语言中 如果不加空格不会输出内容 但是在bs4中 不会报错 会显示内容
print(soup.select('div > ul > li'))
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# (3)select(推荐)
# 找到a标签和li标签的所有的对象
print(soup.select('a,li'))
# 节点信息
# 获取节点内容
obj = soup.select('#d1')[0]
# 如果标签对象中 只有内容 那么string和get_text()都可以使用
# 如果标签对象中 除了内容还有标签 那么string就获取不到数据 而get_text()是可以获取数据
# 我们一般情况下 推荐使用get_text()
print(obj.string)
print(obj.get_text())
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# (3)select(推荐)
# 节点的属性
obj = soup.select('#p1')[0]
# name是标签的名字
print(obj.name)
# 将属性值左右一个字典返回
print(obj.attrs)
from bs4 import BeautifulSoup
# 通过解析本地文件 来将bs4的基础语法进行讲解
# 默认打开的文件的编码格式是gbk 所以在打开文件的时候需要指定编码 BeautifulSoup使用的是lxml内核
soup = BeautifulSoup(open('index.html', encoding='utf-8'), 'lxml')
# (3)select(推荐)
# 获取节点的属性
obj = soup.select('#p1')[0]
#
print(obj.attrs.get('class'))
print(obj.get('class'))
print(obj['class'])
import urllib.request
from bs4 import BeautifulSoup
url = 'https://www.starbucks.com.cn/menu/'
response = urllib.request.urlopen(url)
content = response.read().decode('utf-8')
soup = BeautifulSoup(content, 'lxml')
# //ul[@class="grid padded-3 product"]//strong/text()
name_list = soup.select('ul[class="grid padded-3 product"] strong')
for name in name_list:
print(name.get_text())
3. xpath
基本
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高,基于路径的方式来获取元素和节点
三部分组成:
路径表达式: 基础,用于描述元素和节点的位置关系,如
/bookstore/book[1]/title
表示选择bookstore
元素下的第一个book
元素的title
节点。a = """ <bookstore> <book> <title>Book 1</title> <price>30</price> </book> <book> <title>Book 2</title> <price>40</price> </book> <book> <title>Book 3</title> <price>25</price> </book> </bookstore> """ from lxml import etree root = etree.XML(a) # 如果有多个book标签,则返回一个列表 books = root.xpath("/bookstore/book/title") # [<Element title at 0x1047d90b8>
选择器:
通过添加谓词来筛选元素和节点
/bookstore/book[price>35]/title表示选择bookstore元素下价格大于35的book元素的title节点
函数:
XPath支持多种函数来处理节点和元素。例如,
count(/bookstore/book)
函数可以计算bookstore
元素下的book
元素数量print(root.xpath("count(/bookstore/book)"))
安装、导入库
pip install lxml
from lxml import etree
实例化一个etree的多种方式
# 1. 直接传入字符串
str_etree = etree.HTML(a) # etree.HTML会尝试根据HTML的宽松语法解析输入的字符串
xml_etree = etree.XML(a) #求标签必须正确嵌套和闭合
# 2. 传入文件路径
file_etree = etree.parse("./book.xml")
# 3. 传入文件对象
file_object = open("./book.xml")
# 4. 传入etree.Element对象
root_etree = etree.fromstring(a)
表达式
XPath 表达式 | 描述 |
---|---|
nodename |
选择指定节点名为 nodename 的所有元素。 |
/ |
从根节点开始选择。 |
// |
选择匹配选择的任何位置的节点。 |
. |
选择当前节点。 |
.. |
选择当前节点的父节点。 |
@attribute |
选择具有指定属性名 attribute 的所有元素。 |
@* |
选择所有具有属性的元素。 |
[@attribute='value'] |
选择具有指定属性名为 attribute ,且属性值为 value 的所有元素。 |
[@attribute1='value1' and @attribute2='value2'] |
选择具有指定属性名和属性值的所有元素。 |
[@attribute='value']/child::node() |
选择具有指定属性名和属性值的元素的子节点。 |
//child::node() |
选择文档中的所有子节点。 |
//child::element |
选择文档中的所有元素节点。 |
child::node()[position()=1] |
选择文档中的第一个子节点。 |
child::*[position()=last()] |
选择文档中的最后一个元素节点。 |
child::text() |
选择文档中的所有文本节点。 |
child::comment() |
选择文档中的所有注释节点。 |
descendant::node() |
选择当前节点的后代节点(包括子孙节点)。 |
ancestor::node() |
选择当前节点的祖先节点(包括父辈节点)。 |
parent::node() |
选择当前节点的父节点。 |
from lxml import etree
html = """
<book>
<id>1</id>
<name>蔡依林</name>
<price>1.23</price>
<nick>臭豆腐</nick>
<author>
<nick id="10086">周大强</nick>
<nick id="10081">周杰伦</nick>
<nick class="jay">周大福</nick>
<nick class="jolin">蔡依林</nick>
<div>
<nick>林俊杰</nick>
</div>
</author>
<partner>
<nick href="www.baidu.com" id="ppc">胖胖陈</nick>
<nick id="ppbc">胖胖不陈</nick>
</partner>
</book>
"""
# 构建xml树
et = etree.XML(html)
root = et.xpath('/book') # 表示根节点
son = et.xpath('/book/name') # 在xpath中间的/表示的是儿子
son_text = et.xpath('/book/name/text()')[0] # text()取文本
nick_all = et.xpath('/book//nick') # //表示的是子孙后代
nick = et.xpath('/book/*/nick/text()')
nick_class = et.xpath('/book/author/nick[@class="jay"]/text()') # []表示属性筛选,@属性名=值
id = et.xpath('/book/partner/nick/@id') # 取属性值,比如id
href = et.xpath('/book/partner/nick/@href') # 取属性值,比如href
ID = et.xpath('/book/partner/nick[2]/text()') # [i] 表述取第i个
nick_ppbc = et.xpath('/book/partner/nick[@id="ppbc"]/text()')
print(nick_ppbc)
实战
import requests
from lxml import etree
url='https://sh.58.com/ershoufang/'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.7 Safari/537.36'}
page_test=requests.get(url=url,headers=headers).text
tree=etree.HTML(page_test)
#先看图二的解释,这里li有多个,所里返回的li_list是一个列表
li_list=tree.xpath('//ul[@class="house-list-wrap"]/li')
#这里我们打开一个58.txt文件来保存我们的信息
fp=open('58.txt','w',encoding='utf-8')
for li in li_list:
#这里 ./是对前面li的继承,相当于li/div...
title=li.xpath('./div[2]/h2/a/text()')[0]
print(title+'\n')
fp.write(title+'\n')
fp.close()