使用 urllib

首先,了解一下 urllib 库,它是 Python 内置的 HTTP 请求库,也就是说不需要额外安装即可使用。它包含如下 4 个模块。

  • request:它是最基本的 HTTP 请求模块,可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样,只需要给库方法传入 URL 以及额外的参数,就可以模拟实现这个过程了。
  • error:异常处理模块,如果出现请求错误,我们可以捕获这些异常,然后进行重试或其他操作以保证程序不会意外终止。
  • parse:一个工具模块,提供了许多 URL 处理方法,比如拆分、解析、合并等。
  • robotparser:主要是用来识别网站的 robots.txt 文件,然后判断哪些网站可以爬,哪些网站不可以爬,它其实用得比较少。

发送请求

urlopen

  • 构造方法

    urllib.request.urlopen(url, data=None, [timeout,]-, cafile=None, capath=None, cadefault=False, context=None)
  • 基本使用

    import urllib.request

    response = urllib.request.urlopen('https://www.python.org')
    # 获取返回类型
    print(type(response))
    # 获取返回结果的状态码
    print(response.status)
    # 获取响应头的各数据
    print(response.getheaders())
    # 获取了响应头中的 Server 值,结果是 nginx,意思是服务器是用 Nginx 搭建的。
    print(response.getheader('Server'))
    #获取读取信息,即网页的源代码
    print(response.read().decode('utf-8'))
  • data参数
    将参数转化为字节流编码格式的内容,即 bytes 类型,POST请求方式

    import urllib.parse  
    import urllib.request

    # 获取hello!xxz,会传送到运行结果的word --POST模拟表单获取
    # urlencode 方法来将参数字典转化为字符串
    data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')
    response = urllib.request.urlopen('http://httpbin.org/post', data=data)
    print(response.read())
  • timeout参数

    用于设置超时时间,单位为秒;如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常

    可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取

    import socket  
    import urllib.request
    import urllib.error

    try:
    response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
    except urllib.error.URLError as e:
    # socket.timeout --超时异常
    if isinstance(e.reason, socket.timeout):
    print('TIME OUT')
  • 其他参数

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

    ……

    详见文档:urllib.request — 用于打开 URL 的可扩展库 — Python 3.11.4 文档

Requset[power!]

如果请求中需要加入 Headers 等信息,就可以利用更强大的 Request 类来构建

  • Request构造方法

    class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
    • url 用于请求 URL,这是必传参数,其他都是可选参数。

    • data 如果要传,必须传 bytes(字节流)类型的。如果它是字典,可以先用 urllib.parse 模块里的 urlencode() 编码。

    • headers 是一个字典,它就是请求头,我们可以在构造请求时通过 headers 参数直接构造,也可以通过调用请求实例的 add_header() 方法添加。

    • unverifiable 表示这个请求是否是无法验证的,默认是 False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML 文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True。

    • method 是一个字符串,用来指示请求使用的方法,比如 GET、POST 和 PUT 等。

  • 初步使用

    from urllib import request, parse  

    url = 'http://httpbin.org/post'
    headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
    'Host': 'httpbin.org'
    }
    dict = {'name': 'Germey'}
    # data用 urlencode 和 bytes 方法转成字节流
    data = bytes(parse.urlencode(dict), encoding='utf8')
    req = request.Request(url=url, data=data, headers=headers, method='POST')
    # headers 也可以用 add_header 方法来添加
    # req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
    response = request.urlopen(req)
    print(response.read().decode('utf-8'))

