Python--urllib


urllib

request

最基本的http请求模块

build_opener 函数

使用一些默认的handler构建一个opener(OpenerDirector类型)

urlopen 函数

最基本的构造HTTP请求的方法,实际这个函数内部就是使用build_opener完成创建opener,然后由opener完成请求

  • 参数
    • data: 二进制,如果传入了此参数,请求方式是POST
    • timeout: 超时时间
    • context: ssl.SSLContext类型
    • cafile: CA证书
    • capath: CA证书路径
  • 返回值
    • http.client.HTTPResponse类型,包含以下方法和属性
      • 方法: read, readinfo, getheader, getheaders, fileno
      • 属性: msg, version, status, reason, debuglevel, closed
      • 返回状态: status
      • 返回的数据: read(),如果是文本,可以:read().decode('utf-8)

OpenerDirector 类

实际上请求都是由这个类的实例完成

open 方法

发起请求

Request 类

可以构建更复杂的HTTP请求,比如添加headers

init 方法
  • 参数
    • headers: 字典形式的headers
add_header 方法

添加请求头,如果已经存在会覆盖原来的值,否则新增

BaseHandler 类

各种handler的父类

HTTPDefaultErrorHandler 类

用于处理HTTP响应错误,所有错误都会抛出HTTPError类型的异常

HTTPRedirectHandler 类

用于处理重定向

HTTPCookieProcessor 类

用于处理Cookie

ProxyHandler 类

用于设置代理,代理默认为空

HTTPPasswordMgr 类

用于管理密码,它维护着用户名密码的对照表

HTTPBasicAuthHandler 类

用于管理认证,如果一个链接在打开时需要认证,那么可以用这个类来解决认证问题

parse

工具模块,用来对URL进行解析,合并等操作

urlparse 函数

实现URL的识别和分段,结果包含6部分:scheme://netloc/path;params?query#fragment

  • 参数

    • urlstring: 即待解析的url
    • scheme: 如果url中不包含scheme,则此参数作为默认值
    • allow_fragments: True(默认)/False,如果设置为False,则解析后的fragment为空字符串,对应的数据会被解析为path、params或者query的一部分,一般前面三者谁在最后就跟谁
  • 返回值

    • urllib.parse.ParseResult
    • 可以直接.属性获取相关部分的数据,也可以使用[下标]的方式

urlunparse 函数

这个函数用于构造url,即urlparse的逆操作

  • 参数
    • components:包含6个元素的可迭代对象,多了或者少了都会报错

urlsplit 函数

与方法urlparse类似,只是会把params部分合并到path中,结果只有5部分

  • 返回值
    • urllib.parse.SplitResult

urlunsplit 函数

urlsplit的逆操作

urljoin 函数

常用于拼接 base_url和path
把base链接里面的部分填充到新url中对应缺失的部分

  • 参数
    • base: 基础链接
    • url: 新url
    • allow_fragments

urlencode 函数

可以将字典类型转成GET请求参数

parse_qs 函数

urlencode的逆操作,结果是字典类型

parse_qsl 函数

同parse_qs,结果是列表类型

quote 函数

对内容进行url编码

unquote 函数

quote的逆操作

error

异常处理模块

URLError 类

其他异常类的基类

  • 属性
    • reason

HTTPError 类

专门处理HTTP请求错误

  • 属性
    • code
    • reason
    • headers

robotparser

处理网站robot.txt文件

RobotFileParser 类

用来解析robots.txt文件,并可以判断是否能爬取指定的url

parse 方法
  • 参数
    • lines: robots.txt文件中的一行行
can_fetch 方法

判断某个useragent是否能爬取某个url

  • 参数
    • useragent:
    • url:
import datetime
import http.cookiejar
import time
from urllib import request, parse, error, robotparser


def test_urlopen():
    url_get = 'https://www.httpbin.org/get'
    url_post = 'https://www.httpbin.org/post'
    data = bytes(parse.urlencode({'name': 'post'}), encoding='utf-8')
    print(datetime.datetime.now())
    try:
        response = request.urlopen(url_get)
        # response = request.urlopen(url_post, data=data)
        print(response.status)
        print(response.read().decode('utf-8'))
    except Exception as e:
        print(f'异常: {e}')
    print(datetime.datetime.now())

def test_Request():
    print('Request'.center(50, '='))
    url_post = 'https://www.httpbin.org/post'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36',
    }
    req = request.Request(url_post, headers=headers, data=data)
    req.add_header('User-Agent', 'mycrawler')
    req.add_header('My', 'Yep')
    response = request.urlopen(req)
    print(response.status)
    print(response.read().decode('utf-8'))

