urllib-访问网页的两种方式:GET与POST
学习自:https://www.jianshu.com/p/4c3e228940c8
使用参数、关键字访问服务器
访问网络的两种方法:
1、GET
- 利用参数给服务器传递信息
- 参数data为dict类型,然后用parse.urlencode()编码为str类型,用编码后的data+baseURL构成完整的URL
- GET中不需要用encode
- 打开网页
- 读取页面内容
- 内容编码转换
2、POST
- 一般向服务器传递参数使用
- post把信息自动加密处理
- 如果想使用post信息,需要用到data参数
- 使用post,意味着HTTP的请求头请求需要修改:
- Content-Type:application/x-www.form-urlencode
- Content-Length:数据长度
- 简而言之,一旦修改请求方法,请注意与其它请求的头部信息相适应
- urllib.parse.urlencode可以将dict类型转化为str,encode将str转化为bytes
- 如果想要设置更多的头部信息,那么urlopen()是无法满足要求的,因此可以用request.Request().add_header(Header信息名,Header信息值)方法
- request.Request()用来创建Request实例,它包含了所有信息,如url、data、headers、method等。最后只需要用request.urlopen()打开这个实例即可。
3、urllib.error
当使用request进行爬虫时,尽量采用try...except代码块,并把爬虫代码放入try中,避免错误。
- URLError产生的原因
- 没网络
- 服务器链接失败
- OSError的子类
- HTTPError
- URLError的子类
- 两者区别
- HTTPError是对应的HTTP请求返回的错误
- URLError对应的是一般的网络问题,包括URL错误
- 继承关系有:OSError - URLError - HTTPError
GET访问方式
1、parse.urlencode()的简单应用
在使用搜索引擎时,如搜索“学习Python”,当搜索结果出来后,可以在URL地址栏发现如下的URL字符串:
https://www.baidu.com/s?wd=学习Python&...#后边省略
如果我们只保留其中主要的部分
https://www.baidu.com/s?wd=学习Python
其搜索结果还是一样的。
这就说明,在使用搜索引擎时,前面的 'https://www.baidu.com/s?' 是固定不变的,为baseURL,只需要输入关键字即可返回结果。
此时,在浏览器中按F12进入开发者模式,查看Network->Name栏的Headers部分。这里就是HTTP请求头Header的信息内容。
可以看到,“学习”二字变成了一串由16进制字符组成的字符串%E5%AD%A6%E4%B9%A0,说明浏览器在发送请求时对URL进行了编码
①利用parse模块模拟GET请求
在使用爬虫时,可以只需要输入关键字,并且将关键字进行编码,将其转换成服务器识别的形式,最后把关键字与baseURL连接起来即可实现访问。
这里的编码采用的方法就是parse模块下的parse.urlencode()方法。
from urllib import parse,request baseURL='https://www.baidu.com/s?' wd=input('Input your keyword:') #这里必须是wd,不能是其他的字符串 data={'wd':wd} data=parse.urlencode(data) URL=baseURL+data with request.urlopen(URL) as f: html=f.read().decode('utf-8') print(html)
运行程序,输入“学习Python”,结果:
最终的URL: https://www.baidu.com/s?wd=%E5%AD%A6%E4%B9%A0Python <!DOCTYPE html> <html> <head> </head> <body> </body> </html>
打印出来的结果就是html文档文件,也就是搜索“学习Python”后的网页内容。
同时也能看到最终提交到服务器端的URL地址,其中“学习Python”中的汉字已经转化为bytes类型了,这就是parse.urlencode()的作用。
注意:
1、baseURL最后是/s?而不是/?s
2、输入的关键字,必须存储为wd,不能自定义为其他的变量名
3、编码前要先把关键字wd添加到一个dict对象中去,对得到的dict对象编码
data={'wd':wd}
4、两个关键语句:
data=parse.urlencode(data) with request.urlopen(URL) as res: html=res.read().decode('utf-8')
其中,①parse.urlencode的参数必须是dict对象。
②request.urlopen(URL)获取URL句柄,标记为对象res
对句柄res调用read()获取最原始的信息,res.read()
对原始信息进行解码后显示:res.read().decode()
5、从服务器端读取的内容为bytes类型,要用decode将之转化为str类型,才能打印出来。
POST访问方式
post方式访问,是提交表单类的数据到服务器。
request.urlopen()中的data参数
以百度翻译举例使用data参数:
利用parse模块模拟POST请求:
- 首先进入百度翻译首页,并用F12进入开发者模式,查看Network栏
- 输入单词girl,可以发现每输入一个字母后边都有请求
- 请求地址是:https://fanyi.baidu.com/sug
- 请求方式是:POST
- Form data的值是kw:girl,所以字典的Key名为kw(这点注意与get中的Key名为wd相区分)
- 查看返回内容格式,content-length是返回内容长度;content-type是返回内容格式——返回的是json格式内容,需要用到json包
利用parse模拟POST请求
步骤:
- 利用data参数构造内容,这里的内容就是之前所说的关键字内容Form data
- 使用request.urlopen(url , data)打开url,并传入内容data;baseURL不需要和data连接以构成一个完整的URL
- 返回的JSON数据类型,用json.loads()转换为str字符串类型
- 返回的结果就是搜索词的释义
from urllib import parse,request,error #返回内容为JSON格式,用json.loads()转换为str import json #基本URL baseURL='https://fanyi.baidu.com/sug' #为防止报错,使用try...except语句块,使用error.URLerror try: #输入Form data kw=input('Input your keyword:') #用data存放Form data;Request传入的数据必须是字典类型 #浏览器中开发模式下,Form data下字典的Key为kw,所以data的Key为kw data={'kw':kw} data=parse.urlencode(data).encode() print('即将发送的data数据的类型:',type(data)) #打开网页,传入data参数 #urlopen的参数为baseURL和data参数 with request.urlopen(baseURL,data=data) as res: json_data=res.read().decode() print('返回数据的类型:',type(json_data)) json_data=json.loads(json_data) print('转换后的数据类型:',type(json_data)) for i in json_data['data']: print(i) except error.URLError as e: print(e)
结果:
Input your keyword:>? Girl 即将发送的data数据的类型: <class 'bytes'> 返回数据的类型: <class 'str'> 转换后的数据类型: <class 'dict'> {'k': 'girl', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;'} {'k': 'girls', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎; girl的复数;'} {'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'} {'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'} {'k': "Girls' Generation", 'v': ' 少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);'}
这就是使用parse模拟浏览器访问服务器的用法。
补充:
1、最开始传入的data为dict类型,最后返回的数据也是转化为dict类型
2、Post的传入关键字构建data的Key必须命名为爬虫页开发者模式下的Form data
3、url.urlencode(data),将原始的dict转化为str类型;encode('utf-8'),将str类型转化为bytes类型;服务器接收的类型为bytes类型,所以中间将data进行类型转化的语句是:
data=parse.urlencode(data).encode() #encode参数缺省时默认为'utf-8'
此时的data为bytes类型,可以向服务器发送了
4、发送——request.urlopen(baseURL,data=data)
with request.urlopen(baseURL,data=data) as res:
json_data=res.read()
用json_data接收返回信息。此时json_data为bytes类型,要通过decode()解码为str类型,再通过json.loads()函数变为dict类型。
(实际应用时,我发现不用decode()解码,直接对read()的结果进行json.loads(),同样可以返回一个正常的dict对象,不知道以后有没有影响?)
5、为防止报错,可以用try...except程序块
6、这里的urlopen中的URL为baseURL,与data各自为urlopen的两个参数,不用连接成为一个完整的URL
7、有的网站返回的数据不是json类型,这时候就不能用json.load了,具体类型应去网页中通过F12开发者模式header->Content-Type获知。
request.Request(url= , data= , heads= , method='POST'/'GET')
request.urlopen()的功能有限,如果我们想为请求头部添加更多的设置信息,如“Content-Length”,那么用urlopen()是无法实现的。
因此,在此基础上,我们可以使用request.Request(),它的功能与urlopen()类似,可以打开网页,但比urlopen()优秀的是,它可以为请求头部自定义信息,无限扩展功能。
于是,在上面代码的基础上,可以修改一番:定义一个headers用以存放自定义的头部信息,然后用request.Request()创建一个Request实例,该实例将所有信息全部包括,到最后只需要用urlopen()打开这个实例即可。
另外,如果想要往该Request实例中补充headers,假设该实例名为req,则可以用方法add_header()实现:
req.add_header(Key , Value)
但需要注意的是add_header的Key-Value必须是合法的,不合法的header会导致其他错误!
from urllib import request,error,parse import json baseURL='https://fanyi.baidu.com/sug' kw=input('Input your keyword:') data={'kw':kw} data=parse.urlencode(data).encode() #构造headers,这里的header是自定义要访问的,如模拟浏览器访问服务器 #构造的headers中,至少包含Content-Length #传入Request参数的data和headers都是dict类型 headers={'Content-Length':len(data)} #用request.Request模拟浏览器访问服务器 #requset.Request()构造了一个Request实例,其中可以包含大量header内容 #URL为baseURL,data为输入的关键字参数,这里不用连接成一个完整的URL req=request.Request(baseURL,data=data,headers=headers,method='POST')
#添加一个User-Agent的Header信息 req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25') #最后用urlopen()打开该实例即可 try : with request.urlopen(req) as res: json_data=res.read() rec=json.loads(json_data) for k in rec['data']: print(k) except error.URLError as e: print(e)
结果与之前的相同。
比urlopen()好的是,Request可以无限扩展功能,模拟浏览器访问服务器,不仅如此,对于之后的身份隐藏等操作,也只需在headers中设置即可。
Post爬虫的基本操作流程:
①使用data参数创建内容(需要搜索的内容)
②用parse.urlencode和encode进行转码
③用urlopen或者request.Request打开URL,并传入data
④返回结果
⑤对结果进行其他操作
总结:
1、关键字参数(需要搜索的内容比如data)必须是dict类型,发送请求时,首先用parse.urlencode(data),将dict类型转化为str,再用encode()将str转化为服务器接收的bytes类型。
而把接收到的数据转化时,需要先read(),再decode(),再json.loads()。(实际应用时,发现不用decode()一样可以得到相同结果,推测可能是json.loads()的作用。暂不知道这样做有没有副作用)
2、想要对某个网页进行爬虫操作,需要用F12打开开发者模式,查看这个页面的信息。主要信息是:请求URL、访问服务器的方式、返回数据的格式(如果是JSON,需要用json模块转化)、Form data 如果有该项,要确定参数的Key名
3、dict类型:Request参数的data、headers;urlopen参数的data
4、GET与POST在程序中的最大区别是:
GET中的urlopen的参数只有URL(可能有headers)
POST中的urlopen的参数是URL+data(可能有headers),data为POST请求的数据
#GET req=request.Request(URL) with request.urlopen(req) as res: ... #POST req=request.Request(baseURL , data=data , headers=headers , method = 'POST') with request.urlopen(req) as res: ...
5、header不是GET与POST的区别,这两种请求均可以加header
方式是:
req=request.Request(URL,headers=headers)
header的目的是通过加首部信息模拟浏览器对服务器访问
6、有的网站返回的数据不是json类型,这时候就不能用json.load了,具体类型应去网页中通过F12开发者模式header->Content-Type获知。
比如:访问百度首页时的请求,就是html类型
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性