目录
前言
本篇博客用于记录学习到的Python网络爬虫相关知识点。
三、请求模块
3.1 urllib 模块
3.1.1 urllib 简介
urllib库,Python内置的HTTP请求库,官方文档链接在这里。它包含如下4个模块:
- request:最基本的HTTP请求模块,用来模拟浏览器发送请求,还带有处理授权验证(authentication)、重定向(redirection)、浏览器 Cookies 以及其他内容。
- error:异常处理模块,该模块定义了由 request 模块产生的异常。如果出现了请求错误,request 模块便会抛出 error 模块中定义的异常。
- parse:解析工具模块,包含许多URL处理方法,比如拆分、解析、合并等。
- robotparser:主要用于解析网站的robots.txt文件,判断网站是否可以爬取信息(该模块使用较少)。
3.1.2 urllib.request 请求模块
简介: 最基本的HTTP请求模块,用来模拟浏览器发送请求,还带有处理授权验证(authentication)、重定向(redirection)、浏览器 Cookies 以及其他内容。
1. urlopen()方法
- 格式:
urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None)
- 作用: 实现最基本的 HTTP 请求,并接收服务端所返回的响应数据。
- 返回类型: http.client.HTTPResponse 对象。
- 主要方法:
read()、readinto()、getheader(name)、getheaders()、fileno()
等- read(): 得到返回的网页内容(bytes类型),使用
.decode("utf-8)
方法解码成可读形式,解码方式为unicode_escape
可正常显示中文。。 - getheaders():得到返回的所有响应头信息
- getheader(name):得到响应头信息中指定的参数值,如果属性不存在,返回None。
- read(): 得到返回的网页内容(bytes类型),使用
- 主要属性:
msg、version、status、reason、debuglevel、closed
等- status:得到返回的响应状态码
- 参数:
- url:需要访问网站的URL完整地址。
- data:通过该参数确认请求方式。默认值为None,表示请求方式为GET,否则为POST。在发送 POST 请求时,需要将字典类型的参数值转换为字节bytes类型的数据,传递给参数 data。
- timeout:设置请求超时时间,以秒为单位,默认不超时。如果请求超出了设置的时间,还没有得到响应,就会抛出异常。
- cafile、capath:指定一组 HTTPS 请求受信任的 CA (Certificate Authority)证书单个文件和文件所在目录。这两个参数在请求 HTTPS 链接时会有用。
- cadefault:(参数已经弃用)CA 证书默认值,默认值为 False。
- context:ssl.SSLContext 类型,用来指定 SSL 设置。(SSL,Secure Scoket Layer,安全套接层,在传输通信协议(TCP/IP)上实现的一种安全协议。)
- 编程举例:
import socket import urllib.parse import urllib.error import urllib.request try: data = bytes(urllib.parse.urlencode({'hello': 'world'}), encoding='utf-8') response = urllib.request.urlopen("http://httpbin.org/post", data=data, timeout=0.1) print(response.read().decode('utf-8')) except urllib.error.URLError as e: if isinstance(e.reason, socket.timeout): print("TIME OUT")
- bytes():第一个参数是需要转换的字符串,第二个参数指定编码格式。
- httpbin.org:提供HTTP请求测试的网站。/post测试 POST 请求,/get测试 GET 请求。
- socket.timeout 类型:超时异常
2. Request()方法
- 格式:
urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- 作用: 构建一个含有多种功能(如 Headers、Cookies、代理IP等)的请求对象。
- 参数:
- url:需要访问网站的URL完整地址。
- data:通过该参数确认请求方式。默认值为None,表示请求方式为GET,否则为POST。在发送 POST 请求时,需要将字典类型的参数值转换为字节bytes类型的数据,传递给参数 data。
- headers:字典类型参数,设置请求头部信息(也可以通过调用请求实例的 add_header()方法设置)。添加请求头信息最常见的用法就是修改 User-Agent 来伪装成浏览器,默认的 User-Agent 是 Python-urllib。
- origin_req_host:设置请求方的 host 名称或者 IP 地址。
- unverifiable:请求是否无法验证,即用户是否缺少足够权限来接收这个请求结果,默认为False。
- method:设置请求方式,比如 GET、POST 和 PUT 等,默认 GET。
- 关于请求头: 在浏览器中寻找有效的请求头信息步骤如下:
- F12开发者工具
- 点击“网络”
- 任意打开一个网页
- 在请求列表里选中一项请求信息
- 在“消息头”中找到请求头信息
- 编程举例:
import urllib.parse import urllib.request url = "http://httpbin.org/post" data = bytes(urllib.parse.urlencode({'hello': 'world'}), encoding='utf-8') # 示例1:伪装成谷歌浏览器 headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'} # 示例2:伪装成火狐浏览器 headers = {'User-Agent':'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11'} request = urllib.request.Request(url=url, data=data, headers=headers, method='POST') response = urllib.request.urlopen(request) print(response.read().decode('utf-8')) # 使用 add_header()方法添加请求头 request = urllib.request.Request(url=url, data=data, method='POST') request.add_header('User-Agent', 'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11')
3. Handler 处理器
- 作用: 利用 Handler 处理器,几乎可以做到 HTTP 请求中所有的事情。
- 分类: 所有的 Handler 子类均继承自 urllib.request.BaseHandler 类,常见的 Handler 处理器如下:
- HTTPDefaultErrorHandler:用于处理 HTTP 响应错误,错误都会抛出 HTTPError 类型的异常。
- HTTPRedirectHandler:用于处理重定向。
- HTTPCookieProcessor:用于处理 Cookies。
- ProxyHandler:用于设置代理,默认代理为空。
- HTTPPasswordMgr:用于管理密码,维护用户名和密码的表。
- HTTPBasicAuthHandler:用于管理认证,解决链接打开时需要认证的问题。
- Opener: Handler 处理器常与 OpenerDirector (简称 Opener) 类搭配使用(利用 Handler 来构建 Opener),从而实现更高级的功能。Opener 可以使用 open() 方法,返回的类型与 urlopen() 一致。
(1) Cookie
- 定义: Cookie 指某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。通俗讲,Cookie 是服务器向客户端返回响应数据时所留下的标记,当客户端再次访问服务器时将携带这个标记。
- 模拟登录: 获取登录验证的请求地址,并通过 POST 请求的方式将表单数据发送至该地址。
- 火狐浏览器打开至登录界面,开发者工具 -> 网络 -> “设置”按钮 -> 持续记录
- 输入用户名和密码,点击“登录”,在开发者工具的网络请求列表点击“chklogin.html”的网络请求文件,获取登录验证的请求地址,点击“请求”选项获取所需表单数据。
- 模拟登录示例:
import urllib.parse import urllib.request url = url data = bytes(urllib.parse.urlencode({'username':'admin', 'password':'123456'}), encoding='utf-8') headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'} request = urllib.request.Request(url=url, data=data, headers=headers, method='POST') response = urllib.request.urlopen(request) print(response.read().decode('utf-8'))
- 获取Cookie: 获取登录成功所生成的 Cookie 信息。
- 创建 CookieJar 对象
- 利用 HTTPCookieProcessor 来构建一个 Handler
- 利用 build_opener() 方法构建出 Opener
- 发送登录请求
import urllib.parse import urllib.request import http.cookiejar url = url data = bytes(urllib.parse.urlencode({'username':'admin', 'password':'123456'}), encoding='utf-8') cookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open(url, data=data) for item in cookie: # 注意这里是cookie print(item.name+"="+item.value)
- 保存/读取 Cookie 文件: 将 Cookie 信息保存成指定的文件形式,下次登录请求时直接读取文件中的 Cookie 信息。
# 保存成文件格式 filename = 'cookies.txt' # 将 Cookies 保存成 Mozilla型浏览器的 Cookies 格式 cookie = http.cookiejar.MozillaCookieJar(filename) # 将 Cookies 保存成 libwww-perl(LWP)格式 cookie = http.cookiejar.MozillaCookieJar(filename) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open(url, data=data) cookie.save(ignore_discard=True, ignore_expires=True) # ignore_discard=True 即使 Cookie 将被丢弃,也将它保存下来。 # ignore_expires=True 如果在该文件中 Cookie 已经存在,则覆盖原文件写入。 # 从文件中读取 Cookies文件 cookie = http.cookiejar.LWPCookieJar() cookie.load('cookies.txt', ignore_discard=True, ignore_expires=True) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open(url) # 不需要data了,已经有Cookie了 print(response.read().decode('utf-8'))
(2) 设置代理IP
- 作用: 对网络请求设置代理IP,可以避免在短时间内同一IP访问服务器的大量数据时,客户端IP被禁用,从而解决该情况下的反爬虫技术。
- 步骤:
- 创建 ProxyHandler 对象:参数为字典类型的代理 IP,键为协议类型(如HTTP、HTTPS等),键为代理链接,可以添加多个代理。
- 利用 build_opener() 方法构建出 Opener
- 发送登录请求
import urllib.request proxy_handler = urllib.request.ProxyHandler({ 'https': '58.220.95.114:10053', 'http': 'http://127.0.0.1:9743' }) opener = urllib.request.build_opener(proxy_handler) response = opener.open(url, timeout=2) print(response.read().decode('utf-8'))
(3) 验证
- 使用场景: 网站在打开时弹出提示框,要求输入用户名和密码,验证成功后才能查看页面。
- 步骤 :
- 创建 HTTPBasicAuthHandler 对象:参数为 HTTPPasswordMgrWithDefaultRealm 对象,利用 add_password() 方法添加用户名和密码。
- 利用 build_opener() 方法构建出 Opener
- 发送登录请求
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener username = 'admin' password = '123456' p = HTTPPasswordMgrWithDefaultRealm() p.add_password(None, url, username, password) handler = HTTPBasicAuthHandler(p) opener = build_opener(handler) response = opener.open(url) print(response.read().decode('utf-8'))
3.1.3 urllib.error 异常模块
简介: 异常处理模块,该模块定义了由 request 模块产生的异常。如果出现了请求错误,request 模块便会抛出 error 模块中定义的异常。
1. URLError 类
- 含义: urllib.error 异常模块的基类,继承自 OSError 类,由 urllib.request 模块产生的异常都可以使用该类进行捕获。
- 主要属性:
- reason:返回错误的原因
- 编程举例:
from urllib import request, error try: response = request.urlopen(url) except error.URLError as e: print(e.reason)
2. HTTPError 类
- 含义: URLError 的子类,专门用来处理 HTTP 请求错误,比如认证请求失败等。
- 主要属性:
- code:返回 HTTP 状态码
- reason:返回错误的原因
- headers:返回请求头
- 编程举例:
from urllib import request, error try: response = request.urlopen(url) except error.HTTPError as e: print(e.reason, e.code, e.headers)
3. 综合举例
- 使用技巧: 可以先捕获子类的错误,再捕获父类的错误,最后用 else 来处理正常的逻辑。
- 关于 reason 属性: reason 属性返回的不一定是字符串,也可能是一个对象,如 socket.timeout 类
- 编程举例:
from urllib import request, error try: response = request.urlopen(url) except error.HTTPError as e: print(e.reason, e.code, e.headers) except error.URLError as e: print(e.reason) else: print("Request Successfully!")
3.1.4 urllib.parse 解析模块
- 简介: 解析工具模块,包含许多URL处理方法,比如拆分、解析、合并等。
- 标准的链接格式:
- 格式:
scheme://netloc/path;params?query#fragment
- 解释:
://
前面的就是 scheme,代表协议;第一个/
符号前面是 netloc,即域名,后面是 path,即访问路径;分号;
后面是 params,代表参数;问号?
后面是查询条件 query,一般用作 GET 类型的 URL;井号#
后面是锚点,用于直接定位页面内部的下拉位置。
- 格式:
1. urlparse()
- 格式:
urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
- 作用: 识别 URL 并分段。
- 返回类型: urllib.parse.ParseResult 类型对象,包含了 6 部分:scheme、netloc、path、params、query、fragment。
- 参数:
- urlstring: 必填参数,表示待解析的 URL。
- scheme: 可选参数,默认为空字符串,表示需要设置的默认协议。scheme 参数只有在 URL 中不包含 scheme 信息时才生效。
- allow_fragments: 是否忽略 fragment。如果设置为 False,fragment 部分就会被忽略,它会被解析为 path、parameters 或者 query 的一部分,而 fragment 部分为空。
- 方法属性: 返回的 ParseResult 实际上是一个元组,既可以用索引顺序,也可以用属性名来获取对应的属性值。
- 编程示例:
from urllib.parse import urlparse result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='http', allow_fragments=False) print(result) # 使用索引顺序获取属性值 print(result[0],result[1],result[2],result[3],result[4],result[5]) # 使用属性名获取属性值 print(result.scheme,result.netloc,result.path,result.params,result.query,result.fragment)
2. urlsplit()
- 格式:
urllib.parse.urlsplit(urlstring, scheme='', allow_fragments=True)
- 作用: 方法与 urlparse() 类似,但是不再单独解析 params 这部分,将其合并到 path 中。(解析识别 URL 并分段。)
- 返回类型: urllib.parse.SplitResult 类型对象,包含了 5 部分:scheme、netloc、path、query、fragment。
- 参数:
- urlstring: 必填参数,表示待解析的 URL。
- scheme: 可选参数,默认为空字符串,表示需要设置的默认协议。scheme 参数只有在 URL 中不包含 scheme 信息时才生效。
- allow_fragments: 是否忽略 fragment。如果设置为 False,fragment 部分就会被忽略,它会被解析为 path 或者 query 的一部分,而 fragment 部分为空。
- 方法属性: 返回的 SplitResult 实际上是一个元组,既可以用索引顺序,也可以用属性名来获取对应的属性值。
- 编程示例:
from urllib.parse import urlsplit result = urlsplit('www.baidu.com/index.html;user?id=5#comment', scheme='http', allow_fragments=False) print(result) # 使用索引顺序获取属性值 print(result[0],result[1],result[2],result[3],result[4]) # 使用属性名获取属性值 print(result.scheme,result.netloc,result.path,result.query,result.fragment)
3. urlunparse()
- 格式:
urllib.parse.urlunparse(components)
- 作用: 将可迭代对象类型的参数合并成 URL。
- 返回类型: 合并后的URL,str类型。
- 参数:
- components: 长度必须为 6 的可迭代对象,如列表、元组、字典等
- 编程示例:
from urllib.parse import urlunparse # 列表 list_utrl = ['http','www.baidu.com','index.html','','',''] # 元组 tuple_utrl = ('http','www.baidu.com','index.html','','','') # 字典 dict_utrl = {'scheme':'http','netloc':'www.baidu.com','path':'index.html','query':'','params':'','fragment':''} result = urlunparse(list_utrl) result = urlunparse(tuple_utrl) # dict_utrl 等价于 dict_utrl.keys() result = urlunparse(dict_utrl.values()) print(result)
4. urlunsplit()
- 格式:
urllib.parse.urlunsplit(components)
- 作用: 方法与 urlunsplit() 类似,但是不再单独需要 params 这部分,将其放在 path 中。(将可迭代对象类型的参数合并成 URL。)
- 返回类型: 合并后的URL,str类型。
- 参数:
- components: 长度必须为 5 的可迭代对象,如列表、元组、字典等
- 编程示例:
from urllib.parse import urlunparse # 列表 list_utrl = ['http','www.baidu.com','index.html','',''] # 元组 tuple_utrl = ('http','www.baidu.com','index.html','','') # 字典 dict_utrl = {'scheme':'http','netloc':'www.baidu.com','path':'index.html','query':'','fragment':''} result = urlunparse(list_utrl) result = urlunparse(tuple_utrl) # dict_utrl 等价于 dict_utrl.keys() result = urlunparse(dict_utrl.values()) print(result)
5. urljoin()
- 格式:
urllib.parse.urljoin(base, url, allow_fragments=True)
- 作用: 解析基础链接
base
的 scheme、netloc 和 path 三部分内容,并对新链接url
中对应缺失的部分进行补充,最后返回新的 url。 - 参数:
- base:基础链接
- url:新链接
- allow_fragments:基础链接中的 params(如果存在)或者 fragment(如果 params 和 query 都不存在)是否合并到 path 中,默认为Ture,不合并。
- 编程举例:
from urllib.parse import urljoin result = urljoin('www.baidu.com/about.html#hahaha','?category=2#comment',allow_fragments=False) print(result)
6. urlencode() 方法
- 格式:
urllib.parse.urlencode(query)
- 作用: 对请求地址中的参数进行编码,尤其是对于中文参数。
- 参数:
- query:需要进行编码的参数字典。
- 编程举例:
from urllib.parse import urlencode base_url = 'http://httpbin.org/get?' params = {'name':'Chia','country':'中国','age':30} url = base_url + urlencode(params) print(url)
7. quote() 方法
- 格式:
urllib.parse.quote(string)
- 作用: 与 urlencode() 方法类似,对请求地址中的参数进行编码,但是参数必须是字符串类型。
- 参数:
- string:需要进行编码的参数字符串。
- 编程举例:
from urllib.parse import quote base_url = 'http://httpbin.org/get?country=' url = base_url + quote('中国') print(url)
8. unquote() 方法
- 格式:
urllib.parse.unquote(string)
- 作用: 将编码后的 URL 字符串逆向解码。
- 参数:
- string:需要进行逆向编码的 URL 字符串。
- 编程举例:
from urllib.parse import urlencode, unquote base_url = 'http://httpbin.org/get?' params = {'name':Chia,'country':'中国','age':30} url = base_url + urlencode(params) decode_url = unquote(url) print(decode_url)
9. parse_qs() 方法
- 格式:
urllib.parse.parse_qs(string)
- 作用: 获取 URL 中的参数部分后,使用该方法将参数转换成字典类型的数据。
- 返回类型: 字典 dict 类型。
- 参数:
- string:待转换的参数。
- 编程举例:
from urllib.parse import urlsplit, parse_qs url = 'http://httpbin.org/get?name=Chia&age=18' dict_query = parse_qs(urlsplit(url).query) print(dict_query) # 输出结果:{'name':['Chia'], 'age':['30']}
10. parse_qsl() 方法
- 格式:
urllib.parse.parse_qsl(string)
- 作用: 获取 URL 中的参数部分后,使用该方法将参数转换成元组组成的列表数据。
- 返回类型: 列表 list 类型。
- 参数:
- string:待转换的参数。
- 编程举例:
from urllib.parse import urlsplit, parse_qsl url = 'http://httpbin.org/get?name=Chia&age=18' list_query = parse_qsl(urlsplit(url).query) print(list_query) # 输出结果:[('name','Chia'), ('age','30')]
3.1.5 urllib.robotparser 模块
简介: 主要用于解析网站的robots.txt文件,判断网站是否可以爬取信息(该模块使用较少)。
1. Robots 协议
-
定义: Robots 协议(又称爬虫协议、机器人协议),全名叫作网络爬虫排除标准(Robots Exclusion Protocol),用来告诉爬虫和搜索引擎页面的可抓取性。
-
协议介绍:
- 位置: Robots 协议通常是一个名为 robots.txt 的文本文件,放在网站根目录下,和网站的入口文件放在一起。
- 规则: 当搜索爬虫访问站点时,首先会检查该站点根目录下是否存在 robots.txt 文件:如果存在,爬虫会根据其中定义的爬取范围来爬取;否则,爬虫便会访问所有可直接访问的页面。
- 协议内容:
- User-agent:描述了 Robots 协议的有效范围(指定搜索爬虫)
- Disallow: 指定了不允许抓取的目录
- Allow:通常与 Disallow 一起使用,用来排除某些限制
-
协议举例:
# 含义:对所有搜索爬虫只允许爬取 public 目录 User-agent: * Disallow: / Allow: /public/ # 含义:禁止所有搜索爬虫访问任何目录 User-agent: * Disallow: / # 含义:允许所有搜索爬虫访问任何目录,只可以直接把txt文件留空 User-agent: * Disallow: # 含义:禁止所有搜索爬虫访问private目录和tmp目录 User-agent: * Disallow: /private/ Disallow: /tmp/ # 含义:只允许WebCrawler爬虫访问所有目录,禁止其他所有搜索爬虫访问任何目录 User-agent: WebCrawler Disallow: User-agent: * Disallow: /
-
爬虫名称: 常见的搜索爬虫的名称及其对应的网站:
爬虫名称 名 称 网 站 BaiduSpider 百度 www.baidu.com Googlebot 谷歌 www.google.com 360Spider 360搜索 www.so.com YodaoBot 有道 www.youdao.com ia_archiver Alexa www.alexa.cn Scooter altavista www.altavista.com
2. robotparser 模块
- 格式:
urllib.robotparser.RobotFileParser(url)
- 作用: 根据 url 链接下包含的 robots.txt 文件来判断爬虫对网站的爬取权限。
- 参数:
- url:Robots 协议的 url 链接,允许默认置空,之后使用
set_url()
方法设置。
- url:Robots 协议的 url 链接,允许默认置空,之后使用
- 常用方法:
set_url()
:用来设置 Robots 协议的 url 链接。read()
:读取 Robots 协议文件并进行分析。注意,该方法会执行一个读取和分析操作,但不会返回任何内容,不可省略。parse()
:按照 Robots 协议的语法规则来解析文件,入参是 robots.txt 部分行的内容,该方法可以替代上述的 read() 方法。can_fetch()
:入参有两个:(1) User-agent;(2) 要抓取的 URL。返回的布尔型结果代表该搜索引擎是否可以抓取该 URL。mtime()
:返回上次抓取和分析 Robots 协议的时间。modified()
:将当前时间设置为上次抓取和分析 Robots 协议的时间。
- 编程举例:
from urllib.robotparser import RobotFileParser from urllib.request import urlopen # 等价于 rp = RobotFileParse('http://www.jianshu.com/robot.txt') rp = RobotFileParser() rp.set_url('http://www.jianshu.com/robot.txt') rp.read() # 上述两行代码等价于 rp.parse(urlopen('http://www.jianshu.com/robot.txt').read().decode('utf-8').split('\n')) print(rp.can_fetch('*', 'http://www.jianshu.com/p/test'))
3.2 urllib3 模块
3.2.1 urllib3 简介
urllib3 是一个功能强于 urllib 标准库的第三方网络请求模块。可以通过pip install urllib3
命令安装并使用该模块,也可以通过pip install requests
命令安装 requests 模块后,通过代码from requests.packages import urllib3
调用该模块。
3.2.2 urllib3 请求格式
- 格式:
import urllib3 # 创建 PoolManager 连接池管理对象 http = urllib3.PoolManager() # 调用 request() 方法实现网络请求的发送 response = http.request(method,url,fields=None,headers=None,**urlopen_kw)
- 返回类型: urllib3.response.HTTPResponse 对象。可以使用
json.loads()
方法将返回的 JSON 信息(解码后)转换为字典数据。 - 参数:
- method:必选参数,用于指定请求方式,如 GET、POST、PUT 等。
- url:必选参数,用于设置所请求的 url 地址。
- fields:可选参数,用于设置请求参数。
- headers:可选参数,用于设置请求头,字典类型。
- urlopen_kw:urllib 模块中 urlopen() 方法中一些可复用的参数。
- 主要属性:
- status:得到返回的响应状态码。
- data:得到返回的网页内容(bytes类型),使用
.decode("utf-8)
方法解码成可读形式,解码方式为unicode_escape
可正常显示中文。 - retries.total:所设置的请求重试次数。
- 主要方法:
info()
:获取 HTTP 响应头信息,该信息为字典类型的数据。urllib3.disable_warnings()
:忽略有关 SSL 的警告。
3.2.3 urllib3 实现简单请求
1. GET 请求
- 编程示例:
# 向多个服务器发送请求 import urllib3 urllib3.disable_warnings() http = urllib3.PoolManager() response1 = http.request('GET',url1) print(response1.status) response2 = http.request('GET',url2) print(response2.status)
2. POST 请求
- 注意: 需要将 fields 参数设置为字典类型的表单参数。
- 编程示例:
import urllib3 params = {'name':'Chia','country':'中国','age':30} http = urllib3.PoolManager() response = http.request('POST', url, fields=params) # 中文正常显示 print(response.data.decode('unicode_escape'))
3. retries 参数重试请求
- 介绍: urllib3 可以自动重试请求,默认情况下,request() 方法的请求重试次数为 3 。允许设置为 0 或者 False。
- 编程示例:
# 向多个服务器发送请求 import urllib3 http = urllib3.PoolManager() response1 = http.request('GET',url) # 默认 3 次重试请求 print(response1.retries.total) response2 = http.request('GET',url,retries=5) # 设定 5 次重试请求 print(response2.retries.total) response3 = http.request('GET',url,retries=False) # 关闭重试请求 print(response3.retries.total) # 输出为 False,不是0
3.2.4 urllib3 实现复杂请求
1. 设置请求头
- 介绍: 为 headers 参数指定一个有效的字典类型的请求头信息。
- 编程示例:
import urllib3 headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'} http = urllib3.PoolManager() response = http.request('GET',url,headers=headers) print(response.data.decode('utf-8'))
2. 设置超时
- 介绍:
- 超时参数既可以设置在 request() 方法里,也可以设置在 PoolManager 对象中。
- 使用 urllib3.Timeout 实例对象,可以单独设置连接超时与读取超时,格式:
Timeout(connect=0.5, read=0.1)
。
- 编程示例:
import urllib3 from urllib3 import Timeout # 简单设置 timeout = 0.1 # 精确设置 timeout = Timeout(connect=0.5, read=0.1) http1 = urllib3.PoolManager(timeout=timeout) response1 = http1.request('GET',url1) print(response1.status) http2 = urllib3.PoolManager() response2 = http2.request('GET',url2,timeout=timeout) print(response2.status)
3. 设置代理
- 格式:
import urllib3 proxy = urllib3.ProxyManager(proxy_url,headers) response = proxy.request()
- 参数: 为了模拟浏览器请求,避免后台服务器发现,建议填写以下两个参数:
- proxy_url:需要使用的代理 IP,str 类型。
- headers:请求头信息。
3.2.5 urllib3 上传文件
1. fields 参数上传文本文件
- 介绍: 通过 fields 参数对 filefield 键参数以元组形式指定文件名&类型、文件内容。
- 编程示例:
import urllib3, json with open('test.txt') as f: data = f.read() http = urllib3.PoolManager() response = http.request('POST', 'http://httpbin.org/post', fields={'filefield':('example.txt',data)}) files = json.loads(response.data.decode('utf-8'))['files'] print(files)
2. body 参数上传图片文件
- 介绍: 在 request() 方法中指定 body 参数并传入对应图片的二进制数据,同时使用 headers 参数为其指定文件类型。
- 编程示例:
import urllib3 with open('Python.jpg','rb') as f: data = f.read() http = urllib3.PoolManager() response = http.request('POST', 'http://httpbin.org/post', body=data, headers={'Content-Type':'image/jpeg'}) print(response.data.decode())
3.3 requests 模块
3.3.1 requests 简介
相比于 urllib、urllib3 模块,第三方模块 requests 模块在实现 HTTP 请求时所使用的方法更加简单和人性化。
3.3.2 requests 实现简单请求
1. GET 请求
-
格式:
requests.get(url, params=None, **kwargs)
-
返回类型: requests.models.Response 对象
-
主要属性:
- text:得到返回的网页内容(str 类型)
- content:得到返回的 bytes 类型的二进制数据
- status_code:返回的状态码,int 类型
- url:请求 URL
- headers:响应头信息
- cookies:cookie 信息(RequestsCookieJar 类型)
-
主要方法:
- encoding:对响应结果进行编码
- json():如果响应数据是 str 类型的 json 信息,可以使用
json()
方法将其转换成字典格式的数据。(如果不是,便会出现解析错误)
-
参数:
- url:需要访问网站的URL完整地址
- params:GET 请求参数。
-
编程举例:
import requests # (不推荐)也可以在url中通过 “?参数1=值1&参数2=值2” 添加参数 params = {'name':'Chia','age':'28'} response = requests.get(url, params=params) # 不对响应数据进行 utf-8 编码,可能会到导致中文乱码 response.encoding = 'utf-8' print(response.text)
2. POST 请求
-
格式:
requests.post(url, data=None, **kwargs)
-
返回类型: requests.models.Response 对象
-
主要属性:
- text:得到返回的网页内容(str 类型)
- content:得到返回的 bytes 类型的二进制数据
- status_code:返回的状态码,int 类型
- headers:响应头信息
- cookies:cookies 信息(RequestsCookieJar 类型),可使用
.items()
方法遍历每一个 cookie 的名称和值 - url:请求 URL
- history:请求历史
-
主要方法:
- encoding:对响应结果进行编码
- json():如果响应数据是 str 类型的 json 信息,可以使用
json()
方法将其转换成字典格式的数据。(如果不是,便会出现解析错误)
-
参数:
- url:需要访问网站的URL完整地址
- data:POST请求参数,数据格式允许为列表、元组、字典或者JSON
-
编程举例:
import requests, json data = {'user':'Chia','password':'123456'} data = json.dumps({'user':'Chia','password':'123456'}) data = (('user','Chia'),('password','123456')) data = [['user','Chia'],['password','123456']] response = requests.post(url, data=data) print(response.text)
3. 其他信息
- 其他请求: 其他类型的请求依旧可以用一行代码来实现
requests.put()/delete()/head()/options()
- 状态码查询对象: requests 提供了内置的状态码查询对象 requests.code,用来判断请求状态。
- 示例1:状态码 200 包含了查询条件 “ok”,可以用
response.status_code == requests.code.ok
来判断请求是否成功。 - 示例2:状态码 404 包含了查询条件 “not_found”,可以用
response.status_code == requests.code.not_found
来判断结果是否是 404 状态。
- 示例1:状态码 200 包含了查询条件 “ok”,可以用
3.3.3 requests 实现复杂请求
1. 添加请求头 headers
- 方法: 向请求方法的 headers 参数传入有效的字典类型信息。
- 编程举例:
import requests headers = {'User-Agent':'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11'} response = requests.get(url, headers=headers)
2. 验证 cookies
- 获取cookies
- 利用 requests 请求的响应对象的 cookies 属性获取
- 在浏览器中寻找:
- 打开登录界面,F12 开发者工具,点击“Network”
- 输入账号&密码,点击登录
- 在 Network 里面选择响应的请求信息,依次点击“Headers -> Request Headers -> Cookie”
- 设置 cookies
- 通过 headers 参数设置:将请求信息直接设置到 headers 字典里,然后传入请求方法的 headers 参数中。
import requests headers = { 'User-Agent':'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11', 'Cookie':'cookies信息' } response = requests.get(url, headers=headers)
- 通过 cookies 参数设置:构造 RequestsCookieJar 对象,并对 Cookie 信息进行处理,最后将处理后的对象传入请求方法的 cookies 参数。
import requests cookies = 'cookies信息' jar = requests.cookies.RequestsCookieJar() for cookie in cookies.split(";"): key, val = cookie.split("=", 1) jar.set(kay,val) headers = {'User-Agent':'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11'} response = requests.get(url, cookies=jar, headers=headers)
- 通过 headers 参数设置:将请求信息直接设置到 headers 字典里,然后传入请求方法的 headers 参数中。
3. 会话维持
- 介绍: 利用 Session 对象,维持同一个会话,即实现在同一会话内多次发送请求。通常用于模拟登录成功后再进行下一步的操作。
- 编程举例:
import requests s = requests.Session() data = {'user':'Chia','password':'123456'} response1 = s.post(url1, data=data) response2 = s.get(url2)
4. SSL 证书验证
- 证书验证错误: 当使用 requests 发送 HTTP 请求时,默认(verify=True)会检查 SSL 证书,如果请求的网站没有被官方 CA 机构信任,就会报错提示证书验证错误
requests.exceptions.SSLError
。解决方法如下:- 忽略错误:在请求方法中,将 verify 参数设置为 Fasle 即可忽略该错误。
- 指定本地证书:向 cert 传递单个文件(包含证书和密钥)路径,或者包含两个文件路径的元组。本地私有证书的 key 必须是解密状态。
- 警告: 解决证书验证错误后,会抛出“建议指定证书”的警告,解决方法如下:
- 忽略警告:
urllib3.disable_warnings()
。 - 捕获警告:
logging.capturewarnings(True)
- 忽略警告:
- 编程举例:
import requests # 忽略警告 from requests.packages import urllib3 urllib3.disable_warnings() # 捕获警告 import logging logging.capturewarnings(True) # 忽略错误 response = requests.get(url, verify=False) # 指定本地证书 response = requests.get(url, cert=('/path/server.crt','/path/key'))
5. 验证请求
- 场景: 网站在打开时弹出提示框,要求输入用户名和密码,验证成功后才能查看页面。
- 解决方法: 验证成功返回 200 状态码,验证失败返回 401 状态码。
- HTTPBasicAuth 对象:向请求方法的 auth 参数传入传入带有验证参数(用户名与密码)的 HTTPBasicAuth 对象。
- 元组:直接传一个元组,默认使用 HTTPBasicAuth 类来验证。
- 其他验证方式:如 OAuth 验证(需要安装 oauth 包:requests_oauthlib)
- 编程举例:
import requests from requests.auth import HTTPBasicAuth from requests_oauthlib import OAuth1 ah = HTTPBasicAuth('user','password') ah = ('user','password') ah = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET') response = requests.get(url, auth=ah)
6. 网络超时与常见异常
- 网络超时:
- 可以在请求方法中通过 timeout 参数设置请求的超时时间,这是时间是指发出请求到服务器返回响应的时间。
- 请求分为连接(connect)和读取(read)两个阶段,timeout 将用作这二者的时间总和,也可以传入一个元组分别指定
- 编程举例:
import requests
timeout = 0.1 # 连接 + 读取
timeout = (5,10) # 连接: 5s, 读取: 10s
time = None # 永久等待设置为 None 或不设置
response = requests.get(url, timeout=timeout)
- 异常: requests 模块中 3 种常见的网络异常类如下:
- ReadTimeout:超时异常
- HTTPError:HTTP 异常
- RequestException:请求异常
7. 文件上传
- 方法: 向
post()
方法的 files 参数传入一个 BufferedReader 对象(open() 函数返回类型),即可实现向服务器上传文件。注意,post() 方法将上传的文件转换成了 base64 编码格式。 - 编程举例:
import requests files = {'file': open('test.png','rb')} response = requests.post(url, files=files)
8. 代理服务
- 方法:
- 向请求方法的 proxies 参数中传入字典类型的有效代理数据。
- 如果代理需要使用 HTTP Basic Auth,对应的代理语法格式为
scheme://user:password@host:port
。 - requests 还支持 SOCKS 协议的代理。(需要安装 socks 库:‘requests[socks]’)
- 编程举例:
import requests # 普通代理 proxies = { 'http':'http://host:port', 'https':'https://host:port' } # HTTP Basic Auth proxies = { 'http':'http://user:password@host:port', 'https':'https://user:password@host:port' } # SOCKS 协议 proxies = { 'http':'SOCKS5://user:password@host:port', 'https':'SOCKS5://user:password@host:port' } response = requests.post(url, proxies=proxies, headers=headers, verify=False, timeout=2)
- 检测代理 IP 是否可用: 遍历待检测的代理 IP,并使用代理 IP 向查询 IP 地址的网页发送请求,如果请求成功,说明该 IP 可以使用。
import requests
for ip_i in ip_list:
proxies = {
'http':f'http://{ip_i}',
'https':f'https://{ip_i}'
}
response = requests.get('http://2020.ip138.com/', proxies=proxies, headers=headers, verify=False, timeout=2)
9. Prepared Request
- 介绍: 在 requests 模块中,可以将请求表示为一个名为 prepared request 的数据结构,其中各个参数都可以通过一个 Request 对象来表示。
- 编程示例:
from requests import Request, Session
s = Session()
request = Request('POST', url, data=data, headers=headers)
prepped = s.prepare_request(request)
response = s.send(prepped)
print(response.text)
3.3.4 高级请求模块
四、解析模块
4.1 正则表达式
4.1.1 正则表达式简介
获取的 web 资源(HTML 代码),大多以字符串类型返回,而正则表达式可以实现字符串的检索、替换、匹配验证等功能。Python的标准库 re 提供了整个正则表达式的实现。
4.1.2 字符
1. 元字符
不同的元字符规定了不同的字符集合。常见的元字符如下:
元字符 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母、数字、下划线或汉字 |
\W | 匹配除字母、数字、下划线或汉字以外的字符 |
\s | 匹配任意空白字符,等价于 [\t\n\r\f] (\f是换页符) |
\S | 匹配任意非空字符 |
\d | 匹配任意数字 |
\D | 匹配任意非数字 |
\A | 匹配字符串开头 |
\Z | 匹配字符串结尾,如果存在换行,只会匹配到换行前的字符串 |
\z | 匹配字符串结尾,如果存在换行,同时还会匹配换行符 |
\G | 匹配最后匹配完成的位置 |
\b | 匹配字符串边界,如在开始处、结尾处,或字符串的分界符为空格、标点以及换行 |
\B | 匹配非字符串边界 |
^ | 匹配一行字符串的开头 |
$ | 匹配一行字符串的结尾 |
[] | 匹配在 [] 中的字符,[\u4e00-\u9fa5] 表示匹配任意一个汉字 |
[^] | 匹配不在 [] 中的字符 |
() | 匹配括号内的表达式,也表示一个分组 |
2. 限定符
限定符可以匹配特定数量的指定字符,常见的限定符如下:
限定符 | 说明 |
---|---|
? | 匹配0个或1个字符,非贪婪模式 |
+ | 匹配1个或多个字符 |
* | 匹配任意个字符 |
{n} | 匹配n个字符 |
{n,} | 匹配至少n个字符 |
{n,m} | 匹配至少n个、至多m个字符,贪婪模式 |
a|b | 匹配 a 或 b |
(ab|cd) | 匹配 ab 或者 cd |
3. 转义字符与原生字符串
- 在正则表达式中使用转义字符
\
将特殊字符(如. ? \
)转换为普通的字符,如\.
表示匹配字符.
。 - 考虑到模式字符串中可能存在大量的特殊字符和反斜杠,可以在模式字符串前面加上
r
或者R
,将其转换成原生字符串。例如,模式字符串’\\bm\\w*\\b’
,对应的原生字符串为r’\bm\w*\b’
。
注意,并不是所有的反斜杠都需要转换,\+字母
组成的整体如果在python中没有特殊意义,就不需要转换,如\d
。
4. 贪婪与非贪婪匹配
.*
可以匹配任意长度的字符。- 在贪婪匹配下,
.*
会匹配到尽可能多的字符;通过在.*
后面添加?
,可以进行非贪婪匹配,匹配尽可能少的字符。 - 在做匹配的时候,字符串中间尽量使用贪婪匹配,而字符串结尾使用非贪婪匹配有可能匹配不到任何内容。
4.1.3 匹配方法
1. match()
- 格式:
re.match(pattern, string[, flags])
- 作用: 尝试从字符串开始的位置匹配正则表达式。(更适合用来检测整个字符串是否符合整个正则表达式的规则。)
- 返回类型: 匹配成功,返回 SRE_match 对象;匹配失败,返回 None。
- 主要方法与属性:
- start():匹配值的起始位置
- end():匹配值的结束位置
- span():匹配位置的元组,左闭右开,符合python规范
- string:要匹配的字符串
- group():匹配到的字符串。如果要获取字符串中的部分内容,只需将这部分内容放在
()
内,接着调用group()
方法获取指定分组的索引即可。索引从1开始。
- 参数:
- pattern:必选,表示正则表达式。
- string:必选,表示要匹配的字符串。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘Jct_hahaha’ match = re.match(pattern, string, re.I)
常见的修饰符如下:
修饰符 | 说明 |
---|---|
re.A 或 re.ASCII | 对于 \w、\W、\b、\B、\d、\D、\s、\S 只进行 ASCII 字符集的匹配 |
re.DEBUG | 显示编译时的 DEBUG 信息 |
re.I 或 re.IGNORECASE | 匹配时不区分大小写 |
re.L 或 re.LOCALE | (不常用)做本地化识别(locale-aware)匹配,使用当前预定字符类\w\W\b\B\s\S 时取决于当前区域设定 |
re.M 或re.MULTILINE | 多行匹配,即将 ^ 和 $ 用于包括整个字符串的开始和结尾的每一行 |
re.S 或 re.DOTALL | 使. 匹配包括换行符在内的所有字符 |
re.U 或 re.UNICODE | (不常用)根据 Unicode 字符集解析字符。这个标志影响\w\W\b\B\s\S\d\D |
re.X 或 re.VERBOSE | 忽略正则表达式中未转义的空格和注释 |
2. search()
- 格式:
re.search(pattern, string[, flags])
- 作用: 在整个字符串中搜索第一个匹配的值。
- 返回类型: 匹配成功,返回 SRE_match 对象;匹配失败,返回 None。
- 主要方法与属性:
- start():匹配值的起始位置
- end():匹配值的结束位置
- span():匹配位置的元组,左闭右开,符合python规范
- string:要匹配的字符串
- group():匹配到的字符串。如果要获取字符串中的部分内容,只需将这部分内容放在
()
内,接着调用group()
方法获取指定分组的索引即可。索引从1开始。
- 参数:
- pattern:必选,表示正则表达式。
- string:必选,表示要匹配的字符串。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘hello Jct_hahaha’ match = re.search(pattern, string, re.I)
3. findall()
- 格式:
re.findall(pattern, string[, flags])
- 作用: 搜索整个字符串,返回匹配正则表达式的所有内容。
- 返回类型: 匹配成功,返回包含所有匹配字符串的列表;匹配失败,返回空列表。
- 参数:
- pattern:必选,表示正则表达式。如果要返回所匹配正则表达式中的部分内容,可以在正则表达式中添加括号,当括号超过1个时,返回的列表元素为元组类型。
- string:必选,表示要匹配的字符串。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘hello jct_123 JcT_hahaha’ match = re.findall(pattern, string, re.I)
4.1.4 字符串处理
1. sub()
- 格式:
re.sub(pattern, repl, string[, count, flags])
- 作用: 将字符串中所有匹配正则表达式的部分,替换成其他字符串。
- 返回类型: 返回替换后的字符串。
- 参数:
- pattern:必选,表示正则表达式。
- repl:必选,表示替换成的字符串,允许通过置空来删除字符串中不需要的数据。
- string:必选,表示要被查找替换的原始字符串。
- count:可选,表示模式匹配后替换的最大次数,默认值为0,表示替换所有的匹配。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘hello jct_123 JcT_hahaha’ final_string = re.sub(pattern, "chia_nb", string, flags=re.I)
2. subn()
- 格式:
re.subn(pattern, repl, string[, count, flags])
- 作用: 将字符串中所有匹配正则表达式的部分,替换成其他字符串。
- 返回类型: 返回一个二元元组,第一个元素为替换后的字符串,第二个元素为替换次数。
- 参数:
- pattern:必选,表示正则表达式。
- repl:必选,表示替换成的字符串,允许通过置空来删除字符串中不需要的数据。
- string:必选,表示要被查找替换的原始字符串。
- count:可选,表示模式匹配后替换的最大次数,默认值为0,表示替换所有的匹配。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘hello jct_123 JcT_hahaha’ str_tuple = re.subn(pattern, "chia_nb", string, flags=re.I)
3. split()
- 格式:
re.split(pattern, string[, maxsplit, flags])
- 作用: 根据正则表达式分割字符串。
- 返回类型: 以列表形式返回分割后的每段字符串。
- 参数:
- pattern:必选,表示正则表达式。
- string:必选,表示要匹配的字符串。
- maxsplit:可选,表示最大的拆分次数。
- flags:可选,表示修饰符,用于控制匹配方式。
- 编程举例:
import re pattern = ‘jct_\w+’ string = ‘hello jct_123 JcT_hahaha’ str_tuple = re.subn(pattern, "chia_nb", string, flags=re.I)
4. compile()
- 格式:
re.compile(pattern[, flags])
- 作用: 将正则字符串编译成正则表达式对象,相当于给正则表达式做了一层封装。
- 返回类型: 返回一个 Pattern 对象。
- 参数:
- pattern:必选,表示正则表达式(正则字符串)。
- flags:可选,表示修饰符,用于控制匹配方式。在这里设置该参数,在 match()、search()、findall() 方法中就不再需要额外传入了。
- 编程举例:
import re re.compile(r"\d{2,}_\w+")