近日想学习爬虫,入手一本《python3:网络爬虫开发实战》,此书不错,打算学习一番,为此发文记录自己的学习成果。
前言是一些html基础和爬虫基础,文字太多,我们直接开始干货。
python的基本库urllib的使用。
由于是python的基本库,所以无需安装库,可直接使用。
urlopen
urllib.request模块提供了最基本的构造http请求方法,利用这个模块可以模拟浏览器请求发起过程。同时它还具有处理授权验证(Authentication),重定向(Redirection),浏览器cookie以及其他一些功能。
import urllib.request
resp=urllib.request.urlopen('https://www.python.org')
print(resp.read().decode('utf-8'))
以上述代码为例,只有了两行代码就完成了对python官网的抓取,输出网页源码

接下来我们看看网页返回的响应是什么
示例代码:
import urllib.request
resp=urllib.request.urlopen('https://www.python.org')
print(type(resp))
返回响应

可以看出响应是一个HTTPResponse类型的对象,主要包含了read,readinto,getheader,getheaders,flieo等方法。以及msg,version,status,reason,debuglevel.closed等属性。
示例代码
import urllib.request
resp=urllib.request.urlopen('https://www.python.org')
print(resp.status)
print(resp.getheaders())
print(resp.getheader('Server'))
返回结果

原理分析,前两个输出分别是响应的状态码和响应的头信息,最后一个输出是电泳getheader方法,并传入参数Server,获取响应头中的Server的值,结果是nginx,意思是Nginx搭建的。
以上利用最基本的urlopen方法已经完成对基本网页的get请求抓取。
如果想给链接传递一些参数又该如何实现呢,首先看下urlopen方法的源码。ctrl点击urlopen
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
*, cafile=None, capath=None, cadefault=False, context=None):
可以发现除了第一个参数传递url之外,我们还可以传递其他内容,例如data(附加数据),timeout(超过时间停止程序)等。接下来我们详细说明一下urlopen方法中其他几个参数的用法。
data参数(附加数据)
data参数是可选的,即需要时可以添加。在添加该参数时,需要使用bytes方法将参数转化为字节流编码格式的内容,即bytes类型。另外,如果传递了这个函数,那么它的请求就不再是GET,而是POST了。
示例代码(https://www.httpbin.org/post 是专门提供测试Http请求网站,使用post方法是因为代码中携带了data参数)
import urllib.parse
import urllib.request
data=bytes(urllib.parse.urlencode({'name':"germey"}),encoding='utf-8')
resp=urllib.request.urlopen("https://www.httpbin.org/post",data=data)
print(resp.read().decode('utf-8'))
运行结果

可以发现我们传递的参数出现在from字段中,这表明是模拟表单提交,以POST方式传输数据。
timeout参数(设置超时时间)
timeout参数用于设置超时时间,单位为秒,意思是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常中止程序,如果不指定该参数,则会使用全局默认时间。这个参数支持HTTP,HTTPS,FTP请求。
示例代码
import urllib.request
resp=urllib.request.urlopen('https://www.httpbin.org/get',timeout=0.1)
print(resp.read())

可以看到运行结果报错,这里我们设置的时间是0.1s也就是说0.1s内服务器没有响应,于是抛出了URLError异常。因此可以通过设置这个超时时间,实现一个网页长时间没有响应就跳过该网页的抓取。
示例代码
import socket
import urllib.request
import urllib.error
try:
resp=urllib.request.urlopen('https://www.httpbin.org/get',timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason,socket.timeout): #
print("TIME OUT")

运行结果TIME OUT 按照常理来说,0.1s几乎不可能得到服务器响应,因此输出time out的提示
其他参数
除了data参数和timeout参数,urlopen方法还有context参数,该参数必须是ssl.SSLContext类型,用来指定SSL的设置。
此外,cafile和capath这两个参数分别用来指定CA证书和其路径,这两个在请求HTTPS链接时会有用。
cadefault参数现在已经弃用了,其默认值时false
至此,我们讲解了urlopen方法的用法,通过了这个最基本的方法,就可以对简单的网页进行请求和抓取了
Request
利用urlopen方法可以发起最基本的请求,但是他的几个参数不足以构建一个完整的请求,如果需要往参数中添加headers等请求,就得使用更强大的Request类来构建请求了。
首先,上示例代码
import urllib.request
req = urllib.request.Request('https://python.org')
resp=urllib.request.urlopen(req)
print(resp.read().decode('utf-8'))
可以发现的时,我们使用的依旧时urlopen方法来发送请求,只不过,这次该方法请求的参数不在是url,而是一个Request类型的对象,通过这个数据结构,一方面可以将请求独立成一个对象,另一方面可以更加丰富和灵活的配置参数。
下面我们看一下通过怎样的参数来构造Request类,构造方法如下
class urlliib.request.Request(url,data=None,
headers={},origin_req_host=None,unverifiable=Falas,method=None)
第一个参数是url,即我们要请求的网页网址,这个是必传参数。
第二个参数是data参数,如果要传递数据,必须传bytes类型的,如果数据是字典,可以先用urllib.parse模块里的urlencode方法进行编码。
第三个参数header是一个字典参数,这就是一个请求头,我们在构造请求时既可以透过headers参数直接构造此项,也可以通过调用请求实例的add_header方法添加。
添加请求头最常见的方法就是通过修改User-Agent来伪装浏览器,默认的User-Agent时python-urlilb 如下图所示

如果要伪装成浏览器,我们可以在对应的浏览器中找到User-Agent,然后再headers里重新设置User-Agent

实例代码
from urllib import request,parse
url ='https://www.httpbin.org/post'
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0",
'Host':'www.httpbin.org'
}
dict={'name':"germey"}
data = bytes(parse.urlencode(dict),encoding='utf-8')
req=request.Request(url=url,data=data,headers=headers,method="POST")
resp= request.urlopen(req)
print(resp.read().decode('utf-8'))
输出
这里我们通过四个参数构造了一个Request类,其中的url即请求的URL,headers中指定了UA和Host,data用了parsel里的urlencode方法和bytes的方法吧字典数据转成字节流格式。另外,指定请求方法为post(注意,这里的url后缀名一点要是/post,不然会报405错误)
观察结果我们可以发现,我们成功设置了data,headers和method。
通过add_header方法添加headers方法,方法如下
req=request.Request(url=url,data=data,method='POST')
req.add_header("User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0")
有了Request类,我们就可以更加方便的构建请求了,并实现请求的发送了
高级用法
我们以及可以构建请求了,那么对于更高级的操作(例如Cookie处理,代理设置等),又该怎么实现呢。
此时我们需要更加强大的工具——Handler。简而言之,Handler可以理解为各种处理器,有专门处理登录验证的,处理Cookie的,处理代理设置的,利用这些Handler,我们几乎可以实现HTTP请求中所有的功能。
首先介绍一下urllib.request模块里的BaseHandler类,这是其他所有Handler类的父类,他提供了最基本的方法,例如default_open,protocol_request等。
接下来举几个子类的例子。
HTTPDefaultErrorHandler :用于处理HTTP响应错误,所有错误都会抛出HTTPError类型的异常。
HTTPRedirectHandler:用于处理重定向。
HTTPCookieProcessor:用于处理cookie
ProxyHandler:用于设置代理,代理默认为空。
HTTPPasswordMgr:用于管理密码。它维护者用户名密码的对照表。
HTTPBasicAuthHandler:用于管理认证,如果一个链接在打开时需要认证,那么可以用这个类来解决问题。
还有一个OpenerDirector我们可以称之为Opener,我们之前用过urlopen方法,实际上就是urllib库为我们提供的一个Opener。
为什么要引入一个Opener呢,因为需要实现更高级的功能,,之前使用Request类和urlopen类相当于类库已经封装好的极其常用的请求方法,利用这两个类可以完成基本的请求,但是现在我们需要实现更高级的功能,就需要深入一层进行配置,使用更底层的实例来完成操作,所有这里就用到了Opener。
Opener类可以通过open方法,该方法返回的响应类型和urlopen方法如出一辙,那么Opener类和Handler类有什么关系呢,简而言之就是,利用Handler类来构建Opener类。
下面用几个实例来看看Handler类和Opener类的用法。
验证 示例网站 https:ssr3.scrape.center