power!

  • Handler

    把它理解为各种处理器,有专门处理登录验证的,有处理 Cookies 的,有处理代理设置的。利用它们,我们几乎可以做到 HTTP 请求中所有的事情

    父类BaseHandler,各种子类:

    • HTTPDefaultErrorHandler 用于处理 HTTP 响应错误,错误都会抛出 HTTPError 类型的异常。
    • HTTPRedirectHandler 用于处理重定向。
    • HTTPCookieProcessor 用于处理 Cookies。
    • ProxyHandler 用于设置代理,默认代理为空。
    • HTTPPasswordMgr 用于管理密码,它维护了用户名密码的表。
    • HTTPBasicAuthHandler 用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。
    • 详情可以参考官方文档: https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler
  • OpenerDirector
    就是利用 Handler 来构建 Opener

    • 验证

      #通过代码模拟访问需要验证的页面,并获取验证后的内容
      from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
      from urllib.error import URLError

      username = 'username'
      password = 'password'
      url = 'http://localhost:5000/'

      # 调用add_password方法将用户名、密码和URL添加到HTTPPasswordMgrWithDefaultRealm对象中,以建立用户名和密码的映射关系。
      p = HTTPPasswordMgrWithDefaultRealm()
      p.add_password(None, url, username, password)
      # 创建HTTPBasicAuthHandler对象,并将HTTPPasswordMgrWithDefaultRealm对象作为参数传入。这样就建立了一个处理验证的Handler
      auth_handler = HTTPBasicAuthHandler(p)

      # 调用build_opener方法,Opener对象在发送请求时会自动携带验证信息
      opener = build_opener(auth_handler)

      try:
      # 通过Opener对象的open方法打开指定的URL,完成验证过程。获取到验证后的页面源码内容
      result = opener.open(url)
      html = result.read().decode('utf-8')
      print(html)
      except URLError as e:
      print(e.reason)
    • 代理

      # 在爬虫中设置代理,以便在访问目标网站时使用指定的代理服务器
      from urllib.error import URLError
      from urllib.request import ProxyHandler, build_opener

      # 通过ProxyHandler创建了一个代理处理器,将代理的信息传递给处理器
      # 在本地搭建了一个代理,它运行在 9743 端口上。可以添加多个代理,以适应不同协议的请求
      proxy_handler = ProxyHandler({
      'http': 'http://127.0.0.1:9743',
      'https': 'https://127.0.0.1:9743'
      })
      # 用build_opener方法将代理处理器与其他可能的处理器(如身份验证处理器)一起构建成一个 Opener。
      # opener是用于发送请求的对象
      opener = build_opener(proxy_handler)
      try:
      response = opener.open('https://www.baidu.com')
      print(response.read().decode('utf-8'))
      except URLError as e:
      print(e.reason)
    • Coookies

      • 在爬虫中处理 Cookies
      • 从网站获取并使用 Cookies
      • 将 Cookies 保存到文件中以供后续使用

处理异常

URLError

from urllib import request, error  
try:
response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.URLError as e:
print(e.reason)

打开一个不存在的页面,照理来说应该会报错,但是这时我们捕获了 URLError 这个异常,运行结果如下:

Not Found

程序没有直接报错,而是输出了如上内容,这样通过如上操作,我们就可以避免程序异常终止,同时异常得到了有效处理。

HTTPError

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

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

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

  • headers:返回请求头

示例:

from urllib import request,error  
try:
response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, sep='\n')

运行结果:

Not Found
404
Server: nginx/1.4.6 (Ubuntu)
Date: Wed, 03 Aug 2016 08:54:22 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Vary: Cookie
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Link: <https://cuiqingcai.com/wp-json/>; rel="https://api.w.org/"

结合使用

因为 URLError 是 HTTPError 的父类,所以可以先选择捕获子类的错误,再去捕获父类的错误

先捕获 HTTPError,获取它的错误状态码、原因、headers 等信息。如果不是 HTTPError 异常,就会捕获 URLError 异常,输出错误原因。最后,用 else 来处理正常的逻辑。这是一个较好的异常处理写法。

reason返回字符串

from urllib import request, error  

try:
response = request.urlopen('https://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')

reason返回对象

import socket  
import urllib.request
import urllib.error

try:
response = urllib.request.urlopen('https://www.baidu.com', timeout=0.01)
except urllib.error.URLError as e:
print(type(e.reason))
if isinstance(e.reason, socket.timeout):
print('TIME OUT')

运行结果如下:

<class'socket.timeout'>
#reason 属性的结果是 socket.timeout 类,可以用 isinstance 方法来判断它的类型,作出更详细的异常判断。
TIME OUT

