Urllib库的基本方法和使用详解 Python爬虫底层的请求模块

首页 » Python » Urllib库的基本方法和使用详解 Python爬虫底层的请求模块

urllib库,这个是最基本的爬虫库,后面学习的requests库是基于这个进行了封装,我们只要知道底层的使用方法,那么requests就是大同小异。在urllib中存在以下四种HTTP请求模块:

  • urllib.request 请求模块
  • urllib.error 异常处理
  • urllib.parse url解析模块
  • urllib.robotparser robots.txt解析模块

urllib.request.urlopen

最常用的方法就是urllib.request.urlopen,以下是它的参数:

urllib.request.urlopen(url, 
    data=None,
    [timeout,]*, 
    cafile=None,
    capath=None, 
    cadefault=False,
    context=None)

我们刚开始学习的话,只需要注意前三个参数即可,其中有个data参数,你加上的话就是post请求,没有的话就是get请求。我们想要加入代理或者请求头的话就不能使用urllib.request.urlopen()方法,因为没有参数来接受值,就是需要使用urllib.request.Request()

发送get请求

现在我们使用get方式请求百度的url来实现搜索功能,在搜索栏中输入“美女”,会自动跳转到相关的页面,现在我就想要得到这个页面,该怎么搞呢?你可以先采用字符串拼接的方式来找到搜索后的url,有了url我们就可以使用上面的方法来请求url,那么Python解释器会报错UnicodeEncodeError: 'ascii' codec can't encode characters in position 7-8: ordinal not in range(128),原因就是你的url错误,你采用拼接的方式得到的是一个字符串,并不是url,所以这时候就需要使用urllib.parse.quote()方法来拼接,这个你也可以单独打印一下看看返回的结果跟你字符串拼接的一样么。我们拼接的url中如果带有中文的话就需要转换成ascii。

import urllib.request
import urllib.parse
import string
def get_methond():
    base_url='https://baidu.com/s?'
    keyword='美女' #拼接字符串
    new_url=base_url+keyword #url里面含有中文;ascii是没有汉子的,需要尽行url转译
    true_url=urllib.parse.quote(new_url,safe=string.printable) #将包含汉字的url进行转码,拿到正确的url地址
    response=urllib.request.urlopen(true_url) #使用代码发送网络请求
    data=response.read().decode() #read读取内容类型为bytes decode转换为字符串
    with open ('day-01.html','w',encoding='utf-8') as f:
        f.write(data) #可以写入为html文件,然后打开验证结果是否正确
get_methond()

同样我们也可以用字典来拼接参数,需要使用urllib.parse.urlencode()方法,得到的数据是一样的,也比较简单就不做注释了。

import urllib.request
import urllib.parse
def get_methond():
    base_url='https://baidu.com/s?'
    key_dict={'wd':'美女',}
    true_url=base_url+urllib.parse.urlencode(key_dict)
    header={
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0'
    }
    print(true_url)
    request=urllib.request.Request(true_url,headers=header)
    response=urllib.request.urlopen(request)
    data=response.read().decode('utf-8')
    with open ('day-01.html','w',encoding='utf-8') as f:
        f.write(data)
get_methond()

urllib.request.urlretrieve

我们通常需要从网页下载文件,包括一些图片和软件,我们可以通过urllib.request模块的urlretrieve来实现。

