1、基本使用
所谓网页抓取,就是把url地址中指定的网络资源从网络流中读取出来,保存到本地。
2、urlopen
urlopen的参数是一个url地址时
# 导入urllib.rquest库 from urllib import request # 向指定的url发送请求,并返回服务器响应的类文件对象 response = request.urlopen("https://www.baidu.com") # 类文件对象支持文件对象的操作方法,如read()方法读取文件全部内容,返回字符串 html = response.read() print(html)
3、Request
当增加http报头时,必须创建一个Request实例来作为urlopen()的参数,而需要访问的url地址则作为Request实例的参数。
from urllib import request # url作为Request()方法的参数,构造并返回一个Request对象 request_=request.Request("http://www.baidu.com") # Request对象作为urlopen()方法的参数,发送给服务器并接受响应 response=request.urlopen(request_) # 返回类文件对象 html = response.read().decode() print(html)
新建Request实例,除了必须要有url参数之外,还可以设置在另外两个参数:
(1)data(默认空):是伴随url提交的数据(比如要通过post提交的数据),同时http请求将从get方式改为post方式。
(2)headers(默认空):是一个字典,包含了需要发送的http报头的键值对。
4、User-Agent
浏览器是互联网世界上公认被允许访问网页的身份,如果我们希望我们的爬虫程序更像一个真实用户,那我们第一步,就是需要伪装成一个被公认的浏览器。用不同的浏览器在发送请求的时候,会有不同的User-Agent头。 urllib默认的User-Agent头为:Python-urllib/x.y
(x和y是Python主版本和次版本号,例如 Python-urllib/2.7)。
from urllib import request url = "https://www.baidu.com" headers = { "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", } # url连同headers,一起构造Request请求,这个请求将附带浏览器的user-agent request_ = request.Request(url,headers=headers) # 向服务器发送这个请求 response = request.urlopen(request_) html = response.read() print(html)
5、添加更多的Header信息
在HTTP Request中加入特定的Header,来构造一个完整的HTTP请求消息。
5.1 添加一个特定的header
from urllib import request headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", } url = "https://www.baidu.com" request_ = request.Request(url,headers=headers) # 可以通过调用Request.add_header()添加/修改一个特定的header request_.add_header("Connection","keep-alive") # 可以通过Request.get_header()来查看header信息 ret = request_.get_header(header_name="Connection") # print(ret) --->>>>>> keep-alive response = request.urlopen(request_) print(response.code) # 可以查看响应状态码 html = response.read().decode() print(html)
5.2 随机添加/修改User-Agent
User-Agent大全:http://useragentstring.com/pages/useragentstring.php
from urllib import request import random url = "https://www.baidu.com" # http://useragentstring.com/pages/useragentstring.php ua_list = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2919.83 Safari/537.36", "Mozilla/5.0 (X11; Ubuntu; Linux i686 on x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2820.59 Safari/537.36", ] user_agent = random.choice(ua_list) request_ = request.Request(url) # 通过Request.add_header()添加/修改一个特定的header request_.add_header("User-Agent",user_agent) # get_header()的字符串参数,第一个字母大写,后面的全部小写 ret = request_.get_header("User-agent") print(ret) response = request.urlopen(request_) print(response.read().decode())
6、urllib默认只支持HTTP/HTTPS的GET和POST方法
6.1 urllib.parse.urlencode()
编码工作使用urllib.parse的urlencode()函数,帮我们将key:value这样的键值对转换成"key=value"这样的字符串,解码工作可以使用urllib.parse的unquote()函数。
from urllib import request,parse word = { "wd":"人生" } # 通过urllib.urlencode()方法,将字典键值对按url编码转换,从而能被web服务器接受 ret = parse.urlencode(word) print(ret) # wd=%E4%BA%BA%E7%94%9F # 通过parse.unquote()方法,把url编码字符串,转换回原先字符串 ret_ = parse.unquote(ret) print(ret_) # wd=人生
一般HTTP请求提交数据,需要编码成url编码格式,然后作为url的一部分,或者作为参数传到Request对象中。
6.2 GET方式
GET请求一般用于我们向服务器获取数据,比如用百度搜索关键字
from urllib import request,parse url = "https://www.baidu.com/s?{}" word = { "wd":"学习" } word_ = parse.urlencode(word) # 转换成url编码格式(字符串) new_url = url.format(word_) print(new_url) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36", } request_ = request.Request(new_url,headers=headers) response = request.urlopen(request_) print(response.read())
6.3 POST方式
data是一个字典,里面是匹配的键值对。
发送POST请求时,需要特别注headers的一些属性:
(1)Content-Length: 144
: 是指发送的表单数据长度为144,也就是字符个数是144个。
(2)Content-Type: application/x-www-form-urlencoded
: 表示浏览器提交 Web 表单时使用,表单数据会按照 name1=value1&name2=value2 键值对形式进行编码。
(3)X-Requested-With: XMLHttpRequest
:表示Ajax异步请求。
6.4 获取AJAX加载的内容
有些网页内容使用AJAX加载,这种数据无法直接对网页url进行提取。
但是AJAX一般返回的是JSON,只要对AJAX地址进行POST或GET,就能返回JSON数据。
(1)GET方式是直接以链接形式访问,链接中包含了所有的参数,服务器端用Request.QueryString获取变量的值。如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。
(2)POST则不会在网址上显示所有的参数,服务器端用Request.Form获取提交的数据,在Form提交的时候。但是HTML代码里如果不指定 method 属性,则默认为GET请求,Form中提交的数据将会附加在url之后,以?
分开与url分开。
6.5 处理HTTPS请求SSL证书验证
遇到`URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)>`,需要单独处理SSL证书,让程序忽略SSL整数验证错误,即可正常访问。
import urllib # 1. 导入Python SSL处理模块 import ssl # 2. 表示忽略未经核实的SSL证书认证 context = ssl._create_unverified_context() url = "..." headers = {...} request = urllib.request.Request(url, headers = headers) # 3. 在urlopen()方法里 指明添加 context 参数 response = urllib.request.urlopen(request, context = context) print (response.read().decode())
6.6 关于CA
CA(Certificate Authority)是数字证书认证中心的简称,是指发放、管理、废除数字证书的受信任的第三方机构,如北京数字认证股份有限公司、上海市数字证书认证中心有限公司等...
CA的作用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行管理。
7、Handler处理器和自定义opener
(1)opener是urllib.request.OpenerDirector的实例,urlopen是一个特殊的opener(模块帮我们构建好的)
(2)基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。如果要支持这些功能,则需要做以下步骤:
1)使用相关的`Handler处理器`,来创建特定功能的处理器对象
2)通过`urllib.request.build_opener()`方法使用这些处理器对象,创建自定义`opener`对象
3)使用自定义的`opener`对象,调用`open()`方法发送请求
(3)如果程序里所有的请求都使用自定义的opener,可以使用`urllib.request.install_opener()`将自定义的`opener`对象定义为全局`opener`,表示如果之后凡是调用urlopen,都将使用这个`opener`。
7.1 简单的自定义opener()
from urllib import request # 构建一个HTTPHandler处理器对象,支持处理HTTP请求 http_handler = request.HTTPHandler() # 构建一个HTTPSHandler处理器对象,支持处理HTTPS请求 #https_handler = request.HTTPSHandler() # 调用urllib.request.build_opener()方法,创建支持处理HTTP请求的opener对象 opener = request.build_opener(http_handler) # 构建Request请求 request_ = request.Request('https://www.baidu.com') # 调用自定义opener对象的open()方法,发送request请求 response = opener.open(request_) # 获取服务器响应内容 print(response.read().decode())
这种方式发送请求得到的结果,和使用urllib.request.urlopen()发送HTTP/HTTPS请求得到的结果是一样的。
如果在 HTTPHandler()增加 debuglevel=1
参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时可以省去抓包的工作。
# 仅需要修改的代码部分: # 构建一个HTTPHandler 处理器对象,支持处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0 http_handler = urllib.request.HTTPHandler(debuglevel=1) # 构建一个HTTPSHandler 处理器对象,支持处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0 https_handler = urllib.request.HTTPSHandler(debuglevel=1)
7.2 ProxyHandler处理器(代理设置)
from urllib import request # 构建代理Hander HttpProxy_Handler = request.ProxyHandler({"http":"127.0.0.1:80"}) # 通过urllib.request.build_opener()方法使用这些代理Handler对象,创建自定义opener对象 opener = request.build_opener(HttpProxy_Handler) request_ = request.Request(url) # 此时只有使用opener.open()发送请求才使用自定义的代理,而urlopen()则不会使用代理 response = opener.open(request_) # 通过request.install_opener()应用到全局,之后不管是opener.open()还是urlopen()都能使用代理 request.install_opener(opener) response = urlopen(request_) print(response.read().decode())
7.3 Cookie
Cookie是指某些网站服务器为了辨别用户身份和进行Session跟踪,而存储在用户浏览器上的文本文件,Cookie可以保持登录信息到用户下次与服务器的会话。
7.3.1 Cookie原理
HTTP是无状态的面向连接的协议,为了保持连接状态,引入了Cookie机制,Cookie是http消息头中的一种属性,包括:
Cookie名字(Name) Cookie的值(Value) Cookie的过期时间(Expiers/Max-Age) Cookie作用路径(Path) Cookie所在域名(Domain) 使用Cookie进行安全连接(Secure)
前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不同的浏览器对Cookie个数及大小限制是有差异的)
Cookie有变量名和值组成,根据Netscape公司的规定,Cookie格式如下:
Set-Cookie: NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
7.3.2 Cookie应用
Cookies在爬虫方面最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续。
# 获取一个有登录信息的Cookie模拟登陆 import urllib # 1. 构建一个已经登录过的用户的headers信息 headers = { "Host":"www.renren.com", "Connection":"keep-alive", "Upgrade-Insecure-Requests":"1", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6", "Referer":"http://www.renren.com/SysHome.do", # 便于终端阅读,表示不支持压缩文件 # Accept-Encoding: gzip, deflate, sdch, # 重点:这个Cookie是保存了密码无需重复登录的用户的Cookie,这个Cookie里记录了用户名,密码(通常经过RAS加密) "Cookie": "anonymid=j3jxk555-nrn0wh; depovince=BJ; _r01_=1; JSESSIONID=abcnLjz9MSvBa-3lJK3Xv; ick=3babfba4-e0ed-4e9f-9312-8e833e4cb826; jebecookies=764bacbd-0e4a-4534-b8e8-37c10560770c|||||; ick_login=84f70f68-7ebd-4c5c-9c0f-d1d9aac778e0; _de=7A7A02E9254501DA6278B9C75EAEEB7A; p=91063de8b39ac5e0d2a57500de7e34077; first_login_flag=1; ln_uact=13146128763; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=39fca09219c06df42604435129960e1f7; societyguester=39fca09219c06df42604435129960e1f7; id=941954027; xnsid=8868df75; ver=7.0; loginfrom=null; XNESSESSIONID=a6da759fe858; WebOnLineNotice_941954027=1; wp_fold=0" } # 2. 通过headers里的报头信息(主要是Cookie信息),构建Request对象 urllib.request.Request("http://www.renren.com/941954027#", headers = headers) # 3. 直接访问renren主页,服务器会根据headers报头信息(主要是Cookie信息),判断这是一个已经登录的用户,并返回相应的页面 response = urllib.request.urlopen(request) # 4. 打印响应内容 print (response.read().decode())
但是这样做太过复杂,我们先需要在浏览器登录账户,并且设置保存密码,并且通过抓包才能获取这个Cookie。
7.3.4 Cookiejar库和HTTPCookieProcessor处理器
在python中处理Cookie,一般是通过cookiejar模块和urllib模块的HTTPCookieProcessor处理器类一起使用。
`Cookiejar模块:主要作用是提供用于存储cookie的对象`
`HTTPCookieProcessor处理器:主要作用是处理这些cookie对象,并构建handler对象`
7.3.5 cookiejar库
这个模块主要的对象有Cookiejar、FileCookieJar、MozillaCookieJar、LWPCookieJar。
CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。
from urllib import request import http.cookiejar # 构建一个Cookiejar对象实例来保存cookie #CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失 cookiejar_ = http.cookiejar.CookieJar() # 使用HTTPCookieProcessor()来创建cookie处理器对象,参数为cookiejar()对象 handler = request.HTTPCookieProcessor(cookiejar_) # 通过request.build_opener()来构建opener opener = request.build_opener(handler) # 以get方式访问页面,访问之后会自动保存cookie到cookiejar中 opener.open('http://www.baidu.com') # 按照标准格式将保存的Cookie打印出来 cookieStr = str() for item in cookiejar_: cookiejar = cookieStr+item.name+"="+item.value+";" print(cookieStr)
模拟登陆时要注意:
(1)登录一般都会先有一个HTTP GET,用于拉取一些信息及获得Cookie,然后再HTTP POST登录
(2)HTTP POST登录的链接有可能是动态的,从GET返回的信息中获取
(3)password 有些是明文发送,有些是加密后发送。有些网站甚至采用动态加密的,同时包括了很多其他数据的加密信息,只能通过查看JS源码获得加密算法,再去破解加密,非常困难。