解析链接

urlparse -解析URL

  • 构造方法

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

    3 个参数:

    • urlstring:这是必填项,即待解析的 URL。

    • scheme:它是默认的协议(比如 http 或 https 等)。假如这个链接没有带协议信息,会将这个作为默认的协议。

      scheme 参数只有在 URL 中不包含 scheme 信息时才生效。如果 URL 中有 scheme 信息,就会返回解析出的 scheme

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

  • 实现 URL 的识别和分段

    from urllib.parse import urlparse  

    result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', scheme='https', allow_fragments=False)
    print(type(result), result)

    运行结果如下:

    <class 'urllib.parse.ParseResult'>
    ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment',
    fragment='')
  • 返回结果是一个 ParseResult 类型的对象,
    它包含 6 个部分,分别是 scheme、netloc、path、params、query 和 fragment。可以得出一个标准的链接格式,具体如下: scheme://netloc/path;params?query#fragment

  • 返回结果 ParseResult 实际上是一个元组
    我们可以用索引顺序来获取,也可以用属性名获取。示例如下:

    from urllib.parse import urlparse  

    result = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False)
    print(result.scheme, result[0], result.netloc, result[1], sep='\n')

    分别用索引和属性名获取了 scheme 和 netloc,其运行结果如下:

    http  
    http
    www.baidu.com
    www.baidu.com

urlunparse -构造URL

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

from urllib.parse import urlunparse  

data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))

这里参数 data 用了列表类型。也可以用其他类型,比如元组或者特定的数据结构。运行结果如下:

http://www.baidu.com/index.html;user?a=6#comment

urlsplit -解析URL

它不再单独解析 params 这一部分,只返回 5 个结果, params 会合并到 path 中

from urllib.parse import urlsplit  
result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)

运行结果如下:

SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')

返回结果是 SplitResult,也是一个元组类型,既可以用属性获取值,也可以用索引来获取

urlunsplit -构造URL

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

示例如下:

from urllib.parse import urlunsplit  

data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment']
print(urlunsplit(data))

运行结果如下:

http://www.baidu.com/index.html?a=6#comment

urljoin -解析URL

提供一个 base_url(基础链接)作为第一个参数,将新的链接作为第二个参数,该方法会分析 base_url 的 scheme、netloc 和 path 这 3 个内容并对新链接缺失的部分进行补充,最后返回结果。

示例:

from urllib.parse import urljoin  

print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))

运行结果:

http://www.baidu.com/FAQ.html  
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

urlencode -转GET请求参数

示例:

from urllib.parse import urlencode  

params = {
'name': 'germey',
'age': 22
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)

这里首先声明了一个字典来将参数表示出来,然后调用 urlencode 方法将其序列化为 GET 请求参数。运行结果如下:

http://www.baidu.com?name=germey&amp;age=22

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

parse_qs -转字典

利用 parse_qs 方法,就可以将它转回字典,示例如下:

from urllib.parse import parse_qs  

query = 'name=germey&amp;age=22'
print(parse_qs(query))

运行结果如下:

{'name': ['germey'], 'age': ['22']}

parse_qsl -转元组

将参数转化为元组组成的列表,示例如下:

from urllib.parse import parse_qsl  

query = 'name=germey&amp;age=22'
print(parse_qsl(query))

运行结果如下:

[('name', 'germey'), ('age', '22')]

quote -URL 编码

将内容(中文)转化为 URL 编码的格式

示例如下:

from urllib.parse import quote  

keyword = ' 壁纸 '
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)

这里我们声明了一个中文的搜索文字,然后用 quote 方法对其进行 URL 编码,最后得到的结果如下:

https://www.baidu.com/s?wd=% E5% A3%81% E7% BA% B8

unquote –URL 解码

进行 URL 解码,示例如下:

from urllib.parse import unquote  

url = 'https://www.baidu.com/s?wd=% E5% A3%81% E7% BA% B8'
print(unquote(url))

这是上面得到的 URL 编码后的结果,这里利用 unquote 方法还原,结果如下:

