• 自学Python第十四天- 一些有用的模块:urllib、requests 网络编程基础,向爬虫靠拢



    python 很大的一个优点就是爬虫编写方便,当然在写爬虫之前还需要网络编程的基础,毕竟爬虫是基于网络的程序。

    urllib 库

    python 进行网络编程,可以使用 python 的内置库: urllib 库。urllib 库分为以下几个模块:

    • urllib.request :打开和读取 URL
    • urllib.error :包含 urllib.request 抛出的异常
    • urllib.parse :解析 URL
    • urllib.rebotparser :解析 robots.txt 文件

    urllib.request

    urllib.request 负责打开和读取 URL,是 urllib 库最重要也最常用的模块。其定义了一些打开 URL 的函数和类,包含授权验证、重定向、浏览器 cookkies 等。它可以模拟浏览器的一个请求发起过程。

    request 的简单使用

    request 最简单的用法就是模拟浏览器发送一个请求,并获取应答。

    from urllib.request import urlopen
    
    url = 'https://www.baidu.com'		# 设置请求地址
    resp = urlopen(url)					# 发送请求,并获得应答
    print(resp.read().decode('utf-8')	# 查看应答信息,因为默认是以二进制查看,所以需要解码为 utf-8
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样就获取到了应答信息。输出的是 html 文本,也可以写到文件中使用浏览器打开。

    with open('mybaidu.html', 'w', encoding='utf-8') as file:
        file.write(resp.read().decode('utf-8'))
    
    • 1
    • 2

    urllib.request.urlopen 函数的参数

    urlopen 函数的全部参数如下:
    urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

    • url :url地址,此参数也是唯一一个必须参数
    • data :发送到服务器的其他数据对象,默认 None
    • timeout :访问超时时间
    • cafile 和 capath :CA证书的文件名和路径,使用 HTTPS 需要用到
    • context :ssl.SSLContext 类型,用来指定 SSL 设置

    获取 request 的状态码

    发送请求后无论如何会收到一个状态码,这个状态码会表示该请求的响应状态。例如 200 成功、404 页面未找到等。

    import urllib.request
    
    myURL1 = urllib.request.urlopen("https://www.runoob.com/")
    print(myURL1.getcode())   # 200
    
    try:
        myURL2 = urllib.request.urlopen("https://www.runoob.com/no.html")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(404)   # 404
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    具体的状态码和含义可以看这里:

    HTTP请求状态码

    使用 urllib.request 设置请求头

    我们抓取网页一般需要对 headers(网页头信息)进行模拟,这时候需要使用到 urllib.request.Request 类:
    class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)

    • url :url地址
    • data :发送请求的其他数据对象
    • headers :请求头信息,字典格式
    • origin_req_host :请求的主机地址,IP 或域名
    • unverifiable :很少用整个参数,用于设置网页是否需要验证
    • method :请求方法, 如 GET、POST、DELETE、PUT等
    import urllib.request
    
    url = 'https://www.runoob.com/?s='  # 菜鸟教程搜索页面
    keyword = 'Python 教程'
    key_code = urllib.request.quote(keyword)  # 对请求进行编码
    url_all = url + key_code
    header = {
        'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    }  # 头部信息
    request = urllib.request.Request(url_all, headers=header)
    response = urllib.request.urlopen(request)
    
    print(response.read().decode('utf-8'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面的例子是将请求头以实参形式传入,也可以使用 Request.add_header() 方法添加请求头信息。

    import urllib.request
    
    url = 'http://www.baidu.com/'
    req = urllib.request.Request(url)
    req.add_header("User-Agent",'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36')
    html = urllib.request.urlopen(req).read()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    发送带数据的请求

    因为 Request 类的数据信息必须是字节型的,所以需要使用 urllib.parse 进行编码

    import urllib.request
    import urllib.parse
    
    url = 'https://www.runoob.com/try/py3/py3_urllib_test.php'  # 提交到表单页面
    data = {'name':'RUNOOB', 'tag' : '菜鸟教程'}   # 提交数据
    header = {
        'User-Agent':'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    }   #头部信息
    data = urllib.parse.urlencode(data).encode('utf8')  # 对参数进行编码,解码使用 urllib.parse.urldecode
    request=urllib.request.Request(url, data, header)   # 请求处理
    response=urllib.request.urlopen(request).read()      # 读取结果
    print(response.decode('utf-8'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用代理服务器

    使用代理服务器分为5步:设置代理、创建代理、应用代理、创建 Request 对象、发送访问请求:

    import urllib.request
    
    # 设置代理
    httpproxy_handler = urllib.request.ProxyHandler({'https': 'daili.com:8888'})
    # 创建代理
    opener = urllib.request.build_opener(httpproxy_handler)
    # 将代理应用到全局
    urllib.request.install_opener(opener)
    # 创建 request 对象
    request = urllib.request.Request(url)
    # 发送请求
    html = urllib.request.urlopen(request).read()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    urllib.error

    urllib.error 模块为 urllib.request 所引发的异常定义了异常类,基础异常类是 URLError。urllib.error 包含了两个方法,URLError 和 HTTPError。

    URLError 是 OSError 的一个子类,用于处理程序在遇到问题时会引发此异常(或其派生的异常),包含的属性 reason 为引发异常的原因。

    HTTPError 是 URLError 的一个子类,用于处理特殊 HTTP 错误,例如作为认证请求的时候,包含的属性 code 为 HTTP 的状态码, reason 为引发异常的原因,headers 为导致 HTTPError 的特定 HTTP 请求的 HTTP 响应头。

    例如对不存在的页面抓取并处理异常:

    import urllib.request
    import urllib.error
    
    myURL1 = urllib.request.urlopen("https://www.runoob.com/")
    print(myURL1.getcode())   # 200
    
    try:
        myURL2 = urllib.request.urlopen("https://www.runoob.com/no.html")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(404)   # 404
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    import urllib.request
    import urllib.error
    
    def download_with_retry(url,num_retries):
    	print('下载中 ',url)
    	try:
    		# 下载网页并获取网页的 html 内容
    		html = urllib.request.urlopen(url).read()
    	except urllib.erroe.URLError as e:
    		if hasattr(e, 'reason'):
    			print('下载失败')
    			print('失败原因:', e.reason)
    		if hasattr(e, 'code'):
    			print('服务器不能完成请求')
    			print('错误代码:', e.code)
    		if num_retries>0 and 500<=e.code<600:
    			return download_with_retries(url,num_retries-1)
    	return html
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    urllib.parse

    urllib.parse 用于解析 URL,格式如下:
    urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

    • urlstring 为字符串的 url 地址
    • scheme 为协议类型,
    • allow_fragments 参数为 false,则无法识别片段标识符。相反,它们被解析为路径,参数或查询组件的一部分,并 fragment 在返回值中设置为空字符串。
    from urllib.parse import urlparse
    
    o = urlparse("https://www.runoob.com/?s=python+%E6%95%99%E7%A8%8B")
    print(o)
    # 输出结果
    # ParseResult(scheme='https', netloc='www.runoob.com', path='/', params='', query='s=python+%E6%95%99%E7%A8%8B', fragment='')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    也可以使用 urllib.parse.urlencode() 将数据进行编码,例如使用 urllib.request.Request 对象发送带数据的请求时的例子。

    urllib.robotparser

    urllib.robotparser 用于解析 robots.txt 文件。robots.txt(统一小写)是一种存放于网站根目录下的 robots 协议,它通常用于告诉搜索引擎对网站的抓取规则。

    requests 库

    requests 库也是 python 用于网络编程的一个库,由于是第三发库,所以需要进行安装。

    requests 库的参考文档

    简单发送请求

    import requests
    
    url = 'https://www.sogou.com/web?query=周杰伦'
    resp = requests.get(url)
    print(resp)		# 执行结果: <Response [200]>
    print(resp.text)	# 执行结果为页面内容
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    关闭连接

    请求头中有一种设置:Connection: keep-alive ,如果不显式的断开连接,则会长时间占用TCP连接。通常可以发送一个请求报文,设置 Connection: close 。但是实际上其实可以直接对连接对象进行关闭。

    resp.close()	# 关闭 resp 连接
    
    • 1

    设置请求头

    上一个例子中,访问 https://www.sogou.com/web?query=周杰伦 时可以发现,浏览器可以正常访问,而 python 程序则不行。所以需要设置请求头,模拟普通浏览器发送请求来访问页面。

    requests 库的请求头写在一个字典内,并且在发送请求时需要传递至方法实参。

    import requests
    
    url = 'https://www.sogou.com/web?query=周杰伦'
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
    }
    resp = requests.get(url, headers=header)
    
    print(resp.text)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里请求头里的 User-Agent 信息可以使用浏览器调试工具获取。

    查看请求头

    在爬虫测试中,如果被反爬了,则很大几率是出在请求头的 User-Agent 里。我们可以先查看下请求头信息。

    import requests
    
    url = 'https://www.sogou.com/web?query=周杰伦'
    resp = requests.get(url)
    print(resp.request.headers)		# 输出请求头信息
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发送 post 请求

    发送 post 请求使用 requests.post() 方法。发送的数据是以字典形式传入实参。

    import requests
    
    url = 'https://fanyi.baidu.com/sug'
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
    }
    d = {'kw': 'dog'}
    resp = requests.post(url, headers=header, data=d)
    print(resp.json())		# 以 json 字符串形式输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    发送附加参数的 get 请求

    get 请求的参数除了可以直接写在 url 里,还可以使用附加方式发送:

    import requests
    
    url = 'https://movie.douban.com/j/chart/top_list'
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
    }
    param = {
        'type': '24',
        'interval_id': '100:90',
        'action': '',
        'start': 0,
        'limit': 20
    }
    resp = requests.get(url, headers=header, params=param)
    
    print(resp.request.url)
    print(resp.json())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用代理

    requests 使用代理和 urllib 使用代理类似,将代理字典(包含协议、地址+端口)以实参形式传入请求的形参 proxies 即可。

    resp = requests.get(url, headers=head, proxies=proxy)
    
    • 1

    响应对象的常用属性和方法

    不管使用 get 还是 post 发送了请求后,会返回一个 Response 对象,可以通过此对象的一些属性和方法获得我们想要的数据:
    r = requests.get('https://www.baidu.com')

    • r.status_code :返回状态码
    • r.reason :返回请求失败原因(请求成功返回 OK)
    • r.cookies :返回请求 cookies
    • r.headers :返回响应头
    • r.encoding :返回或设置响应内容的编码
    • r.content :返回响应内容
    • r.text :返回响应内容的文本(相当于 r.content.decode(‘utf-8’))
    • r.json() :将响应内容 json 化并返回
    • r.request :返回发送的请求信息
  • 相关阅读:
    Shiro-集成验证码
    软件测试(基础篇)
    ERROR 2003 (HY000) Can‘t connect to MySQL server on ‘localhost3306‘ (10061)解决办法
    通过js动态改变style样式的方法
    ESP8266-Arduino编程实例-Si7021温度传感器驱动
    LeetCode Cookbook 数组习题(4)
    JS排序算法
    浅谈跨域安全
    MySQL之COUNT性能到底如何?
    Vue消息订阅与发布
  • 原文地址:https://blog.csdn.net/runsong911/article/details/125438471