urlretrieve(url, #下载链接地址
    filename=None, #指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据)
    reporthook=None, #是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。
    data=None #指post导服务器的数据,该方法返回一个包含两个元素的(filename, headers) 元组,filename 表示保存到本地的路径,header表示服务器的响应头)

那么我们可以使用这个方法下载这个图片,这个图片是一个下载链接:

from urllib.request import urlretrieve
url='http://2020.jinyemimi.com/2019/1279/d.rosi'
file='C:/图片/{0}.jpg'.format(url.split('/')[-2]) #路径必须是图片路径,不能放个文件夹这个报错
urlretrieve(url,file)

发送post请求

默认使用post请求的话就要加上data参数:

import urllib.request
import urllib.parse
url='https://cn.bing.com/translator/'
data={
'fromLang': 'auto-detect',
'text': 'nihao',
'to': 'en',
}
byte_data=urllib.parse.urlencode(data).encode('utf-8')# 将字典转转译,然后转换为字节类型
response=urllib.request.urlopen(url,data=byte_data) #post请求,需要传入一个bytes类型的data参数
print(response.read().decode())

urllib.request.Requset

urllib.request.Request(url, 
    data=None, 
    headers={}, 
    origin_req_host=None, 
    unverifiable=False, 
    method=None)

有了这个方法我们就可以在请求url的时候带上请求头,加上cookie。urllib.request.Request 有个参数method 可以指定请求方法,也可以通过 data 指定请求参数,还可以通过 headers 指定请求头。

import urllib.request
import urllib.parse
url='https://www.baidu.com/'
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:70.0)'+
                  ' Gecko/20100101 Firefox/70.0',
}
#第一种方式
request=urllib.request.Request(url,headers=headers)# 创建请求对象
response=urllib.request.urlopen(request)#请求网络数据(系统没有提供增添请求头的参数)
# #第二种方式
# request.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:70.0)'+
            # ' Gecko/20100101 Firefox/70.0') #动态添加请求头
# response=request.urlopen(request)
print(response.read().decode('utf-8'))

在添加请求头的时候,我们只是选择了一个user-agent,有时候我们可以使用多个user-agent,这时候你可以先把user-agent放到列表中,然后使用random模块中的random.choice方法,随机收取一个添加到请求头中,方法虽然有点捞,但是就呢样吧,或者你也可以其他办法(自己想不提示因为我也不会😸)。我们也可以查看请求头中的信息,响应头中的信息.

import urllib.request
import urllib.parse
base_url='https://baidu.com/s?'
key_dict={'wd':'美女',}
true_url=base_url+urllib.parse.urlencode(key_dict)
header={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0'
}
print(true_url)
request=urllib.request.Request(true_url,headers=header) #创建请求对象
response=urllib.request.urlopen(request) #请求网络数据
data=response.read().decode('utf-8')
print(type(response)) #查看response的类型 'http.client.HTTPResponse'
print(response.status) #查看返回的状态码
print(response.getheaders) #获取响应头的信息 内存地址
print(response.getheader('Server')) #获取响应头中特定的信息
print(request.get_full_url()) # 获取到完整的url
#第一种方式
print(response.headers) #获取响应头的信息
print(request.headers) #获取请求头的信息,就是你自己加的
#第二种方式
request_headers=request.get_header('User-agent') #获取请求头的信息,首字母大写,其余小写
print(request_headers)

Handler辅助工具

前面说的urllib.request.urlopen()方法不支持代理和cookie的添加,那么就需要使用相应的Handler处理来解决这个问题。为了更好的理解Handler,你可以查看urlopen的源码,其实它就是自己创建Handler,通过下面的代码可以实现跟urlopen同样的功能:

import urllib.request
import urllib.parse
url='https://baidu.com'
#系统的urlopen并没有添加代理的功能,所以需要我们自定义这个功能
#urllopen为什么可以请求数据 查看源码会发现它含有Handler处理器

#创建自己的处理器
handler=urllib.request.HTTPSHandler()
#创建自己的opener
opener=urllib.request.build_opener(handler)
#用自己创建的opener调用open方法请求参数
response=opener.open(url)
print(response.read().decode())

代理

那么想使用代理就需要创建相应的Handler即可,之前需要知道代理分为透明代理(服务器知道你使用了代理,也知道你的真实ip)、匿名代理(服务器知道你使用了代理,但是不知道你的真实ip)、高匿代理(服务器不知道你使用代理,更不知道你的真实ip),我们做测试的一半来说就先拿免费的代理来练习,这里可以百度搜索一下免费的代理。

import urllib.request
import urllib.parse
url='https://baidu.com'
#添加代理
proxy={
    'http':'114.231.45.235:9999',
}
#代理处理器
proxy_handler=urllib.request.ProxyHandler(proxy)
#创建自己的opener
opener=urllib.request.build_opener(proxy_handler)
try:
    #携带代理去发送请求
    response=opener.open(url,timeout=1)
    print(response.read().decode())
except Exception as e:
    print(e)