遇到这种情况,就表示这个网站启用了基本身份认证,这是一种登录验证方式 ,允许网页或其他客户端程序在请求网站时提供用户名和口令形式的身份凭证。
那么,爬虫如何请求这样的页面呢?借助HTTPBasicAuthHandler模块就可以完成
示例代码
from urllib.request import HTTPBasicAuthHandler,HTTPPasswordMgrWithDefaultRealm,build_opener
from urllib.error import URLError
username = 'admin'
password = 'admin'
url = "https://ssr3.scrape.center"
p= HTTPPasswordMgrWithDefaultRealm()
p.add_password(None,url,username,password)
auth_handler=HTTPBasicAuthHandler(p)
opener = build_opener(auth_handler)
try:
result = opener.open(url)
html = result.read().decode('utf-8')
print(result,html)
except URLError as e:
print(e.reason)
这里首先实例化一个HTTPBasicAuthHandler对象auth_handler,其参数就是HTTPPasswordMgrWithDefaultRealm对象,它利用了add_password方法添加了用户名和密码。这样就建立了一个用来处理器验证的Handler类
然后将刚建立的auth_handler类当作参数传入build_opener方法,构造一个Opener,这个Opener在发送请求时就相当于已经验证成功了。最后用Opener类中的方法打开链接,即可完成验证。这里获取的结果就是验证成功后的源码内容。
本次的urllib基本使用先说到这里,明天我们继续学习urllib中代理的使用,cookie处理,异常处理,以及urlparse和urlnoparse等方法的使用
899

被折叠的 条评论
为什么被折叠?



