以下为个人在学习过程中做的笔记总结之爬虫常用库 urllib
urlib 库为 python3 的 HTTP 内置请求库
urilib 的四个模块:
urllib.request: 用于获取网页的响应内容
urllib.error: 异常处理模块, 用于处理异常的模块
urllib.parse: 用于解析 url
urllib.robotparse: 用于解析 robots.txt, 主要用于看哪些网站不能进行爬取, 不过少用
- 1
- urllib.request
- urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,cadefault=False,context=None)
url: 为请求网址
data: 请求时需要发送的参数
timeout: 超时设置, 在该时间范围内返回请求内容就不会报错
示例代码:
- from urllib import request
- # 请求获取网页返回内容
- response = request.urlopen('https://movie.douban.com/')
- # 获取网页返回内容
- print(response.read().decode('utf-8'))
- # 获取状态码
- print(response.status)
- # 获取请求头
- print(response.getheaders())
- # 对请求头进行遍历
- for k, v in response.getheaders():
- print(k, '=', v)
可以使用上面的代码对一些网站进行请求了, 但是当需要一些反爬网站时, 这就不行了, 这时我们需要适当地增加请求头进行请求, 这时就需要使用复杂一点的代码了, 这时我们需要用到 Request 对象
代码示例:
- # 请求头
- headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) ApplewebKit/537.36 (Khtml, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'}
- requests = request.Request('https://movie.douban.com/', headers=headers) # 加入自己的请求头更加接近浏览器
- # 进行请求, 把 Request 对象传入 urlopen 参数中
- response = request.urlopen(requests)
- print(response.read().decode('utf-8'))
这个我添加了请求头进行请求, 使我发送的请求更加接近浏览器的行为可以对应一些反爬网站了
如果网站需要进行登陆, 这时需要用到 post 方法, 用上面的也是可以的代码如下:
- from urllib import request, parse
- # 使用 post 方法来进行模拟登陆豆瓣
- data = {'source': 'None',
- 'redir': 'https://www.douban.com/',
- 'form_email': 'user',
- 'form_password': 'passwd',
- 'remember': 'on',
- 'login': '登录'}
- # 将 data 的字典类型转换为 get 请求方式
- data = bytes(parse.urlencode(data), encoding='utf-8')
- requests = request.Request('https://accounts.douban.com/login', headers=headers, data=data, method='POST')
- response = request.urlopen(requests)
- print(response.read().decode('utf-8'))
这里我用到了 data 的参数把登陆需要的参数传进去, 还加了个请求方法 Method
parse.urlencode() 后面有讲
这里还有另外一种添加请求头的方法
Request.add_header(): 参数有两个, 分别为请求头对应的键和值, 这种方法一次只能添加一个请求头, 添加多个需要用到循环或者直接用前面的方法添加多个请求头
在登陆了网站之后, 我们需要用到 cookie 来保存登陆信息, 这时就需要获取 cookie 了 urllib 获取 cookie 比较麻烦
代码示例如下:
- from http import cookiejar
- # 获取 cookie
- cookie = cookiejar.CookieJar()
- # 获取助手把 cookie 传进去
- handler = request.HTTPCookieProcessor(cookie)
- # 获取 opener 进行请求网站
- opener = request.build_opener(handler)
- # 请求网页
- response = opener.open('https://movie.douban.com/')
- # 打印 cookie
- for c in cookie:
- print(c.name, '=', c.value)
单纯地打印没什么用, 我们需要把他存入文件来保存, 下次使用时再次加载 cookie 来登陆
保存 cookie 为文件:
- from http import cookiejar
- # 将 cookie 保存在文件中
- filename = 'cookie.txt'
- cookie = cookiejar.MozillaCookieJar(filename) # 表示使用 Mozilla 的 cookie 方式存储和读取
- handler = request.HTTPCookieProcessor(cookie)
- opener = request.build_opener(handler)
- opener.open('https://movie.douban.com/')
- # 保存文件
- cookie.save(ignore_discard=True, ignore_expires=True)
另一种保存方法:
- from http import cookiejar
- cookie = cookiejar.LWPCookieJar(filename) # 表示 Set-Cookie3 文件格式存储和读取
- handler = request.HTTPCookieProcessor(cookie)
- opener = request.build_opener(handler)
- opener.open('https://movie.douban.com/')
- # 保存文件
- cookie.save(ignore_discard=True, ignore_expires=True)
这两种保存格式都是不一样的, 需要保存的内容一样
保存可以了, 这时就需要用到加载了, 当然也可以代码如下:
- from http import cookiejar
- # 从 cookie 文件加载到网页上实现记住登陆
- cookie = cookiejar.LWPCookieJar()
- # 加载文件
- cookie.load(filename, ignore_discard=True, ignore_expires=True)
- handler = request.HTTPCookieProcessor(cookie)
- opener = request.build_opener(handler)
- opener.open('https://movie.douban.com/')
这样就可以实现不用密码进行登陆了
cookie 小总结: 在操作 cookie 时, 都是分五步, 如下:
进行导包, 至关重要的一步, 不导包直接出错
获取 cookie 处理对象, 使用 cookiejar 包
创建 cookie 处理器, 使用 request.HTTPCookieJarProcessor()
利用 cookie 处理器构建 opener, 使用 request.build_opener()
进行请求网站, 用 opener.open(), 这个不能用 request.urlopen()
如果有时你在同一 ip 连续多次发送请求, 会有被封 ip 的可能, 这时我们还需要用到代理 ip 进行爬取, 代码如下:
- proxy = request.ProxyHandler({
- 'https': 'https://106.60.34.111:80'
- })
- opener = request.build_opener(proxy)
- opener.open('https://movie.douban.com/', timeout=1)
可以看到越复杂的请求都需要用到 request.build_opener(), 这个方法有点重要, 请记住哈
2
urllib.error
将上面的使用代理 ip 的请求进行异常处理, 如下:
- from urllib import request, error
- try:
- proxy = request.ProxyHandler({
- 'https': 'https://106.60.34.111:80'
- })
- opener = request.build_opener(proxy)
- opener.open('https://movie.douban.com/', timeout=1)
- except error.HTTPError as e:
- print(e.reason(), e.code(), e.headers())
- except error.URLError as e:
- print(e.reason)
因为有时这个 ip 或许也被封了, 有可能会抛出异常, 所以我们为了让程序运行下去进而进行捕捉程序
error.URLError: 这个是 url 的一些问题, 这个异常只有一个 reason 属性
error.HTTPError: 这个是 error.URLError 的子类, 所以在与上面的混合使用时需要将这个异常放到前面, 这个异常是一些请求错误, 有三个方法,.reason(), .code(), .headers(), 所以在捕捉异常时通常先使用这个
3
urllib.parse
解析 url:urllib.parse.urlparse(url, scheme='', allow_fragments=True)
简单的使用:
- from urllib import request, parse
- # 解析 url
- print(parse.urlparse('https://movie.douban.com/'))
- print(parse.urlparse('https://movie.douban.com/', scheme='http'))
- print(parse.urlparse('movie.douban.com/', scheme='http'))
- # 下面是结果
- ParseResult(scheme='https', netloc='movie.douban.com', path='/', params='', query='', fragment='')
- ParseResult(scheme='https', netloc='movie.douban.com', path='/', params='', query='', fragment='')
- ParseResult(scheme='http', netloc='', path='movie.douban.com/', params='', query='', fragment='')
可以看出加了 scheme 参数和没加的返回结果是有区别的而当 scheme 协议加了, 而前面的 url 也包含协议, 一般会忽略后面的 scheme 参数
既然后解析 url, 那当然也有反解析 url, 就是把元素串连成一个 url
- from urllib import parse
- # 将列表元素拼接成 url
- url = ['http', 'www', 'baidu', 'com', 'dfdf', 'eddffa'] # 这里至少需要 6 个元素 (我乱写的, 请忽视)
- print(parse.urlunparse(url))
- # 下面是结果
- http://www/baidu;com?dfdf#eddffa
urlparse() 接收一个列表的参数, 而且列表的长度是有要求的, 是必须六个参数以上, 要不会抛出异常
- Traceback (most recent call last):
- File "E:/anaconda/python_project/python3_spider/urllib_test.py", line 107, in <module>
- print(parse.urlunparse(url))
- File "E:\anaconda\lib\urllib\parse.py", line 454, in urlunparse
- _coerce_args(*components))
- ValueError: not enough values to unpack (expected 7, got 6)
urllib.parse.urljoin(): 这个是将第二个参数的 url 缺少的部分用第一个参数的 url 补齐
- # 连接两个参数的 url, 将第二个参数中缺的部分用第一个参数的补齐
- print(parse.urljoin('https://movie.douban.com/', 'index'))
- print(parse.urljoin('https://movie.douban.com/', 'https://accounts.douban.com/login'))
- # 下面是结果
- https://movie.douban.com/index
- https://accounts.douban.com/login
urllib.parse.urlencode(): 这个方法是将字典类型的参数转为请求为 get 方式的字符串
- data = {'name': 'sergiojuue', 'sex': 'boy'}
- data = parse.urlencode(data)
- print('https://accounts.douban.com/login'+data)
- # 下面是结果
- https://accounts.douban.com/loginname=sergiojuue&sex=boy
- 4
结语
还有个 urllib.robotparse 库少用, 就不说了, 留给以后需要再去查文档吧
上面的只是我在学习过程中的总结, 如果有什么错误的话, 欢迎在留言区指出, 还有就是需要查看更多用法的请查看文档 docs.python.org/3/library/u
需要代码的可以去我的 github 上面 fork, 给个 star 也行!
github:https://github.com/SergioJune/gongzhonghao_code/blob/master/python3_spider/urllib_test.py
学习过程中看的大多是崔庆才大佬的视频: edu.hellobi.com/course/157
来源: https://juejin.im/post/5aa4e2d85188255588050475