https://www.baidu.com/s?wd = 壁纸

分析Robots

Robots 协议

Robots 协议也称作爬虫协议,用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。它通常是一个叫作 robots.txt 的文本文件,一般放在网站的根目录下。

#其设置为 * 则代表该协议对任何爬取爬虫有效
User-agent: *
#指定了不允许抓取的目录,比如上例子中设置为 / 则代表不允许抓取所有页面。
Disallow: /
#Allow一般不会单独使用,用来排除某些限制,表示只可以抓取 public 目录
Allow: /public/

爬虫名称

一些常见搜索爬虫的名称及其对应的网站

爬虫名称 名  称 网  站
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

robotparser

使用 robotparser 模块来解析 robots.txt

  • 构造方法 (very easy啦

    urllib.robotparser.RobotFileParser(url='')
  • 模块常用方法

    • set_url :用来设置 robots.txt 文件的链接
    • read:读取 robots.txt 文件并进行分析,一般为ture
    • parse:用来解析 robots.txt 文件
    • can_fetch:该方法传入两个参数 User-agent& URL。返回的内容是该搜索引擎是否可以抓取这个 URL,返回结果是 True 或 False
    • mtime:返回的是上次抓取和分析 robots.txt 的时间
    • modified:将当前时间设置为上次抓取和分析 robots.txt 的时间
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
#rp = RobotFileParser('http://www.jianshu.com/robots.txt')
rp.set_url('http://www.jianshu.com/robots.txt')
rp.read()
#也可以使用 parse 方法执行读取和分析
#rp.parse(urlopen('http://www.jianshu.com/robots.txt').read().decode('utf-8').split('\n'))

#利用 can_fetch 方法判断了网页是否可以被抓取 True + False
print(rp.can_fetch('*', 'http://www.jianshu.com/p/b67554025d7d'))
print(rp.can_fetch('*', "http://www.jianshu.com/search?q=python&page=1&type=collections"))

request和urllib的区别

  • urllib 是Python标准库中的模块,不需要安装额外的包
  • request是一个三方库,它在urllib的基础上提供了更简洁、更高级的接口和功能。requests库的设计目标是提供更人性化的API,使发送HTTP请求变得更加简单和方便。它支持更多的HTTP功能,例如自动处理重定向、会话管理、上传文件、处理Cookies等
  • 需要进行HTTP请求的基本操作,学习和使用requests库会更加便捷和高效。如果你对Python标准库感兴趣,或者有特定的需求需要使用urllib的功能,也可以深入学习urllib库。
  • 需要进行HTTP请求的基本操作,学习和使用requests库会更加便捷和高效。如果你对Python标准库感兴趣,或者有特定的需求需要使用urllib的功能,也可以深入学习urllib库。

使用 requests

基本用法

GET请求方法:

  • 基本用法

    import requests  

    r = requests.get('http://httpbin.org/get')
    print(type(r))
    print(r.status_code)
    print(type(r.text))
    print(r.text)
    print(r.cookies)

    调用 get 方法得到一个 Response 对象,然后分别输出了 Response 的类型、状态码、响应体的类型、内容以及 Cookies。

  • 利用 params 参数附加额外信息

    请求的链接自动被构造成了:http://httpbin.org/get?age=22&name=germey

    import requests  

    data = {
    'name': 'germey',
    'age': 22
    }
    r = requests.get("http://httpbin.org/get", params=data)

    print(r.text)
    print(type(r.text))
    #返回类型是str类型的json格式,使用json方法解析返回结果,得到字典格式,如果返回结果不是JSON格式,会抛出json.decoder.JSONDecodeError异常
    print(r.json())
    print(type(r.json()))
  • 获取图片、音频和视频文件

    import requests

    r = requests.get("https://github.com/favicon.ico")
    #wb:二进制写的形式打开
    with open('favicon.ico', 'wb') as f:
    f.write(r.content)

其他请求方法:

  • 直接 .请求方法

    r = requests.post('http://httpbin.org/post')  
    r = requests.put('http://httpbin.org/put')
    r = requests.delete('http://httpbin.org/delete')
    r = requests.head('http://httpbin.org/get')
    r = requests.options('http://httpbin.org/get')
  • POST请求方法示例

    import requests

    data = {'name': 'germey', 'age': '22'}
    r = requests.post("http://httpbin.org/post", data=data)
    print(r.text)

    运行结果的 form 部分就是提交的数据,这就证明 POST 请求发送成功

响应

import requests

r = requests.get('http://www.jianshu.com')
#状态码
print(type(r.status_code), r.status_code)
#响应头 --CaseInsensitiveDict 类型
print(type(r.headers), r.headers)
#cookies --RequestsCookieJar 类型
print(type(r.cookies), r.cookies)
#URL
print(type(r.url), r.url)
#请求历史
print(type(r.history), r.history)
#状态码查询对象requests.codes
exit() if not r.status_code == requests.codes.ok else print('Request Successfully')

下面列出了返回码和相应的查询条件:

# 信息性状态码  
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),