一般来说大部分免费的ip都不是太好用,所以会遇到相应的错误,你可以抓一下异常,一般都是超时。有的小伙伴会使用付费的代理,同样可以使用下面的代码:

import urllib.request
import urllib.parse
url='https://baidu.com'
#付费代理ip(带有用户名和密码)
vip_proxy={'http':'username:password@114.231.45.235:9999'}
#代理处理器
proxy_handler=urllib.request.ProxyHandler(vip_proxy)
#创建自己的opener
opener=urllib.request.build_opener(proxy_handler)
#携带代理去发送请求
response=opener.open(url)

#第二种方式发送付费的代理
use_name='username'
password='password'
proxy_vip='114.231.45.235:9999'
#创建密码管理器,添加用户名和密码
manager=urllib.request.HTTPPasswordMgrWithDefaultRealm()
#add_password接收四个参数,可以查看源码
#(realm, uri, user, passwd) uri>url(统一资源定位符)
manager.add_password(None,proxy_vip,use_name,password)
#创建可以验证代理ip的处理器
handler_auth_proxy=urllib.request.ProxyBasicAuthHandler(manager)
#根据处理器创建opener
opener_auth=urllib.request.build_opener(handler_auth_proxy)
#发送请求
response_auth=opener_auth.open(url)

print(response.read().decode())

Cookie

用来记录用户身份的东西,可以在自己的浏览器里面查看,使用的话,可以维持我们的登录信息,可以爬取一些需要登录认证的网址,比方说我要爬取淘宝的个人中心页面:

import urllib.request
#淘宝个人中心url
url='个人中心的url'
#添加请求头
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
    'Cookie':'登录之后在个人中心的network里面找'
}
#构建请求对象
request=urllib.request.Request(url,headers=headers)
#发送请求对象
response=urllib.request.urlopen(request)
#读取数据并打印
print(response.read().decode('gbk'))#这个个人中心的编码是gbk

同样我们可以使用代码登录,成功之后会有cookie,然后自动携带cookie去请求个人中心也是可以的,那么就以淘宝的个人中心为例,难点就在寻找登录的参数,可以先在登录的页面寻找,或者先带上用户名和密码两个参数尝试,不行在寻找其他的参数,个人感觉在登录之后的个人中心页面寻找参数也可以,那么这种方式就没有意义了,既然登录了我直接找cookie不就得了,还找参数干什么

import urllib.request
import urllib.parse
from http import cookiejar
#登录界面的url
login_url='https://login.taobao.com/member/login.jhtml'
#登录的参数
login_form_data={
    "TPL_username": "淘宝用户名或者手机号",
"TPL_password": "密码",
"um_token": "",#该参数必须的,在登录之后的个人中心页面寻找
}
#声明为CookieJar对象
cook_jar=cookiejar.CookieJar()
#定义有添加cookie功能的 处理器
cook_handler=urllib.request.HTTPCookieProcessor(cook_jar)
#根据处理器生成的opener
opener=urllib.request.build_opener(cook_handler)
#带着参数发送post请求 加上请求头
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
}
login_data=urllib.parse.urlencode(login_form_data).encode('utf-8')
#构建请求对象
login_request=urllib.request.Request(login_url,headers=headers,data=login_data)
#如果登录成功,cookiejar会自动保存cookie
opener.open(login_request)

#代码带着cookie去访问个人中心的页面
user_center='https://i.taobao.com/my_taobao.htm'
user_center_request=urllib.request.Request(user_center,headers=headers)
user_response=opener.open(user_center_request)
data=user_response.read()
with open('day-02.html','wb') as f:#保存为html文件,打开查看结果是正确
    f.write(data)
import http.cookiejar,urllib.request
filename='cookie.txt'
cookie=http.cookiejar.MozillaCookieJar(filename)#声明为CookieJar子类对象,带有save方法
handler=urllib.request.HTTPCookieProcessor(cookie)
opener=urllib.request.build_opener(handler)
response=opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)#
#会在本地生成cookie.txt文件
import http.cookiejar,urllib.request
cookie=http.cookiejar.LWPCookieJar()
cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)
handler=urllib.request.HTTPCookieProcessor(cookie)
opener=urllib.request.build_opener(handler)
response=opener.open('http://www.baidui.com')
print(response.read().decode('utf-8'))

