首页 > 技术文章 > c、获取网页

simpleness 2021-03-31 07:44 原文

获取网页

1、urllib库

在 Python2 中,有 urllib 和 urllib2 两个库来实现请求的发送。 而在 Python3中,已经不存在urllib2这个库了 , 统一为urllib,其官方文档链接为: https://docs.python.org/zh-cn/3/library/urllib.html

urllib包含4个模块:

request : 它是最基本的 HTTP 请求模块,可以用来模拟发送请求。 它还带有处理授权验证( authenticaton )、重定向( redirection)、浏览器 Cookies 以及其他内容。

error: 异常处理模块,如果出现请求错误, 我们可以捕获这些异常,然后进行重试或其他操作以保证程序不会意外终止。

parse: 一个工具模块,提供了许多 URL 处理方法,比如拆分、解析、 合并等。

robotparser:主要是用来识别网站的 robots.txt 文件,然后判断哪些网站可以爬,哪些网站不可以爬,它其实用得比较少。

1.1 发送请求

1.1.1、urllib.request.urlopen()

以实现最基本请求的发起

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

参数:

url:打开统一资源定位地址 url,可以是一个字符串或一个 Request 对象。

data:data 参数是可选的。如果要添加该参数,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过 bytes()方法转化。另外,如果传递了这个参数,则它的请求方式就不再是 GET方式,而是POST方式。

timeout:timeout 参数用于设置超时时间,单位为秒,意

思就是如果请求超出了设置的这个时间,还没有得到响应, 就会抛出异常。 如果不指定该参数,就会使用全局默认时间。它支持 HTTP、HTTPS 、FTP 请求。。 程序时间后,服务器依然没有响应,于是抛出了 URLError 异常。 异常属于 urllib. error 模块,错误原因是超时。

cafile、capath : 可选的cafilecapath参数为HTTPS请求指定一组受信任的CA证书。 cafile应该指向包含一堆 CA证书的单个文件,而capath应该指向哈希证书文件的目录。可以在中找到更多信息ssl.SSLContext.load_verify_locations()

cadefaultcadefault参数被忽略

context: context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL设置。

返回值:

返回HTTPResposne 类型的对象,主要包含 read() 、 readinto()、 getheader(name)、 getheaders() 、 fileno()等方法,以及 msg、 version、 status 、 reason 、 debuglevel、 closed 等属性。

read():得到返回的网页内容

status:得到返回结果的状态码

geturl() :返回检索到的资源的URL,通常用于确定是否遵循了重定向

info() :以email.message_from_string()实例的形式返回页面的元信息

getcode() :返回响应的HTTP状态代码

例如:
from urllib.request import urlopen

# 发起网络请求
resp = urllopen('http://www.hao123.com')
assert resp.code == 200
print('请求成功')
# 保存请求的网页
# f 变量接收open()函数返回的对象的__enter__()返回结果
with open('a.html', 'wb') as f:
     f.write(resp.read())

urlopen(url, data=None)可以直接发起url的请求, 如果data不为空时,则默认是POST请求,反之为GET请求。

resp是http.client.HTTPResponse类对象。

1.1.2、urllib.request.Request类

在基本请求中加入 Headers 等信息

构造方法:
class urllib.request.Request(ur1, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None) 