def test_auth_handler():
    print('HTTPBasicAuthHandler'.center(50, '='))

    username = 'admin'
    password = 'admin'
    url = 'https://ssr3.scrape.center'

    p = request.HTTPPasswordMgrWithDefaultRealm()
    p.add_password(None, url, username, password)
    auth_handler = request.HTTPBasicAuthHandler(p)
    opener = request.build_opener(auth_handler)
    try:
        response = opener.open(url)
        print(response.status, response.read().decode('utf-8'))
    except error.URLError as e:
        print(e.reason)


def test_proxy_handler():
    print('ProxyHandler'.center(50, '='))

    proxy_handler = request.ProxyHandler({
        'http': 'http://127.0.0.1:8080',
        'https': 'https://127.0.0.1:8080',
    })
    opener = request.build_opener(proxy_handler)
    try:
        response = opener.open('https://www.baidu.com')
        print(response.status, response.read().decode('utf-8'))
    except error.URLError as e:
        print(e.reason)


def test_cookie_processor():
    print('HTTPCookieProcessor'.center(50, '='))

    cookie = http.cookiejar.CookieJar()
    handler = request.HTTPCookieProcessor(cookie)
    opener = request.build_opener(handler)
    response = opener.open('https://www.baidu.com')
    for item in cookie:
        print(item.name + '=' + item.value)


def test_cookie_file():
    print('cookie_file'.center(50, '='))

    cookie_file = 'cookie.txt'
    # cookie = http.cookiejar.MozillaCookieJar(cookie_file)
    cookie = http.cookiejar.LWPCookieJar(cookie_file)
    handler = request.HTTPCookieProcessor(cookie)
    opener = request.build_opener(handler)
    response = opener.open('https://www.baidu.com')
    cookie.save(ignore_expires=True, ignore_discard=True)

def test_read_cookie_file():
    print('read cookie_file'.center(50, '='))

    # cookie = http.cookiejar.LWPCookieJar()
    # cookie.load(cookie_file, ignore_discard=True, ignore_expires=True)
    # for item in cookie:
    #     print(item.name + '=' + item.value)  # 这里有输出,说明读取成功了
    # handler = request.HTTPCookieProcessor(cookie)
    # opener = request.build_opener(handler)
    # # 貌似不起作用,返回的cookie没有数据
    # response = opener.open('https://www.httpbin.org/cookies')
    # 下面这种方式可以返回cookie数据
    req = request.Request('https://www.httpbin.org/cookies', headers={'Cookie':'BIDUPSID=2189E52DAAF0E09623AD53F9D29B9F81'})
    response = opener.open(req)
    print(response.read().decode('utf-8'))


def test_urlparse():
    print('urlparse'.center(50, '='))
    # url不带scheme,netloc部分被解析到了path
    # url = 'www.baidu.com/index.html;user?id=5#comment'
    url = 'http://www.baidu.com/index.html;user?id=5#comment'
    result = parse.urlparse(url, scheme='https')
    # result = parse.urlparse(url, allow_fragments=False)
    print(type(result))
    print(result)
    print(type(result.fragment))

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


def test_urlunparse():
    print('urlunparse'.center(50, '='))

    url = parse.urlunparse(['https', 'www.baidu.com', '/index.html', 'user', 'id=5', 'comment'])
    print(url)


def test_urljoin():
    print('urljoin'.center(50, '='))

    print(parse.urljoin('https://www.baidu.com', 'index.html'))
    print(parse.urljoin('https://www.baidu.com', 'www.cnblogs.com/index.html'))
    print(parse.urljoin('https://www.baidu.com', 'http://www.cnblogs.com/index.html'))

    '''
    https://www.baidu.com/index.html
    https://www.baidu.com/www.cnblogs.com/index.html
    http://www.cnblogs.com/index.html
    '''


