urllib.request默认只支持HTTP/HTTPS的GET
和POST
方法
urllib.parse.urlencode()
urllib 和 urllib2 都是接受URL请求的相关模块,但是提供了不同的功能。两个最显著的不同如下:
urllib 仅可以接受URL,不能创建 设置了headers 的Request 类实例;
但是 urllib 提供
urlencode
方法用来GET查询字符串的产生,而 urllib2 则没有。(这是 urllib 和 urllib2 经常一起使用的主要原因)编码工作使用urllib的
urlencode()
函数,帮我们将key:value
这样的键值对转换成"key=value"
这样的字符串,解码工作可以使用urllib的unquote()
函数。(注意,不是urllib2.urlencode() )
一般HTTP请求提交数据,需要编码成 URL编码格式,然后做为url的一部分,或者作为参数传到Request对象中。
Get方式
GET请求一般用于我们向服务器获取数据,比如说,我们用百度搜索传智播客
:https://www.baidu.com/s?wd=传智播客
浏览器的url会跳转成如图所示:
https://www.baidu.com/s?wd=%E4%BC%A0%E6%99%BA%E6%92%AD%E5%AE%A2
在其中我们可以看到在请求部分里,http://www.baidu.com/s?
之后出现一个长长的字符串,其中就包含我们要查询的关键词传智播客,于是我们可以尝试用默认的Get方式来发送请求。
1 from urllib import request 2 import urllib.parse 3 4 keyword=input('请输入关键字:') 5 a=urllib.parse.urlencode({'wd':keyword}) #url编码 6 # print(a) 7 # b=urllib.parse.unquote(a) #url解码 8 # print(b) 9 header={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"} 10 fullurl='http://www.baidu.com/s'+'?'+a 11 c=request.Request(fullurl,headers=header) 12 request.urlopen(c) 13 print(fullurl)
批量爬取贴吧页面数据
首先我们创建一个python文件, tiebaSpider.py,我们要完成的是,输入一个百度贴吧的地址,比如:
百度贴吧LOL吧第一页:http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=0
第二页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=50
第三页: http://tieba.baidu.com/f?kw=lol&ie=utf-8&pn=100
发现规律了吧,贴吧中每个页面不同之处,就是url最后的pn的值,其余的都是一样的,我们可以抓住这个规律。
简单写一个小爬虫程序,来爬取百度LOL吧的所有网页。
1 from urllib import request 2 import urllib.parse 3 4 #根据url发送请求,获取服务器响应文件 5 def getPage(url,filename): 6 print('正在下载'+filename) 7 header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"} 8 a=request.Request(url,headers=header) 9 return request.urlopen(a).read() 10 11 #将html内容写入到本地 12 def saveFile(filename,html): 13 print('正在保存' + filename) 14 with open(filename,'wb') as f: 15 f.write(html) 16 print('*'*20) 17 18 #贴吧爬虫调度器,负责组合处理每个页面的url 19 def tiebaSpider(url,startpage,endpage): 20 for page in range(startpage,endpage+1): 21 pn=(page-1)*50 22 fullurl=url+'&pn='+str(pn) 23 print(fullurl) 24 filname='第'+str(page) + "页.html" 25 html = getPage(fullurl, filname) 26 saveFile(filname,html) 27 print('谢谢使用!') 28 29 if __name__ == '__main__': 30 kw=input('请输入要搜索的内容:') 31 startpage=int(input('请输入从第几页开始:')) 32 endpage=int(input('请输入从第几页结束:')) 33 34 kw=urllib.parse.urlencode({'kw':kw}) 35 url='http://tieba.baidu.com/f?'+kw 36 37 tiebaSpider(url, startpage, endpage)
其实很多网站都是这样的,同类网站下的html页面编号,分别对应网址后的网页序号,只要发现规律就可以批量爬取页面了。
POST方式:
上面我们说了Request请求对象的里有data参数,它就是用在POST里的,我们要传送的数据就是这个参数data,data是一个字典,里面要匹配键值对。
有道词典翻译网站:
输入测试数据,再通过使用Fiddler观察,其中有一条是POST请求,而向服务器发送的请求数据并不是在url里,那么我们可以试着模拟这个POST请求。
于是,我们可以尝试用POST方式发送请求。
1 from urllib import request 2 import urllib.parse 3 4 # url='http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule' 5 url='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule' 6 header={ 7 # "Accept": "application/json, text/javascript, */*; q=0.01", 8 "X-Requested-With": "XMLHttpRequest", 9 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", 10 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 11 "Origin": "http://fanyi.youdao.com" 12 } 13 14 data={ 15 "i":"python", 16 "from":"AUTO", 17 "to":"AUTO", 18 "smartresult": "dict", 19 "client":"fanyideskweb", 20 "salt": "15825474729954", 21 "sign":"792ead532594975beac7426b8b565dff", 22 "ts":"1582547472995", 23 "bv":"4b1009b506fa4405f21e207abc4459fd", 24 "doctype": "json", 25 "version": "2.1", 26 "keyfrom": "fanyi.web", 27 "action ":"FY_BY_REALTlME" 28 } 29 30 datas=urllib.parse.urlencode(data).encode('utf-8') 31 32 a=request.Request(url,data=datas,headers=header) 33 print(request.urlopen(a).read().decode('utf-8'))
发送POST请求时,需要特别注意headers的一些属性:
Content-Length: 144
: 是指发送的表单数据长度为144,也就是字符个数是144个。
X-Requested-With: XMLHttpRequest
:表示Ajax异步请求。
Content-Type: application/x-www-form-urlencoded
: 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。
获取AJAX加载的内容
有些网页内容使用AJAX加载,只要记得,AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就返回JSON数据了。
"作为一名爬虫工程师,你最需要关注的,是数据的来源"
1 from urllib import request 2 import urllib.parse 3 4 5 ######post方式 6 url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend" 7 8 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"} 9 10 formdata = { 11 "page_limit":"5", 12 "page_start":"20" 13 } 14 15 data=urllib.parse.urlencode(formdata).encode('utf-8') 16 a=request.Request(url,data=data,headers=headers) 17 18 print(request.urlopen(a).read().decode('utf-8')) 19 20 21 #####get方式 22 url1 = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=5&page_start=20" 23 24 headers1 = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"} 25 26 b=request.Request(url1,headers=headers1) 27 # print(request.urlopen(b).read().decode('utf-8'))
问题:为什么有时候POST也能在URL内看到数据?
GET方式是直接以链接形式访问,链接中包含了所有的参数,服务器端用Request.QueryString获取变量的值。如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。
POST则不会在网址上显示所有的参数,服务器端用Request.Form获取提交的数据,在Form提交的时候。但是HTML代码里如果不指定 method 属性,则默认为GET请求,Form中提交的数据将会附加在url之后,以
?
分开与url分开。表单数据可以作为 URL 字段(method="get")或者 HTTP POST (method="post")的方式来发送。比如在下面的HTML代码中,表单数据将因为 (method="get") 而附加到 URL 上:
<form action="form_action.asp" method="get"> <p>First name: <input type="text" name="fname" /></p> <p>Last name: <input type="text" name="lname" /></p> <input type="submit" value="Submit" /> </form>
处理HTTPS请求 SSL证书验证
现在随处可见 https 开头的网站,urllib2可以为 HTTPS 请求验证SSL证书,就像web浏览器一样,如果网站的SSL证书是经过CA认证的,则能够正常访问,如:https://www.baidu.com/
等...
如果SSL证书验证不通过,或者操作系统不信任服务器的安全证书,比如浏览器在访问12306网站如:https://www.12306.cn/mormhweb/的时候,会警告用户证书不受信任。(据说 12306 网站证书是自己做的,没有通过CA认证)
urllib2在访问的时候则会报出SSLError:
import urllib2 url = "https://www.12306.cn/mormhweb/" headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} request = urllib2.Request(url, headers = headers) response = urllib2.urlopen(request) print response.read()
运行结果:
urllib2.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>
所以,如果以后遇到这种网站,我们需要单独处理SSL证书,让程序忽略SSL证书验证错误,即可正常访问。
import urllib import urllib2 # 1. 导入Python SSL处理模块 import ssl # 2. 表示忽略未经核实的SSL证书认证 context = ssl._create_unverified_context() url = "https://www.12306.cn/mormhweb/" headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} request = urllib2.Request(url, headers = headers) # 3. 在urlopen()方法里 指明添加 context 参数 response = urllib2.urlopen(request, context = context) print response.read()
关于CA
CA(Certificate Authority)是数字证书认证中心的简称,是指发放、管理、废除数字证书的受信任的第三方机构,如北京数字认证股份有限公司、上海市数字证书认证中心有限公司等...
CA的作用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理。
现实生活中可以用身份证来证明身份, 那么在网络世界里,数字证书就是身份证。和现实生活不同的是,并不是每个上网的用户都有数字证书的,往往只有当一个人需要证明自己的身份的时候才需要用到数字证书。
普通用户一般是不需要,因为网站并不关心是谁访问了网站,现在的网站只关心流量。但是反过来,网站就需要证明自己的身份了。
比如说现在钓鱼网站很多的,比如你想访问的是www.baidu.com,但其实你访问的是www.daibu.com”,所以在提交自己的隐私信息之前需要验证一下网站的身份,要求网站出示数字证书。
一般正常的网站都会主动出示自己的数字证书,来确保客户端和网站服务器之间的通信数据是加密安全的。