目录内容
- urllib基础知识
- 3.1 HTTP Response示例
- 3.2 HTTP POST示例
- 3.3 Timeout超时设置
- 3.4 Host请求头设置示例
- 3.5 设置中文的HTTP请求头并获取
- 3.7 ProxyHandle设置代理
- 3.8 获取并读取Cookie的示例
- 3.9 CookieFile
- 3.10 LoadCookie
- 3.11 UrlError
- 3.12 HTTPError
- 3.13 urlparse解析url地址
- 3.14 urlsplit分割url地址
- 3.15 urljoin合成url地址
- 3.16 urlencode对url进行解码
- 3.17 quote和unquote
- 3.18 parse_qs和parse_qsl拆解参数
- robots检查robot协议内容
urllib基础知识
urllib四个模块
1、request: 最基本的HTTP请求模块,可以用来发送HTTP请求,并接收服务端的相应数据。就像输入网址敲回车
2、error: 异常处理模块,如果出现请求错误可以捕捉异常,然后根据实际情况,重试或者忽略
3、parse: 工具模块,提供了很多处理URL的API,如拆分、解析、合并等
4、robotparser: 只要用来识别网站的robots文件,然后判断哪些网站可以抓取,那些网站不能抓取
Request类构造方法参数的作用:
1、url:用于发送请求的URL,必需
2、data:要提交的数据,必须是bytes类型
3、headers:请求头,字典类型
4、origin_req_host:请求方的host名称或IP地址
5、unverifiable:表示这个请求是否是无法验证的,默认是False,用户没有足够的权限来选择接收这个请求的结果
6、method:用来指定请求的方法,GET、POST、PUt等
3.1 HTTP Response示例
import urllib.request
# 想京东商城发送HTTP GET请求,urlopen函数既可以使用http,也可以使用https
response = urllib.request.urlopen('https://www.jd.com')
# 输出urlopen函数返回值的数据类型
print('response的类型:',type(response))
# 输出响应状态码、响应消息和HTTP版本
print('status:',response.status,'msg:',response.msg,'version:',response.version)
# 输出所有的响应头信息
print('headers:',response.getheaders())
# 输出名为Content-Type的响应头信息
print('headers.Content-Type',response.getheader('Content-Type'))
# 输出京东商城首页所有的HTML代码(经过utf-8解码)
print(response.read().decode('utf-8'))
3.2 HTTP POST示例
import urllib.request
# 将表单数据转换为bytes类型,用utf-8编码
data=bytes(urllib.parse.urlencode({'name':'Bill','age':30}),encoding='utf-8')
#提交HTTP POST请求
response = urllib.request.urlopen('http://httpbin.org/post',data=data)
# 输出响应数据
print(response.read().decode('utf-8'))
3.3 Timeout超时设置
import urllib.request
import socket
import urllib.error
try:
# 将请求设置为 0.1s
response=urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
except urllib.error.URLError as e:
# 判断抛出的异常是否为超时异常
if isinstance(e.reason,socket.timeout):
# 进行异常处理,只是简单输出了“超时”,真实情况可以进行更复杂的处理
print('超时')
# 在这里继续进行爬虫工作
print('在这里继续进行爬虫工作')
3.4 Host请求头设置示例
from urllib import request,parse
# 定义要提交的HTTP请求的URL
url = 'http://httpbin.org/post'
# 定义HTTP请求头,其中who是自定义的请求字段
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3 '
'AppleWebKit/537.36(KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
'Host':'httpbin.org',
'who':'Python Scrapy'
}
# 定义表单数据
dict = {
'name':'Bill',
'age':30
}
# 将表单数据转换为bytes形式
data = bytes(parse.urlencode(dict),encoding='utf-8')
#定义Request对象,通过Request类的构造方法指定了表单数据和HTTP请求头
req = request.Request(url=url,data=data,headers=headers)
#urlopen函数通过Request对象向服务端发送HTTP POST请求
response=request.urlopen(req)
#返回输出结果
print(response.read().decode('utf-8'))
3.5 设置中文的HTTP请求头并获取
from urllib import request
from urllib.parse import unquote,urlencode
import base64
url = 'http://httpbin.org/post'
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3 '
'AppleWebKit/537.36(KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
'Host':'httpbin.org',
#设置中文HTTP请求头,用url编码格式
'Chinese1':urlencode({'name':'李宁'}),
#设置中文HTTP请求头,用base64编码格式
'MyChinese':base64.b64encode(bytes('这是中文HTTP请求头',encoding='utf-8')),
'who':'Python Scrapy'
}
dict = {
'name':'Bill',
'age':30
}
data = bytes(urlencode(dict),encoding='utf-8')
req = request.Request(url=url,data=data,headers=headers,method="POST")
# 通过add_header方法添加中文HTTP请求头,url编码
req.add_header('Chinese2',urlencode({"国籍":"中国"}))
response=request.urlopen(req)
# 获取服务端的响应消息
value = response.read().decode('utf-8')
print(value)
import json
# 将返回值转换为json对象
responseObj = json.loads(value)
# 解码url编码格式的HTTP请求头
print(unquote(responseObj['headers']['Chinese1']))
# 解码url编码格式的HTTP请求头
print(unquote(responseObj['headers']['Chinese2']))
# 解码base64编码格式的HTTP请求头
print(str(base64.b64decode(responseObj['headers']['Mychinese']),'utf-8'))
3.7 ProxyHandle设置代理
通过ProxyHandler设置HTTP和HTTPS代理,然后访问相关的页面,并输出服务器的响应结果
from urllib.error import URLError
from urllib.request import ProxyHandler,build_opener
# 创建ProxyHandler对象,并指定HTTP和HTTPS代理的IP和端口号
proxy_handler = ProxyHandler({
'http':'http://182.86.191.16:24695',
'https':'https://182.86.191.16:24695'
})
opener =build_opener(proxy_handler)
try:
response = opener.open('https://www.jd.com')
print(response.read().decode('utf-8'))
except URLError as e:
print(e.reason)
3.8 获取并读取Cookie的示例
ps = '''首先运行此flask框架的内容在执行第二个文件的获取Cookie的操作'''
from flask import Flask
from flask import request
app = Flask(__name__)
# 输出客户端发送过来的所有Cookie,以及名为MyCookie的Cookie的值
@app.route("/readCookie")
def readCookie():
print(request.cookies)
print(request.cookies.get('MyCookie'))
return "hellow world"
# 向客户端写入名为id的Cookie(设置Set-Cookie字段)
@app.route('/writeCookie')
def writeCookie():
response = app.make_response('write cookie')
# 写入Cookie
response.set_cookie("id",value="123456789")
response.set_cookie("user", value="xiao_zang")
return response
if __name__ == '__main__':
app.run()
ps = '''再运行次文件获取Cookie'''
import http.cookiejar,urllib.request
# 创建CookieJar对象
cookie = http.cookiejar.CookieJar()
# 创建HTTPCookieProcessor对象
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
# 给http://www.baidu,com发送请求,获得相应数据
response = opener.open('http://www.baidu.com')
print('-----http://www.baidu.com-----')
# 输出服务端发送的所有Cookie
for item in cookie:
print(item.name + '=' + item.value)
print('-----http://127.0.0.1:5000/writeCooke-----')
# 下面的代码用同样的方式访问CookieServer,并输出返回的Cookie
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://127.0.0.1:5000/writeCookie')
for item in cookie:
print(item.name + '=' + item.value)
运行结果: -----http://www.baidu.com----- BAIDUID=D9805637EF14D31784DE7D2674560997:FG=1 BIDUPSID=D9805637EF14D3170E50C201F143C0C2 H_PS_PSSID=35837_31253_35978_36088_36165_34584_36144_36120_36073_35993_35956_35316_26350_35868_36102_36061 PSTM=1648304135 BDSVRTM=0 BD_HOME=1 -----http://127.0.0.1:5000/writeCooke----- id=123456789 user=xiao_zang
3.9 CookieFile
可以使用MozillaCookieJar类和LWPCookieJar类在获得Cookie的同时
将Cookie分别保存成Mozilla浏览器格式和libwww-perl(LWP)格式
ps = ''''''
import http.cookiejar,urllib.request
filename1 = 'cookies1.txt'
filename2 = 'cookies2.txt'
# 创建MozillCookieJar对象
cookie1 = http.cookiejar.MozillaCookieJar(filename1)
# 创建LWPCookieJar对象
cookie2 = http.cookiejar.LWPCookieJar(filename2)
handler1 = urllib.request.HTTPCookieProcessor(cookie1)
handler2 = urllib.request.HTTPCookieProcessor(cookie2)
opener1 = urllib.request.build_opener(handler1)
opener2 = urllib.request.build_opener(handler2)
opener1.open('http://www.baidu.com')
opener2.open('http://www.baidu.com')
# 将Cookie保存成Mozilla格式
cookie1.save(ignore_discard=True,ignore_expires=True)
# 将Cookie保存成LWP格式
cookie2.save(ignore_discard=True,ignore_expires=True)
ps2 = '''运行的结果会生成Cookie1和Cookie2的两种Cookie格式文件'''
在获得服务端发送过来的Cookie后,当爬虫再次访问服务端是,一般需要将这些Cookie原封不动再发送给服务端,
所以就需要从Cookie文件中读取Cookie,然后将这些Cookie发送给服务端,需要用到load()方法
3.10 LoadCookie
创建一个名为cookies.txt的文件,并自定义2个Cookie,
然后通过load方法安装在cookies.txt文件中的Cookie,并发给3.8.1的CookieServer
注意此案例需要启动3.8.1的Server服务器
import http.cookiejar,urllib.request
filename = 'cookies.txt'
cookie = http.cookiejar.LWPCookieJar()
# 装载cookies.txt文件,由于使用了LWPCookieJar读取Cookie,所以Cookie文件必须是LWP格式
cookie.load(filename,ignore_discard=True,ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://127.0.0.1:5000/readCookie')
print(response.read().decode('utf-8'))
3.11 UrlError
向不同服务器发送多个请求,并用URLError类捕捉发生的异常
ps = '''本例向不同服务器发送多个请求,并用URLError类捕捉发生的异常'''
from urllib import request,error
try:
response = request.urlopen('http://www.jd123.com/test.html')
except error.URLError as e:
# Bad Request 坏的请求头
print(e.reason)
try:
response = request.urlopen('https://geekori.com/abc.html')
except error.URLError as e:
# Not Found 资源无法访问
print(e.reason)
try:
response = request.urlopen('https://geekori123.com/abc.html')
except error.URLError as e:
# [Errno 8] nodename nor servname provided, or not known
print(e.reason)
try:
response = request.urlopen('https://bbbccc.com',timeout=2)
except error.URLError as e:
# time out 超时
print(e.reason)
3.12 HTTPError
HTTPError是URLError的子类,专门用于处理HTTP请求错误,如400、404错误,该类的三个属性
1、code:返回HTTP状态码
2、reason:用于返回错误原因
3、headers:返回请求头
由于HTTPError是子类,所以try catch中先用HTTPError捕捉异常,再用URLError捕捉异常
from urllib import request,error
import socket
try:
response = request.urlopen('http://www.jd123.com/test.html')
except error.HTTPError as e:
print(type(e.reason))
print(e.reason,e.code,e.headers)
try:
response = request.urlopen('https://bbbccc.com',timeout=2)
except error.HTTPError as e:
print('error.HTTPError:',e.reason)
except error.URLError as e:
print(type(e.reason))
print('error.URLError:',e.reason)
# 判断r.reason的类型是否为socket.timeout类
if isinstance(e.reason,socket.timeout):
print('超时错误')
else:
print('成功发送请求')
3.13 urlparse解析url地址
urlparse函数用于拆分URL,有3个参数
1、url:必填参数,待解析的URL
2、scheme:可选,如果url没有协议,默认为空
3、allow_fragments:可选参数,表示是否忽略fragment部分,如果为False忽略,默认为True
# 公式:scheme://netloc/path;params?query#fragment
# 本例使用urlparse函数拆分了一个URL,并输出拆分后的各个部分,以及使用urlunparse函数合并不同的部分,组成完整的URL
from urllib.parse import urlparse,urlunparse
result = urlparse('https://search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment')
print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)
print('params:',result.params)
print('query:',result.query)
print('fragment:',result.fragment)
print('-----------------')
# 拆分URL,指定默认的scheme,并且忽略fragment部分
result = urlparse('search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment',scheme='ftp',allow_fragments=False)
print('scheme:',result.scheme)
print('fragment:',result.fragment)
print('----------------')
# 合并不同的部分,组成一个完整的URL
data = ['https','search.jd.com','Searchprint','world','keyword=Python从菜鸟到高手&enc=utf-8','comment']
print(urlunparse(data))
3.14 urlsplit分割url地址
第二种分割url的方法,urlsplit()
from urllib.parse import urlsplit,urlunsplit
result = urlsplit('https://search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment')
# 将URL拆分成5部分,并指定默认的scheme,以及不考虑fragment部分
print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)
print('query:',result.query)
print('fragment:',result.fragment)
print('-----------------')
result = urlsplit('search.jd.com/Searchprint;hello?keyword=Python从菜鸟到高手&enc=utf-8#comment',scheme='ftp',allow_fragments=False)
# 将5部分合并成完整的URL
print('scheme:',result.scheme)
print('fragment:',result.fragment)
print('----------------')
data = ['https','search.jd.com','Searchprint;world','keyword=Python从菜鸟到高手&enc=utf-8','comment']
print(urlunsplit(data))
3.15 urljoin合成url地址
使用urljoin函数合成不同的部分,组成一个完整的URL。
urljoin函数的第1个参数是base_url,是一个基URL,只能设置scheme、netloc和path,第2个参数是url。
如果第2个参数不是一个完整的URL,将第2个参数的值加到第1个参数后面,自动添加斜杠(/)。
如果第2个参数是一个完整的URL,那么直接返回第2个参数的值
from urllib.parse import urljoin
# 输出https://www.jd.com/index.html
print(urljoin('https://www.jd.com','index.html'))
# 输出https://www.taobao.com
print(urljoin('https://www.jd.com','https://www.taobao.com'))
# 输出https://www.taobao.com/index.html
print(urljoin('https://www.jd.com/index.html','https://www.taobao.com/index.html'))
# 输出https://www.jd.com/index.php?name=Bill&age=30
print(urljoin('https://www.jd.com/index.php','?name=Bill&age=30'))
# 输出https://www.jd.com/index.php?name=Bill
print(urljoin('https://www.jd.com/index.php?value=123','?name=Bill'))
3.16 urlencode对url进行解码
中文转码后的格式是%xx,其中xx是2位十六进制数
urlencode函数接受一个字典类型,用于定义URl的参数
from urllib.parse import urlencode,urljoin
params = {
'name':'王军',
'country':'China',
'age':30
}
base_url = 'https://www.google.com?'
# url = base_url + urlencode(params)
url = urljoin(base_url,'?' + urlencode(params))
print(url)
# https://www.google.com?name=%E7%8E%8B%E5%86%9B&country=China&age=30
3.17 quote和unquote
使用quote函数对URL中的参数编码,然后使用unquote函数进行解码
from urllib.parse import quote,unquote
keyword = '李宁'
# 对参数进行编码
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)
# 解码
url = unquote(url)
print(url)
3.18 parse_qs和parse_qsl拆解参数
parse_qs函数将多个参数拆成字典的形式,key是参数名,value是参数值,
parse_qsl函数返回一个列表,每个元素时一个包含2个元素的元组,第1个元素值表示key,第2个元素值表示value
from urllib.parse import parse_qs,parse_qsl
query = 'name=王军 & age=35'
# 输出{'name': ['王军 '], ' age': ['35']}
print(parse_qs(query))
# 输出[('name', '王军 '), (' age', '35')]
print(parse_qsl(query))
query = 'name=王军 & age=35 & name=Bill & age=30'
# 输出{'name': ['王军 '], ' age': ['35 ', '30'], ' name': ['Bill ']}
print(parse_qs(query))
# 输出[('name', '王军 '), (' age', '35 '), (' name', 'Bill '), (' age', '30')]
print(parse_qsl(query))
robots检查robot协议内容
当爬虫访问一个网站是,首先会检查这个网址根目录下是否存在robots.txt文件,
如果存在,爬虫就会根据该文件中定义的抓取范围来抓取Web资源。
如果这个文件并不存在,爬虫就会抓取整个网站所有可直接访问的页面
url = 'https://www.jianshu.com/robots.txt'
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
'Host': 'www.jianshu.com',
}
req = request.Request(url=url, headers=headers)
# 抓取robots.txt文件的内容,并提交给parse方法进行分析
robot.parse( request.urlopen(req).read().decode('utf-8').split('\n'))
# 使用can_fetch方法判断是否可以抓取某一个页面
# 输出True
print(robot.can_fetch('*','https://www.jd.com'))
# 输出True
print(robot.can_fetch('*','https://www.jianshu.com/p/92f6ac2c350f'))
# 输出False
print(robot.can_fetch('*','https://www.jianshu.com/search?q=Python&page=1&type=note'))