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源码获得加密算法,再去破解加密,非常困难。