一个是储存cookie.txt的,一个是读取的

urllib.error

一般来说可能遇到url错误或者404,就会显示

import urllib.request
import urllib.error
try:
    response=urllib.request.urlopen("https://e1yu/yingshi")
except urllib.error.HTTPError as e:#捕获子类异常,网址404
    print(e.reason,e.code,e.headers,sep="\n")
except urllib.error.URLError as e:#捕获父类异常,网址不存在
    print(e.reason)
else:
    print("Request Successfuily")

urllib.parse

下面先介绍 urllib.parse 子模块中用于解析 URL 地址和查询字符串的函数:

1、urllib.parse.urlparse(urlstring, scheme=”, allow_fragments=True)

该函数用于解析 URL 字符串。程序返回一个 ParseResult 对象,可以获取解析出来的数据。

2、urllib.parse.urlunparse(parts)

该函数是上一个函数的反向操作,用于将解析结果反向拼接成 URL 地址。


3、urllib.parse.parse_qs(qs,keep_blank_values=False, strict_parsing=False, encoding=’utf-8′, errors=’replace’)

该该函数用于解析查询字符串,并以 dict 形式返回解析结果。


4、urllib.parse.parse_qsl(qs,keep_blank_values=False, strict_parsing=False, encoding=’utf-8′, errors=’replace’)

该函数用于解析查询字符串,并以列表形式返回解析结果。


5、urllib.parse.urlencode(query, doseq=False, safe=”, encoding=None,errors=None, quote_via=quote_plus)

将字典形式或列表形式的请求参数恢复成请求字符串。该函数相当于 parse_qs()、parse_qsl() 的逆函数。

6、urllib.parse.urljoin(base,url,allow_fragments=True)

该函数用于将一个 base_URL 和另一个资源 URL 连接成代表绝对地址的 URL。

实例引入

from urllib.parse import urlparse
result=urlparse('https://www.baidu.com/baidu?tn=monline_3_dg&ie=utf-8&wd=urllib.request')
print(type(result)) #上面的网址,就是在百度搜索之后,它会产生参数
print(result) #返回结果就是把url各部分分割开来,我们根据参数来提取
print(result.query,result[4]) #也可以通过索引,自行尝试

通常我们要获得就是后半部分,带有参数的,我们肉眼看不出来,可以用这种解析方式,然后可以进行提取

# 解析查询字符串,返回dict类型
result = parse_qs(result.query) #一般来说query就是url后面的参数,我们只需要解析这个
print(result)

# 解析查询字符串,返回list类型
result = parse_qsl(result.query)
print(result)

# 将列表格式的请求参数恢复成请求参数字符串
print(urlencode(result))

urllib.parse.urlunparse就是和urlparse相反的用法,上面的是拆分,这个就是拼接了

from urllib.parse import urlunparse
data=['http','www.baidui.com','index.html','user','a=6','comment']
print(urlunparse(data))
from urllib.parse import urljoin
print(urljoin('http://www.baidu.com','s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baiduerr&bar=&wd=python'))
print(urljoin('http://www.baidu.com','https://e1yu'))
print(urljoin('https://www.baidu.com/link?','https://e1yu/post-721.html'))

用法就是这么个用法,自己在多试试,换个url,输出什么我也不再贴代码了,总之,后面的url会覆盖前面的,后面的url如果没有就用前面的,总感觉这个代码编辑起不怎么好看,准备换掉它

urllbi.parse.urlencode通常会采用下面的方法使用,它可以吧一个字典对象转换为get参数,还是比较有用的

from urllib.parse import urlencode
params={

    'name':'e1yu',
    'age':'1'
}
base_url='http://www.baidu.com?'
url=base_url+urlencode(params)
print(url)
#这个真的比较有用

未经允许不得转载:作者:鳄鱼君, 转载或复制请以 超链接形式 并注明出处 鳄鱼君
原文地址:《Urllib库的基本方法和使用详解 Python爬虫底层的请求模块》 发布于2019-11-09

分享到:
赞(0) 赏杯咖啡

评论 抢沙发

3 + 3 =


文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册