nwnusun

   ::  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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)设置代理

https://www.89ip.cn/index_1.html

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>.*?)&nbsp;', re.S)
    pattern = re.compile(
        r'<div class="item">.*?<span class="title">(?P<item>.*?)</span>.*?<p class="">.*?导演: (?P<auth>.*?)&nbsp;.*?<br>'
        r'(?P<year>.*?)&nbsp;.*?<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文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的

image-20230904161313773

然后通过这个对象来实现对获取到的源码进行筛选和处理

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()
posted on 2023-09-07 20:36  匿名者nwnu  阅读(82)  评论(0编辑  收藏  举报