在python2中有urllib和urllib2两个库来实现发送请求,而在python3中,将这两个库统一为一个urllib库了。
主要分为一下四个功能模块:
requests (请求)
error (异常处理)
parse (url解析)
robotparser(识别robots.txt文件【告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的】)
1. urllib.request
请求方法一共有8种,包括: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TARCE。而我们写爬虫常用到的就是get请求和post请求,get请求就是不需要往服务器上传数据的请求,而post请求就需要上传数据,如用户登录,用户注册这些都是post请求
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
url可以是字符串,也可以是自己构造的Request对象
data是需要发送给服务器的数据对象,如果没有则不用填写,默认为None
timeout超时设置,是一个可选参数,传入超时时间后,如果在指定的时间内服务器没有响应则抛出time out异常
cafile和capath代表 CA 证书和 CA 证书的路径。如果使用HTTPS则可能需要用到
context参数必须是ssl.SSLContext类型,用来指定SSL设置
该函数返回的对象提供的方法有:
read() ,readline() ,readlines() ,fileno(),close() ,这些函数是对HTTP response类型的数据进行操作
info(),返回HTTP message对象,表示远程服务器返回的头信息
getcode(),返回HTTP状态码
geturl(),返回请求的url
status,返回状态码
getheaders(),响应的头部信息
getheader(“Server”),返回响应头指定的参数Server的值
from urllib import request response = request.urlopen('http://www.baidu.com') # get方式请求 print(response.read().decode('utf-8')) # 返回的数据格式为bytes类型,需要decode()解码,转换成str类型
构造Request
urlopen()可以支撑我们的一些简单的请求,但是请记住,一个没有请求头的爬虫是没有灵魂的。虽然不使用请求头也可以访问一些网页,但是这样的行为是直接告诉服务器“我是一个爬虫”,那么服务器可能就会拒绝程序的请求,因此我们需要进行伪装,这时候我们就需要去构造我们的HTTP请求体,一个Request对象。比如上面的代码,我们可以这样改写:
from urllib import request request = request.Request("http://www.baidu.com") response = request.urlopen(request) print(response.read())
运行结果是完全一样的,只不过中间多了一个request对象,推荐大家这么写,因为在构建请求时还需要加入好多内容,通过构建一个request,[服务器]响应请求得到应答,这样显得逻辑上清晰明确。
POST方式
前面我们说的data就是post请求需要传送的数据
from urllib import request, parse login_url = "http://www.xbiquge.la/login.php?jumpurl=http://www.xbiquge.la/" dic = { "LoginForm[username]": "spider", "LoginForm[password]": "123456", } data = parse.urlencode(dic) # 不自己构造Request response = request.urlopen(login_url, data.encode()) # 注意传入data必须是字节类型 print(response.getcode()) # 自己构造Request Reqeust = request.Request(login_url, data) response = request.urlopen(Request) print(response.getcode())
这里的parse.urlencode就是将数据dic进行URL编码
在某些时候,POST请求是可以使用GET请求的,因为将表单数据添加到URL上的
设置代理(proxy)
有的服务器会根据ip检测用户的访问频度,发现不对劲就把你的ip封了,你就在也访问不了了。这是就需要使用代理ip了,每个一段时间跟换一个ip,服务器就发现不了你在搞蛋。
import urllib.request proxy_support = urllib.request.ProxyHandler({'https': '58.253.155.101:9999'}) opener = urllib.request.build_opener(proxy_support) # 方式一 urllib.request.install_opener(opener) a = urllib.request.urlopen("http://www.baidu.com").read().decode("utf8") # 方式二 # a = opener.open("http://www.baidu.com").read().decode("utf8") print(a)
设置请求头headers
User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
**Referer **: 该网页来自哪页面,服务器在检查反盗链是会判断
Cookie : 保存用户信息,我们在绕过登录时常用到
Content-Type : 在使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析。
application/[xml] : 在 XML RPC,如 RESTful/SOAP 调用时使用
application/json : 在 JSON RPC 调用时使用
application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用
有的服务器会检测请求的cookie,user-agent等等信息,这时我们就需要将这些信息添加到headers里一起发送给服务器,而这些请求头信息就需要我们在浏览器复制过来。这个user-agent一般都是需要加上的
右键选择“检查”(也可按F12),进入开发者页面
悬着Network这一栏,在刷新该网站
代码部分:
from urllib import request url = "http://www.baidu.com" headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36" } req = request.Request(url=url, headers=headers) response = request.urlopen(req) print(response.read().decode())
2. urllib.error
请求异常主要有两类:URLError和HTTPError
URLError
该异常主要原因有:没有网络,服务器连接失败,找不到指定的服务器等等。我们可以用try except来捕获相应的异常
from urllib import request from urllib import error url = "https://www.xgrang23234234.com" try: res = request.urlopen(url) print(res.read().decode()) except error.URLError as err: print(err)
HTTPError
该异常是URLerror的子类(我们在捕获异常时需要将父类的异常写在子类的异常后面 ),我们发出一个请求时,服务器上都会对应一个 response应答对象,其中它包含一个数字"响应状态码"。
如果 urlopen 或 opener.open 不能处理的,会产生一个 HTTPError,对应相应的状态码,HTTP 状态码表示 HTTP 协议所返回的响应的状态。
注意,urllib 可以为我们处理重定向的页面(也就是 3 开头的响应码),100-299 范围的号码表示成功,所以我们只能看到 400-599 的错误号码。
from urllib import request from urllib import error url1 = "https://www.xgrang.com/1/index.index" # 会造成HTTPError的异常 url2 = "https://www.xgrang5435356.com" #会造成URLError的异常 try: res = request.urlopen(url1) print(res.read().decode()) except error.HTTPError as err: #父类在后,子类在前的原则 print("发生了一个HTTPError的异常:") print(err) except error.URLError as err: print("发生了一个URLError的异常:") print(err)
3. urllib.parse
python中的url提供了很多解析和组建URL的函数
urlparse() , 可以将 URL 解析成 ParseResult 对象。对象中包含了六个元素,分别为:
协议(scheme)
域名(netloc)
路径(path)
路径参数(params)
查询路径(query)
片段(fragment)
from urllib import parse url = "http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment" url_parse = parse.urlparse(url) print(url_parse.scheme) print(url_parse.netloc)
urlsplit() , 该函数与前一个函数不同的是不会将路径参数(params)从路径(path)分离出来。当出现多个路径参数是urlparse()会出现问题
from urllib import parse url = "http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment" url_parse = parse.urlparse(url) print(url_parse.params) # params2 print(url_parse.path) # /path1;params1/path2 url_parse2 = parse.urlsplit(url) print(url_parse2.path) # /path1;params1/path2;params2 # 很明显urlparse在解析具有多个路径参数是会出错
urlunparse()接收一个列表的参数,而且列表的长度是有要求的,是必须六个参数以上,否则抛出异常。
from urllib.parse import urlunparse url_compos = ('http', 'user:pwd@domain:80', '/path1;params1/path2', 'params2', 'query=queryarg', 'fragment') print(urlunparse(url_compos))
urljoin()将两个字符串拼接成url
from urllib.parse import urljoin # 连接两个参数的url, 将第二个参数中缺的部分用第一个参数的补齐,如果第二个有完整的路径,则以第二个为主 print(urljoin('https://movie.douban.com/', 'index')) print(urljoin('https://movie.douban.com/', 'https://accounts.douban.com/login'))
urlencode() 函数可以将一个 dict 转换成合法的查询参数,除一些字符以原字符表示外,其他的都会进行URL编码来表示。
from urllib.parse import urlencode query_args = { 'username': 'python你好', 'password': '123456' } query_args = urlencode(query_args) print(query_args) # username=python%E4%BD%A0%E5%A5%BD&password=123456
quote()对特殊字符进行URL编码,unquote()则相反。所以我们可以用这个来实现urlencode的效果
from urllib.parse import quote, unquote string = "你好" q_string = quote(string) uq_string = unquote(q_string) print(q_string) # %E4%BD%A0%E5%A5%BD print(uq_string) # 你好
其他
使用Cookie
一个 Web 站点可能会为每一个访问者产生一个唯一的ID, 然后以 Cookie 文件的形式保存在每个用户的机器上。如果使用浏览器访问 Web, 会看到所有保存在硬盘上的 Cookie。在这个文件夹里每一个文件都是一个由“键/值”对组成的文本文件,另外还有一个文件保存有所有对应的 Web 站点的信息
当客户端再次访问这个 Web 站点时这些信息可供该站点使用。由于“Cookie”具有可以保存在本地客户端上的神奇特性, 因此它可以帮助我们实现记录用户个人信息的功能
我们可以通过使用cookie避免一些不要登录认证或者来完善的伪装我们的爬虫
而管理cookie我们需要借助http.cookiejar库中的CookieJar,CookieJar类管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
该库使用CookieJar的步骤
创建CookieJar 对象或其子类的对象,负责处理cookie
以 CookieJar 对象为参数,创建 urllib.request.HTTPCookieProcessor 对象,该对象负责调用 CookieJar 来管理 cookie
以 HTTPCookieProcessor 对象为参数,调用 urllib.request.build_opener() 函数创建 OpenerDirector 对象
使用 OpenerDirector 对象来发送请求,该对象将会通过 HTTPCookieProcessor 调用 CookieJar 来管理 cookie
# 使用cookie登录人人网 import urllib.request import http.cookiejar def spider(url, cookie): headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36", "cookie": cookie } request = urllib.request.Request(url, headers=headers) cookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open(request) print(response.read().decode("utf8")) if __name__ == '__main__': url = "http://www.renren.com/" cookie = "111" #假cookie # cookie = "anonymid=kkg86whm-yk1bg4; depovince=GW; _r01_=1; taihe_bi_sdk_uid=c533a95f05616fb08f75377d3930ddce; _de=46FEFF8EAAD410B79011D95327F36536; __utma=151146938.1023465658.1611800234.1611800234.1611800234.1; __utmz=151146938.1611800234.1.1.utmcsr=renren.com|utmccn=(referral)|utmcmd=referral|utmcct=/; taihe_bi_sdk_session=2ee77ba5e0cbfabf28c21913cf66b58a; ick_login=069126e8-fb87-426c-bb9c-75c4f195c9a7; t=0a3e28f5f6c7b27379c00cccd65969820; societyguester=0a3e28f5f6c7b27379c00cccd65969820; id=975870750; xnsid=b3b06863; ver=7.0; loginfrom=null; wpsid=15896663617555; wp_fold=0; jebecookies=47e35a5a-8034-4680-bac2-3d63bdf289c6|||||" # 真cookie spider(url, cookie)
自动发送B站评论
# 发送b站弹幕 import urllib.request import http.cookiejar import urllib.parse def spider(Id, Message): url = "https://api.bilibili.com/x/v2/reply/add" data = { "oid": Id, # 你要发送的视频id "type": "1", "message": Message, # 你要发送到消息 "plat": "1", "ordering": "heat", "jsonp": "jsonp", "csrf": "44e011475a96e0d86c7ccad207f03508", } headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36", "cookie": "cookie" # 复制自己浏览器的cookie } data = urllib.parse.urlencode(data) request = urllib.request.Request(url=url, data=data.encode('utf8'), headers=headers) cookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open(request) print(response.read().decode()) if __name__ == '__main__': spider(Id="801326692", Message="阿七回来了!")
参考来源
https://www.cnblogs.com/-wenli/p/10894562.html
https://pujichun.gitee.io/2020/03/01/urllib%E5%BA%93%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/
https://blog.csdn.net/qq_29983883/article/details/105492143