url: 用于请求URL, 这是必传参数,其他都是可选参数。
data: 如果要传,必须传 bytes (字节流)类型的。 如果它是字典,可以先用 urllib.parse 模块里的urlencode()编码。
headers:是一个字典,它就是请求头,我们可以在构造请求时通过 headers 参数直接构造,也可以通过调用请求实例的 add_header()方法添加。 添加请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-Agent 是Python-urllib ,我们可以通过修改它来伪装浏览器。 比如要伪装火狐浏览器,你可以把它设置为:Mozilla/s.o (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11
origin_req_host:指的是请求方的 host名称或者 IP 地址。

unverifiable:表示这个请求是否是无法验证的,默认是 False,意思就是说用户没 有足够权限来选择接收这个请求的结果。 例如,我们请求一个 HTML 文档中的图片,但是我 们没有向动抓取图像的权限,这时 unverifiable 的值就是 True。
method:是一个字符串 ,用来指示请求使用的方法,比如 GET、 POST 和 PUT 等。

属性:

Request.full_url:设置、获取和删除构造函数的URL

Request.type:请求类型

Request.host:URI授权,通常是主机,但也可以包含由冒号分隔的端口

Request.origin_req_host: 请求的原始主机,不含端口。

Request.selector:URI 路径。若 Request 使用代理,选择器将会是传给代理的完整 URL。

Request.data:请求的实体,或者None未指定。

Request.unverifiable:布尔,表明请求是否为 RFC 2965 中定义的无法证实的。

Request.method:要使用的HTTP请求方法。默认情况下,其值为None,这意味着get_method()将对要使用的方法进行常规计算。可以通过get_method()在Request子类的类级别设置默认值来提供默认值,或通过method 参数将值传递给Request构造函数,从而设置其值(从而覆盖中的默认计算)。

Request.get_method():返回指示HTTP请求方法的字符串。如果 Request.method不是None,返回其值,否则返回 'GET'如果Request.data是None,或者'POST'如果它不是。这仅对HTTP请求有意义。

Request.add_header(keyval: 向请求添加另一个标头。目前,所有处理程序都会忽略标头,但HTTP处理程序除外,HTTP处理程序会将标头添加到发送到服务器的标头列表中。请注意,具有相同名称的头不能超过一个,并且如果密钥冲突,以后的调用将覆盖以前的调用。当前,这不会丢失HTTP功能,因为所有具有多次使用意义的标头都具有一种(标头专用)方式,仅使用一个标头即可获得相同的功能。

Request.add_unredirected_header(keyheader: 添加一个不会被加入重定向请求的头部。

Request.has_header(标题): 返回实例是否具有命名头(检查常规和未重定向)。

Request.remove_header(标题): 从请求实例中删除命名标头(从常规标头和未重定向标头中都删除)。

Request.get_full_url() : 返回构造器中给定的 URL。

Request.set_proxy(host,type ): 通过连接到代理服务器来准备请求。主机和类型将替换实例的主机和类型,实例的选择器将是构造函数中提供的原始URL。

Request.get_header(header_name,默认= None ) : 返回给定标头的值。如果标题不存在,则返回默认值。

Request.header_items() : 返回请求标头的元组列表(header_name,header_value)。

例如:
from urllib.request import Request

def search_baidu():
    # 网络资源的接口(URL)
    url = 'https://www.baidu.com'
    # 生成请求对象,封装请求的url和头header
    request = Request(url,
                      headers={
                          'Cookie': 'BIDUPSID=16CECBB89822E3A2F26ECB8FC695AFE0; PSTM=1572182457; BAIDUID=16CECBB89822E3A2C554637A8C5F6E91:FG=1; BD_UPN=123253; H_PS_PSSID=1435_21084_30211_30283; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_645EC=6f7aTIObS%2BijtMmWgFQxMF6H%2FhK%2FcpddiytCBDrefRYyFX%2B%2BTpyRMZInx3E',
                          'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
                      })
    response = urlopen(request)  # 发起请求
    assert response.code == 200
    print('请求成功')
    # 读取响应的数据
    bytes_ = response.read()
    # 将响应的数据写入文件中
    with open('index.html', 'wb') as file:
        file.write(bytes_)

1.1.3、高级用法

urllib. request.BaseHandler 类,它是所有其他 Handler的父类,它提供了最基本的方法。各种Handler子类继承这个 BaseHandler 类

BaseHandler.add_parent(director): 添加一个director作为父辈

BaseHandler.close():删除任何父辈

HITPDefaultErrorHandler:用于处理HTTP 响应错误,错误都会抛出 HTTPError类型的异常。
HTTPRedirectHandler:用于处理重定向。
HTTPCookieProcessor: 用于处理 Cookies。
ProxyHandler:用于设置代理, 默认代理为空。
HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表。
HTTPBasicAuthHandler : 用于管理认证,如果一个链接打开时需要认证,那么可以用它来解 决认证问题。

OpenerDirector,我们可以称为 Opener。就是利用 Handler 来构建 Opener

1.2、 处理异常

urllib 的 error 模块定义了由 request 模块产生的异常。 如果出现了问题, request 模块便会抛州 error 模块中定义的异常。

1.2.1、 URLError

URL Error 类来自 urllib 库的 error 模块,它继承自 OSError 类,是 error 异常模块的基类,由 request 模块生的异常都可以通过捕获这个类来处理。

reason属性:返回错误的原因。

1.2.2、 HTTPError

它是 URLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。 它有如下 3 个属性。

code: 返回 HTTP 状态码,比如 404 表示网页不存在, 500 表示服务器内部错误等。

reason:同父类一样,用于返回错误的原因。

headers: 返回请求头。

1.3、解析链接

urllib 库里还提供了 parse 模块,它定义了处理 URL 的标准接口,例如实现 URL 各部 分的抽取、合并以及链接转换。 它支持如下协议的 URL 处理:file、ftp、 gopher、 hdl、 http、 https、 imap、 mailto 、 mms 、 news、 nntp 、 prospero 、 rsync、 rtsp、 rtspu、 sftp 、 sip、 sips 、 snews 、 svn 、 svn+ssh 、 telnet 和 wais。

1.3.1、urlparse()

该方法返回结果是一个 ParseResult 类型的对象,它包含 6个部分,分别是 scheme、 netloc、 path、 params 、 query 和 fragment。

urlparse()方法将URL拆分成了 6个部分。 解析时有特定的分隔符。 比如,://前面的就是 scheme,代表协议;第一个/符号前面便是 netloc ,即域名,后面是 path,即访 问路径;分号;前面是 params ,代表参数;问号?后面是查询条件 query, 一般用作 GET 类型的 URL; 井号#后面是锚点,用于直接定位页面内部的下拉位置。

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

urlstring:必填项,即待解析的URL

scheme:是默认的协议,假如这个链接没有带协议信息,会将这个作为默认的协议

allow_fragments:即是否忽略fragment。 如果它被设置为 False,fragment部分就会被忽略, 它会被解析为 path、 parameters 或者 query 的一部分,而 fragment 部分为空。

1.3.2、urlunparse()

有了 urlparse(), 相应地就有了它的对立方法 urlunparse()。 它接受的参数是一个可迭代对象, 但是它的长度必须是 6, 否则会抛出参数数量不足或者过多的问题。

1.3.3、 urlsplit()

这个方法和 urlparse()方法非常相似, 只不过它不再单独解析 params 这一部分,只运回 5 个结果。 上面例子中的 params 会合并到 path 中。

1.3.4、urlunsplit()

与 urlunparse()类似,它也是将链接各个部分组合成完整链接的方法,传人的参数也是一个可迭代对象,例如列表、 元组等,唯一的区别是长度必须为 5。

1.3.5、urljoin()

有了 urlunparse()和 urlunsplit()方法,我们可以完成链接的合井,不过前提必须要有特定长度 的对象,链接的每一部分都要清晰分开。

此外,生成链接还有另一个方法,那就是 urljoin()方法。 我们可以提供一个 base_url (基础链接 ) 作为第一个参数,将新的链接作为第二个参数,该方法会分析 base_url 的 scheme、 netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果。

1.3.6、urlencode()

这个方法非常常用。 有时为了更加方便地构造参数,我们会事先用字典来表示。 要转化为 URL 的参数时,只需要调用该方法即可。

1.3.7、parse_qs()

有了序列化,必然就有反序列化。 如果我们有一串 GET 请求参数,利用 parse_qs()方法, 就可以将它转回字典。

1.3.8、parse_qsl()

它用于将参数转化为元组组成的列表

1.3.9、quote()

该方法可以将内容转化为 URL 编码的格式。 URL 中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将中文字符转化为 URL 编码。

1.3.10、 unquote()

有了 quote()方法,当然还有 unquote()方法,它可以进行 URL解码

1.4、分析Robots协议

利用 urllib 的 robotparser 模块,我们可以实现网站 Robots 协议的分析。

1.4.1、Robots协议

Robots 协议也称作爬虫协议、机器人协议,它的全名叫作网络爬虫排除标准( Robots Exclusion Protocol ),用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。 它通常是一个叫作 robots.txt 的文本文件,一般放在网站的根目录下。

当搜索爬虫访问一个站点时,它首先会检查这个站点根目录下是否存在 robots.txt 文件,如果存在, 搜索爬虫会根据其中定义的爬取范围来爬取。 如果没有找到这个文件,搜索爬虫便会访问所有可直接访问的页面。

User-agent 描述了搜索’爬虫的名称,这里将其设置为*则代表该协议对任何爬取爬虫有效。

Allow 一般和 Disallow 一起使用,一般不会单独使用,用来排除某些限制。 现在我们设置为 /public/,则表示所有页面不允许抓取,但可以抓取 public 目录。

1.4.2、爬虫名称

一些常见的搜索爬虫的名称及对应的网站。比如百度的就叫作 BaiduSpider。

1.4.3、robotparser

了解 Robots 协议之后,我们就可以使用 robotparser 模块来解析 robots.txt 了。 该模块提供了一个 类RobotFileParser,它可以根据某网站的 robots.txt 文件来判断一个爬取爬虫是否有权限来爬取这个网页。 该类用起来非常简单,只需要在构造方法里传人 robots.txt 的链接即可。

首先看一下它的声明:

urllib.robotparser.RobotFileParser(url=’’)

当然,也可以在声明时不传人,默认为空,最后再使用 set_url()方法设置一下也可。

下面列出了这个类常用的几个方法。

set_url(): 用来设置 robots.txt 文件的链接。 如果在创建 RobotFileParser 对象时传入了链接,那么就不需要再使用这个方法设置了。

read(): 读取 robots.txt 文件并进行分析。注意,这个方法执行一个读取和分析操作,如果不 调用这个方法, 接下来的判断都会为 False,所以一定记得调用这个方法。 这个方法不会返 回任何内容,但是执行了读取操作。

parse(): 用来解析robots.txt文件,传人的参数是 robots.txt某些行的内容,它会按照 robots.txt 的语法规则来分析这些内容。

can_fetch(): 该方法传人两个参数, 第一个是 User-agent,第二个是要抓取的 URL。 返回的 内容是该搜索引擎是否可以抓取这个 URL,返回结果是 True 或 False。

mtime(): 返回的是上次抓取和分析 robots.txt 的时间,这对于长时间分析和抓取的搜索爬虫是 很有必要的,你可能需要定期检查来抓取最新的 robots.txt。

modified() :它同样对长时间分析和抓取的搜索爬虫很有帮助,将当前时间设置为上次抓取 和分析 robots.txt 的时间。

例如:
"""
复杂的GET请求,多页面请求下载
"""
from urllib.request import Request, urlopen
from urllib.parse import urlencode

import ssl

import time

ssl._create_default_https_context = ssl._create_unverified_context

url = 'https://www.baidu.com/s?'

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
    'Cookie': 'BIDUPSID=16CECBB89822E3A2F26ECB8FC695AFE0; PSTM=1572182457; BAIDUID=16CECBB89822E3A2C554637A8C5F6E91:FG=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1573184257; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; H_PS_PSSID=1435_21084_30211_30283; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; PSINO=1; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; APPGUIDE_8_2_2=1; yjs_js_security_passport=0927713bf2c240ca607108086d07729426db4dbb_1577084843_js; __yjsv5_shitong=1.0_7_c3620451e4363f4aed30cbe954abf8942810_300_1577084847314_223.255.14.197_2d7151e0; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D',
    'x-requested-with': 'XMLHttpRequest'
}

params = {
    'wd': '',
    'pn': 0  # 0, 10, 20, 30 ...  = (n-1)*10
}

def pages_get(wd):
    params['wd'] = wd
    for page in range(1, 101):
        params['pn'] = (page-1)*10

        page_url = url+urlencode(params)
        resp = urlopen(Request(page_url,
                               headers=headers))
    
        assert resp.code == 200
        file_name = 'baidu_pages/%s-%s.html' % (wd, page)
        with open(file_name, 'wb') as f:
            bytes_ = resp.read()
            f.write(bytes_)
            print(f'{file_name} 写入成功!')
            time.sleep(0.5)
    
    print('下载 %s 100页成功!' % wd)

if __name__ == '__main__':
    pages_get('Python3.6')

2、使用requests

2.1、基本用法

image-20200825194822879

2.1.1、Requests.request()

Requests.request(method,url,**kwargs)
  • method: 请求方式,对应get/post/put等7种方式

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)

  • **kwargs:控制访问参数,均为可选项,共13个

    • Params:字典或字节序列,作为参数增加到url中

    • Data:字典、字节序列或文件对象,作为requests的内容

    • Json:json格式的数据,作为request的内容

    • Header:字典,HTTP定制头

    • Cookies: 字典或cookiejar,request中的cookie

    • Auth:元组,支持HTTP认证功能

    • Files:字典类型,传输文件结构,如果是tuple, 则有三种情况:

      • ('filename', file-like-object)
      • ('filename', file-like-object, content_type)
      • ('filename', file-like-object, content_type, custom-headers)
    • Timeout:设置以s为单位的超时时间

    • Proxies:字典类型,设定访问代理服务器,可增加登录认证(可防逆追踪)

    • Allow_redirects:默认为true,重定向开关

    • Stream:默认为true,获取内容立即下载开关

    • Verify:默认为true,认证ssl证书开关

    • Cert:本地ssl证书路径

2.1.2、Requests.get()

Requests.get(url,**kwargs)                

requests.get() 发起GET请求, 查询数据

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,除Proxies其他12个控制访问参数

2.1.3、Requests.head()

Requests.get(url,**kwargs)                

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,共13个

2.1.4、Requests.post()

Requests.post(url,**kwargs)                

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,除data、json其他11个

2.1.5、Requests.put()

Requests.put(url,**kwargs)                

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,除data其他12个控制访问参数

2.1.6、Requests.patch()

Requests.patch(url,**kwargs)                

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,共13个

2.1.7、Requests.delete()

Requests.delete(url,**kwargs)                

可用参数:

  • url: str 请求的资源接口(API),在RESTful规范中即是URI(统一资源标签识符)
  • **kwargs:控制访问参数,均为可选项,共13个

2.1.8、响应

r.text: HTTP响应内容的字符串形式,即url对应的页面内容

r.content: HTTP响应内容的二进制形式

r.encoding: 从HTTP header中猜测的响应内容编码方式

r.apparent_encoding: 从内容中分析出的响应内容编码方式(备用编码格式)

r.headers: 返回响应头

r.cookies: 返回响应的Cookies

r.url: 返回响应的url属性

r.history: 返回请求的历史

r.status_code: HTTP请求的返回状态,200表示连接成功,404表示失败

2.2、异常

image-20200825220740552

2.3、requests.Respose

以上的请求方法返回的对象类型是Response, 对象常用的属性如下:

  • status_code 响应状态码
  • url 请求的url
  • headers : dict 响应的头, 相对于urllib的响应对象的getheaders(),但不包含cookie。
  • cookies: 可迭代的对象,元素是Cookie类对象(name, value, path)
  • text : 响应的文本信息
  • content: 响应的字节数据
  • encoding: 响应数据的编码字符集, 如utf-8, gbk, gb2312
  • json(): 如果响应数据类型为application/json,则将响应的数据进行反序化成python的list或dict对象。
    • 扩展-javascript的序列化和反序列化
      • JSON.stringify(obj) 序列化
      • JSON.parse(text) 反序列化

推荐阅读