# 成功状态码
200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),
201: ('created',),
202: ('accepted',),
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),

# 重定向状态码
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
302: ('found',),
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0

# 客户端错误状态码
400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),

# 服务端错误状态码
500: ('internal_server_error', 'server_error', '/o\\', '✗'),
501: ('not_implemented',),
502: ('bad_gateway',),
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication')

高级用法

文件上传

import requests

#rb -只读二进制打开文件
files = {'file': open('favicon.ico', 'rb')}
#post方法--提交数据
#将打开的文件favicon.ico作为文件对象传递给requests.post()方法,以进行文件上传。
r = requests.post('http://httpbin.org/post', files=files)
print(r.text)
#返回响应,里面包含 files 这个字段

cookies

  • 获取cookies

      import requests

    r = requests.get('https://www.baidu.com')
    print(r.cookies)
    #用items方法转化成元组 ,遍历输出每一个cookies的名称和值
    for key, value in r.cookies.items():
    print(key + '=' + value)
    ````

    - 设置cookies并维持登录状态

    ```python
    import requests

    headers = {
    'Cookie': 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0',
    'Host': 'www.zhihu.com',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36',
    }
    r = requests.get('https://www.zhihu.com', headers=headers)
    print(r.text)
  • 使用Cookies参数设置Cookies的方法

    新建一个 RequestCookieJar 对象–>将复制下来的 cookies 利用 split 方法分割–>利用 set 方法设置好每个 Cookie 的 key 和 value–>通过调用 requests 的 get() 方法并传递给 cookies 参数【可以但没必要?】

    import requests

    cookies = 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0'
    jar = requests.cookies.RequestsCookieJar()
    headers = {
    'Host': 'www.zhihu.com',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
    }
    for cookie in cookies.split(';'):
    key, value = cookie.split('=', 1)
    jar.set(key, value)
    r = requests.get('http://www.zhihu.com', cookies=jar, headers=headers)
    print(r.text)

会话维持

会话维持是指在进行多个请求时,保持相同的会话状态,而不是每个请求都创建一个新的会话。

解决问题::第一个请求使用post方法登录了一个网站,然后想要获取登录后的个人信息,你再次使用get方法请求个人信息页面。实际上,这相当于在两个独立的会话中操作,它们并不相关,所以无法成功获取个人信息。

有一种解决方法是在每次请求时手动设置相同的Cookies,但这样做非常麻烦。我们有更简单的解决方法,那就是使用Session对象。

Session对象可以方便地维护一个会话,并自动处理Cookies的问题。通过创建一个Session对象,我们可以在同一个会话中发送多个请求,会话对象会自动处理Cookies的传递和存储。

import requests

s = requests.Session()
#请求了一个测试网址,设置一个cookie名称叫作 number,内容是 123456789
s.get('http://httpbin.org/cookies/set/number/123456789')
#随后又请求了http://httpbin.org/cookies,此网址可以获取当前的 Cookies
r = s.get('http://httpbin.org/cookies')
print(r.text)

运行结果:(运行的好慢,建议直接点进网址)

{
"cookies": {"number": "123456789"}
}

SSL 证书验证

当发送 HTTP 请求的时候,requests 会提供验证SSL 证书功能。通过verify参数控制是否验证证书,默认情况下verifyTrue,会自动验证证书。

import requests
from requests.packages import urllib3

#当禁用证书验证或出现其他安全相关警告时,禁用urllib3库的警告信息来忽略警告
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)

指定本地证书用作客户端证书:

import requests

response = requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key'))
print(response.status_code)

代理设置

  • proxies参数

    import requests

    proxies = {'https': 'http://user:password@10.10.1.10:3128/',}
    requests.get('https://www.taobao.com', proxies=proxies)
  • 使用 HTTP Basic Auth

    import requests

    proxies = {'https': 'http://user:password@10.10.1.10:3128/',}
    requests.get('https://www.taobao.com', proxies=proxies)
  • SOCKS 协议的代理 pip3 install "requests[socks]"

    import requests

    proxies = {
    'http': 'socks5://user:password@host:port',
    'https': 'socks5://user:password@host:port'
    }
    requests.get('https://www.taobao.com', proxies=proxies)

超时设置

timeout参数

import requests
#timeout=(5, 30) 连接+读取分别指定时间
r = requests.get('https://www.taobao.com', timeout=1)
print(r.status_code)

身份认证

import requests

r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)

OAuth 认证 pip3 install requests_oauthlib

import requests
from requests_oauthlib import OAuth1

url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)

Prepared Request

目的:

  1. 请求定制化:可以设置请求的URL、请求方法、请求数据、请求头、超时时间等各种参数,以满足具体的需求
  2. 请求队列调度:可以将多个Prepared Request对象放入队列中,并按照一定的策略进行调度和发送,实现批量处理请求、并发请求、异步请求等功能,提高请求的效率和性能
  3. 会话状态保持:在一个会话中,你可以发送多个请求,并共享会话级别的参数,如cookies、认证信息等。这对于模拟登录、进行会话管理以及处理需要会话保持的操作非常有用

构造Prepared Request对象 – 发送Prepared Request

from requests import Request, Session

url = 'http://httpbin.org/post'
data = {'name': 'germey'}
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
}
s = Session()
req = Request('POST', url, data=data, headers=headers)
#调用 Session 的 prepare_request 方法将其转换为一个 Prepared Request 对象 -- 将请求表示为数据结构
prepped = s.prepare_request(req)
#发送`Prepared Request`
r = s.send(prepped)
#获取请求结果
print(r.text)

运行结果如下:

{"args": {}, 
"data": "","files": {},"form": {"name":"germey"},"headers": {"Accept":"*/*","Accept-Encoding":"gzip, deflate","Connection":"close","Content-Length":"11","Content-Type":"application/x-www-form-urlencoded","Host":"httpbin.org","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36"},"json": null,"origin":"182.32.203.166","url":"http://httpbin.org/post"}

正则表达式

常用的匹配规则

模  式 描  述
\w 匹配字母、数字及下划线
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符,等价于 [\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9]
\D 匹配任意非数字的字符
\A 匹配字符串开头
\Z 匹配字符串结尾,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符,当 re.DOTALL 标记被指定时,则可以匹配包括换行符的任意字符
[…] 用来表示一组字符,单独列出,比如 [amk] 匹配 a、m 或 k
[^…] 不在 [] 中的字符,比如 [^abc] 匹配除了 a、b、c 之外的字符
* 匹配 0 个或多个表达式
+ 匹配 1 个或多个表达式
? 匹配 0 个或 1 个前面的正则表达式定义的片段,非贪婪方式
{n} 精确匹配 n 个前面的表达式
{n, m} 匹配 n 到 m 次由前面正则表达式定义的片段,贪婪方式
a b
( ) 匹配括号内的表达式,也表示一个组

match :从头找

匹配目标

如果匹配,就返回匹配成功的结果;如果不匹配,就返回 None

import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}', content)
#^ 是匹配字符串的开头,也就是以 Hello 开头
#\s 匹配空白字符
#\d 匹配数字 \d{4}匹配 4 个数字
#\w{10} 匹配 10 个字母及下划线
print(result)
#SRE_Match 对象的group方法:匹配的内容
print(result.group())
#span方法:匹配的范围
print(result.span())

运行结果如下:

41
<_sre.SRE_Match object; span=(0, 25), match='Hello 123 4567 World_This'>
Hello 123 4567 World_This
(0, 25)

通用匹配(./*)

.(点)匹配任意字符

(星)匹配前面的字符*无限次

组合在一起就可以匹配任意字符( •̀ ω •́ )y

贪婪与非贪婪

.*:贪婪匹配,匹配尽可能多字符

.*?:非贪婪匹配,匹配尽量少字符

在做匹配的时候字符串中间尽量使用非贪婪匹配,以避免出现匹配结果缺失的情况

修饰符

修饰符 描  述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使。匹配包括换行在内的所有字符
re.U 根据 Unicode 字符集解析字符。这个标志影响 \w、\W、\b 和 \B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

在网页匹配中,较为常用的有 re.S 和 re.I

转义匹配 \

search :找一个

在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果

import re

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
result = re.search('Hello.*?(\d+).*?Demo', content)
print(result)

findall :找全部

返回列表类型,遍历获得每组内容

results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
print(results)
print(type(results))
for result in results:
print(result)
print(result[0], result[1], result[2])

sub :删除

删除匹配到的文本

import re
#删掉数字
content = '54aK54yr5oiR54ix5L2g'
content = re.sub('\d+', '', content)
print(content)

compile :复用

将正则字符串编译成正则表达式对象,相当于做了一层封装,以便服用

import re

content1 = '2016-12-15 12:00'
content2 = '2016-12-17 12:55'
content3 = '2016-12-22 13:21'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1, result2, result3)

方法技巧

在做匹配的时候,字符串中间尽量使用非贪婪匹配

match:更适合用来检测某个字符串是否符合某个正则表达式的规则

search:匹配方便

findall:要提取多个内容时

案例:抓取猫眼电影排行

先浅写一个思路

目标

提取出猫眼电影 TOP100 的电影名称、时间、评分、图片等信息,提取的站点 URL 为 http://maoyan.com/board/4,提取的结果会以文件形式保存下来。

分析

http://maoyan.com/board/4?offset=10 的offset 代表偏移量值

开始写代码

import json
import requests
from requests.exceptions import RequestException
import re
import time
from selenium import webdriver

# 1.获取首页代码内容
def get_one_page(url):
try:
headers = {
'Cookie': '',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/73.0.3683.86 Safari/537.36'
}

# 创建Chrome浏览器的实例
driver = webdriver.Chrome()
driver.get(url)
# 等待页面加载完成
time.sleep(5)
# 获取页面内容
content = driver.page_source
driver.quit()
return content
except RequestException:
return None
# response = requests.get(url, headers=headers)
# if response.status_code == 200:
# return response.text
# except RequestException:
# return None


# 2.正则获取相关内容,并进行格式处理
def parse_one_page(html):
pattern = re.compile(
'<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',
re.S)
items = re.findall(pattern, html)
for item in items:
yield {'index': item[0],
'image': item[1],
'title': item[2],
'actor': item[3].strip()[3:],
'time': item[4].strip()[5:],
'score': item[5] + item[6]
}


# 3.写进文件
def write_to_file(content):
with open('result.txt', 'a', encoding='utf-8') as f:
print(type(json.dumps(content)))
f.write(json.dumps(content, ensure_ascii=False) + '\n')


# 调用main方法
def main(offset):
url = 'http://maoyan.com/board/4?offset=' + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)


# 分页爬取
if __name__ == '__main__':
for i in range(10):
main(offset=i * 10)
# 速度过快,则会无响应
time.sleep(1)

存在的问题:

  1. 首页验证码登录 –请求头添加cookies

  2. 添加了反爬虫-验证拼图-暂未解决

    用selenium打开浏览器,进行了11-50页面的滑动验证,能保证代码继续运行,但是11-50的数据没有获得【?】

    运行结果:image-20230612220406176