def test_urlencode():
    print('urlencode'.center(50, '='))

    data = {
        'name': ['名称', '名称2'],
        'password': '密码'
    }
    # 当一个key有多个值时,字典的方式得到的结果不对,会把整个list作为整体进行encode
    # data = {'name': ['名称', '名称2'], 'password': ['密码']}
    # 需要使用列表的方式
    # data = [('name', '名称'), ('name', '名称2'), ('password', '密码')]
    base_url = 'https://www.baidu.com?'
    print(base_url + parse.urlencode(data))

    '''
    https://www.baidu.com?name=%E5%90%8D%E7%A7%B0&password=%E5%AF%86%E7%A0%81
    '''


def test_parse_qs():
    print('parse_qs'.center(50, '='))
    # url = 'https://www.baidu.com?name=%E5%90%8D%E7%A7%B0&password=%E5%AF%86%E7%A0%81'
    # url = 'https://www.baidu.com?name=名称&name=名称2&password=密码'
    # 这种方式解析出来的name值是字符串,而不是数组
    url = 'https://www.baidu.com?name=%5B%27%E5%90%8D%E7%A7%B0%27%2C+%27%E5%90%8D%E7%A7%B02%27%5D&password=%5B%27%E5%AF%86%E7%A0%81%27%5D'
    '''
    qs: {'name': ["['名称', '名称2']"], 'password': ["['密码']"]}
    qsl: [('name', "['名称', '名称2']"), ('password', "['密码']")]
    '''
    query = parse.urlparse(url).query
    print(query)
    # data = parse.parse_qs(query)
    data = parse.parse_qsl(query)
    print(data)

    ''' parse_qs
    name=%E5%90%8D%E7%A7%B0&password=%E5%AF%86%E7%A0%81
    {'name': ['名称'], 'password': ['密码']}
    '''

    ''' parse_qsl
    name=%E5%90%8D%E7%A7%B0&password=%E5%AF%86%E7%A0%81
    [('name', '名称'), ('password', '密码')]
    '''


def test_quote():
    print('quote'.center(50, '='))
    print(parse.quote('名称'))
    print(parse.quote('https://www.baidu.com?name=名称&name=名称2&password=密码'))
    print(parse.quote('https://www.baidu.com?name=名称&name=名称2&password=密码', safe=''))

    '''
    %E5%90%8D%E7%A7%B0
    https%3A//www.baidu.com%3Fname%3D%E5%90%8D%E7%A7%B0%26name%3D%E5%90%8D%E7%A7%B02%26password%3D%E5%AF%86%E7%A0%81
    https%3A%2F%2Fwww.baidu.com%3Fname%3D%E5%90%8D%E7%A7%B0%26name%3D%E5%90%8D%E7%A7%B02%26password%3D%E5%AF%86%E7%A0%81

    '''

def test_unquote():
    print('unquote'.center(50, '='))
    url = 'https%3A%2F%2Fwww.baidu.com%3Fname%3D%E5%90%8D%E7%A7%B0%26name%3D%E5%90%8D%E7%A7%B02%26password%3D%E5%AF%86%E7%A0%81'
    print(parse.unquote(url))

    '''
    https://www.baidu.com?name=名称&name=名称2&password=密码
    '''


def test_robots_text():
    print('RobotFileParser'.center(50, '='))
    rp = robotparser.RobotFileParser()
    response = request.urlopen('https://www.baidu.com/robots.txt')
    rp.parse(response.read().decode('utf-8').split('\n'))
    print(rp.can_fetch('BaiduSpider', 'https://www.baidu.com'))
    print(rp.can_fetch('BaiduSpider', 'https://www.baidu.com/homepage/'))
    print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))

def main():
    # test_urlparse()
    # test_urlunparse()
    # test_urljoin()
    # test_urlencode()
    # test_parse_qs()
    # test_quote()
    # test_unquote()
    test_robots_text()


if __name__ == '__main__':
    main()