官方参考文档: https://docs.python.org/fr/3.6/library/urllib.html
在Python3.x
中,我们可以使用urlib
这个组件抓取网页,urllib
是一个URL
处理包,这个包中集合了一些处理URL的模块 。
1、urllib.request
模块是用来打开和读取URLs的。
2、urllib.error
模块包含一些有urllib.request
产生的错误,可以使用try进行捕捉处理。
3、urllib.parse
模块包含了一些解析URLs的方法。
4、urllib.robotparser
模块用来解析robots.txt
文本文件.它提供了一个单独的RobotFileParser
类,通过该类提供的can_fetch()
方法测试爬虫是否可以下载一个页面。
urllib.request.urlopen()
这个接口函数就可以很轻松的打开一个网站,读取并打印信息。
from urllib import request
if __name__ == "__main__":
response = request.urlopen("http://fanyi.baidu.com")
html = response.read()
print(html)
1、request.urlopen()
打开和读取URLs
信息,返回对象response
可以调用read(),进行读取。
2、print()
,将读到的信息打印出来。
3、其实这就是浏览器接收到的信息,只不过我们在使用浏览器的时候,浏览器已经将这些信息转化成了界面信息供我们浏览。
网页编码
虽然我们已经成功获取了信息,但是显然他们都是二进制的乱码 ,可以通过简单的decode()
命令将网页的信息进行解码 。
from urllib import request
if __name__ == "__main__":
response = request.urlopen("http://fanyi.baidu.com/")
html = response.read()
html = html.decode("utf-8")
print(html)
自动获取网页编码
chardet
,第三方库,用来判断编码的模块 ,使用chardet.detect()
方法,判断网页的编码方式 。
安装:pip install chardet
from urllib import request
import chardet
if __name__ == "__main__":
response = request.urlopen("http://fanyi.baidu.com/")
html = response.read()
charset = chardet.detect(html)
print(charset)
Request对象
反爬虫机制: 1、封杀爬虫程序 。2、封杀指定IP 。3、 封杀非人操作的程序 。
如果需要执行更复杂的操作,比如增加 HTTP
报头,必须创建一个 Request
实例来作为urlopen()
的参数;而需要访问的 url
地址则作为 Request
实例的参数。
from urllib import request
if __name__ == "__main__":
req = request.Request("http://fanyi.baidu.com/")
response = request.urlopen(req)
html = response.read()
html = html.decode("utf-8")
print(html)
User Agent
浏览器就是互联网世界上公认被允许的身份,如果我们希望我们的爬虫程序更像一个真实用户,那我们第一步,就是需要伪装成一个被公认的浏览器。用不同的浏览器在发送请求的时候,会有不同的 User-Agent
头。中文名为用户代理,简称UA
,User Agent
存放于Headers
中 ,服务器就是通过查看Headers
中的User Agent
来判断是谁在访问,urllib
中默认的User Agent
,会有Python
的字样,如果服务器检查User Agent
,可以拒绝Python
程序访问网站。
常见浏览器 User-Agent: https://blog.csdn.net/Kwoky/article/details/80381246
,
设置User Agent
方法 1、在创建 Request
对象的时候,填入 headers
参数(包含 User Agent
信息),这个Headers
参数要求为字典。
方法2、:在创建Request
对象的时候不添加headers
参数,在创建完成之后,使用add_header()
的方法,添加headers
。
代码:
from urllib import request
if __name__ == "__main__":
#以 CSDN 为例,CSDN 不更改 User Agent 是无法访问的
url = 'http://www.csdn.net/'
head = {}
#写入 User Agent 信息
head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
#创建 Request 对象
req = request.Request(url, headers=head)
# 也可以通过调用 Request.add_header() 添加/修改一个特定的 header
# req.add_header("User-Agent", "Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19")
req.add_header("Connection", "keep-alive")
# 也可以通过调用 Request.get_header()来查看 header 信息
print(req.get_header(header_name="Connection"))
print(req.get_header(header_name="User-Agent"))
#传入创建好的 Request 对象
response = request.urlopen(req)
#读取响应信息并解码
html = response.read().decode('utf-8')
#打印信息
print(html)
IP代理
使用ip
代理之后访问网站,可避免本机的ip
暴露:
代理分类 ,代理可以分为三种,即高度匿名代理、普通匿名代理和透明代理。
1、高度匿名代理 :隐藏客户的真实IP,但是不改变客户机的请求,就像有个真正的客户浏览器在访问服务器。
2、普通匿名代理 :能隐藏客户机的真实 IP
,会改变客户的请求信息,服务器端不知道你的 ip
地址但有可能认为我们使用了代理。
3、透明代理 :不但改变了我们的请求信息,还会传送真实的IP地址。
爬虫程序运行速度是很快,在网站爬取数据时,一个固定IP的访问频率就会很高,这不符合人为操作的标准。所以一些网站会设置一个IP访问频率的阈值,如果一个IP访问频率超过这个阈值,说明这个不是人在访问,而是一个爬虫程序。
解决办法一: 设置延时 。
解决办法二:使用 IP 代理。可以设置一些代理服务器,每隔一段时间换一个代理,就算 IP被禁止,依然可以换个IP继续爬取。
免费短期代理网站举例:
1、西刺免费代理IP http://www.xicidaili.com/
。
2、快代理免费代理 http://www.kuaidaili.com/free/inha/
。
3、聚合数据 https://www.juhe.cn/docs/api/id/62
。
代理IP选取
西刺网站为例,选出信号好的IP
使用代理的步骤:
1、调用urlib.request.ProxyHandler()
,构建处理器对象,proxies
参数为一个字典。
class urllib.request.ProxyHandler(proxy=None)
2、创建Opener对象
urllib.request.build_opener([handler,...])
3、安装Opener
urllib.request.install_opener(opener)
使用 install_opener
方法之后,会将程序默认的 urlopen
方法替换掉。也就是说,如果使用install_opener
之后,在该文件中,再次调用urlopen
会使用自己创建好的opener
from urllib import request
if __name__ == "__main__":
# 访问网址
# url = 'http://www.baidu.com/'
url='http://ip.27399.com/’
# 这是代理 IP
proxy = {'http':'113.92.156.185:808'}
# 创建 ProxyHandler
proxy_support = request.ProxyHandler(proxy)
opener = request.build_opener(proxy_support)
# 添加 User Angent
opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36')]
# 安装 OPener
request.install_opener(opener)
# 使用自己安装好的 Opener
response = request.urlopen(url)
# 读取相应信息并解码
html = response.read().decode("utf-8")
#打印信息
print(html)
url编码解码
urllib.parse.urlencode()
函数帮我们将key:value
这样的键值对转换成"key=value
"这样的的字符串,解码工作可以使用urllib的unquote()
函数。
from urllib import parse
word = {"title" : "天猫商城"}
url1 = parse.urlencode(word)
print(url1)
url2 =parse.unquote(url1)
(url1)
GET请求
GET 请求一般用于向服务器获取数据 ,案例:
from urllib import request
from urllib import parse
import chardet
url = "http://www.baidu.com/s"
word = {"wd":"端午节"}
# 转换成 url 编码格式(字符串)
word = parse.urlencode(word)
# url 首个分隔符就是 ?
newurl = url + "?" + word
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"}
req = request.Request(newurl, headers=headers)
response = request.urlopen(req)
html = response.read()
charset = chardet.detect(html)['encoding']
print(charset)
print(html.decode(charset))
POST请求
urlopen
的data
参数,是POST
方法进行Request
请求时,要传送给服务器的数据,字典类型,里面要匹配键值对。
如果没有设置urlopen()
函数的data参数,HTTP请求采用GET方式 。
使用urllib.parse.urlencode()
函数将发送的表单数据编码 。
案例:向有道翻译发送data,得到翻译结果 。
from urllib import request
from urllib import parse
import json
if __name__ == "__main__":
# 对应上图的 Request URL 为避免{"errorCode":50}的错误,去除 url 中的_o
# Request_URL = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
Request_URL = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
# 创建 Form_Data 字典,存储上图的 Form Data
Form_Data = {}
Form_Data['i'] = 'Tom'
Form_Data['from'] = 'AUTO'
Form_Data['to'] = 'AUTO'
Form_Data['smartresult'] = 'dict'
Form_Data['client'] = 'fanyideskweb'
Form_Data['salt'] = '1526796477689'
Form_Data['sign'] = 'd0a17aa2a8b0bb831769bd9ce27d28bd'
Form_Data['doctype'] = 'json'
Form_Data['version'] = '2.1'
Form_Data['keyfrom'] = 'fanyi.web'
Form_Data['action'] = 'FY_BY_REALTIME'
Form_Data['typoResult'] = 'false'
# 使用 urlencode 方法转换标准格式
data = parse.urlencode(Form_Data).encode('utf-8')
head = {}
# 写入 User Agent 信息
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
# 创建 Request 对象
req = request.Request(Request_URL, headers=head)
# 传递 Request 对象和转换完格式的数据
response = request.urlopen(req,data=data)
# 读取信息并解码
html = response.read().decode('utf-8')
# 使用 JSON
translate_results = json.loads(html)
# 找到翻译结果
translate_results = translate_results['translateResult'][0][0]['tgt']
# 打印翻译信息
print("翻译的结果是:%s" % translate_results)
Cookie
HTTP
是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie
机制, 在浏览器中存储用户信息 。
应用:Cookie模拟登陆
获取一个有登录信息的 Cookie 模拟登陆
from urllib import request
import chardet
# 1. 构建一个已经登录过的用户的 headers 信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.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.9",
"Cache-Control": "max-age=0",
"Connection":"keep-alive",
# 重点:这个 Cookie 是保存了密码无需重复登录的用户的 Cookie,这个 Cookie 里记录 了用户名,密码(通常经过 RAS 加密)
"Cookie": "__cfduid=d813c9add816556c64a8c087554cfb7af1508468882; BDUSS=kM2UnphaUpJRVphdjFrQ3R0TX5KM1NhY25mLVFmdTUtbTJ2ZndiMkw1TWRYRkJiQUFBQ UFBJCQAAAAAAAAAAAEAAABUN0cMVG9temt5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3PKFsdzyhba; BAIDUID=675B04E847A8A586ECC76203C511EA12:FG=1; PSTM=1529894757; BD_UPN=12314753; BIDUPSID=269D2DF8A150308A1A953FED45517BA8; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BD_CK_SAM=1; PSINO=1; H_PS_PSSID=1428_21106_20927; BD_HOME=1",
"Host":"www.baidu.com",
"Upgrade-Insecure-Requests":"1",
}
# 2. 通过 headers 里的报头信息(主要是 Cookie 信息),构建 Request 对象
req = request.Request("https://www.baidu.com/", headers = headers)
# 3. 直接访问 renren 主页,服务器会根据 headers 报头信息(主要是 Cookie 信息),判断这 是一个已经登录的用户,并返回相应的页面
response = request.urlopen(req)
# 4. 打印响应内容
html = response.read()
charset = chardet.detect(html)['encoding']
print(charset)
print(html.decode(charset))
cookielib
库 和 HTTPCookieProcessor
处理器
Python
处理 Cookie
,一般是通过 cookielib
模块和 urllib
的 request
模块的HTTPCookieProcessor
处理器类一起使用。
cookielib
模块:主要作用是提供用于存储cookie
的对象 。
HTTPCookieProcessor
处理器:主要处理cookie
对象,并构建handler
对象。
cookielib
库
主要作用是提供用于存储cookie
的对象 。
该模块主要的对象有CookieJar
、FileCookieJar
、MozillaCookieJar
、LWPCookieJar
。
CookieJar
:管理HTTP cookie
值、存储HTTP
请求生成的cookie
、向传出的HTTP
请求添加cookie
的对象。整个 cookie
都存储在内存中。
FileCookieJar (filename,delayload=None,policy=None)
:CookieJar
派生而来,将 cookie
存储到 文件中。filename
是存储 cookie
的文件名。delayload
为 True
时支持延迟访问文件,即只有 在需要时才读取文件或在文件中存储数据。
MozillaCookieJar (filename,delayload=None,policy=None)
:从 FileCookieJar
派 生 而 来 , MozillaCookieJar
实例与 Mozilla
浏览器 cookies.txt
兼容。
LWPCookieJar (filename,delayload=None,policy=None)
:从 FileCookieJar
派生而来,实例与 libwww-perl
标准的 Set-Cookie3
文件格式兼容。
HTTPCookieProcessor
处理器:处理cookie
对象,并构建handler
处理器对象。
案例:获取Cookie
,生成CookieJar()
对象中 。
from urllib import request
import http.cookiejar
# 构建一个 CookieJar 对象实例来保存 cookie
cookiejar = http.cookiejar.CookieJar()
# 使用 HTTPCookieProcessor()来创建 cookie 处理器对象,参数为 CookieJar()对象
handler=request.HTTPCookieProcessor(cookiejar)
# 通过 build_opener() 来构建 opener
opener = request.build_opener(handler)
# 4. 以 get 方法访问页面,访问之后会自动保存 cookie 到 cookiejar 中
opener.open("https://www.baidu.com")
# 可以按标准格式将保存的 Cookie 打印出来
cookieStr = ""
for item in cookiejar:
cookieStr = cookieStr + item.name + "=" + item.value + ";"
# 舍去最后一位的分号
print(cookieStr[:-1])
案例:获得网站cookie
,保存在cookie
文件中 。
import http.cookiejar
from urllib import request
# 保存 cookie 的本地磁盘文件名
filename = 'cookie.txt'
#1. 声明一个 MozillaCookieJar(有 save 实现)对象实例来保存 cookie,之后写入文件
cookiejar = http.cookiejar.MozillaCookieJar(filename)
#2. 使用 HTTPCookieProcessor()来创建 cookie 处理器对象,参数为 CookieJar()对象
handler = request.HTTPCookieProcessor(cookiejar)
#3. 通过 build_opener() 来构建 opener
opener = request.build_opener(handler)
#4. 创建一个请求,原理同 urllib2 的 urlopen
response = opener.open("http://www.baidu.com")
#5. 保存 cookie 到本地文件
cookiejar.save()
案例:从文件中获取cookies
,做为请求的一部分 。
from urllib import request
import http.cookiejar
import chardet
# 创建 MozillaCookieJar(有 load 实现)实例对象
cookiejar = http.cookiejar.MozillaCookieJar()
# 从文件中读取 cookie 内容到变量
cookiejar.load('cookie.txt')
# 使用 HTTPCookieProcessor()来创建 cookie 处理器对象,参数为 CookieJar()对象
handler = request.HTTPCookieProcessor(cookiejar)
#通过 build_opener() 来构建 opener
opener = request.build_opener(handler)
response = opener.open("https://www.baidu.com")
# 4. 打印响应内容
html = response.read()
charset = chardet.detect(html)['encoding']
print(charset)
print(html.decode(charset))
案例:利用cookielib
和post
登录人人网
from urllib import request
from urllib import parse
import http.cookiejar
# 1. 构建一个 CookieJar 对象实例来保存 cookie
cookie = http.cookiejar.CookieJar()
# 2. 使用 HTTPCookieProcessor()来创建 cookie 处理器对象,参数为 CookieJar()对象
cookie_handler = request.HTTPCookieProcessor(cookie)
# 3. 通过 build_opener() 来构建 opener
opener = request.build_opener(cookie_handler)
# 4. addheaders 接受一个列表,里面每个元素都是一个 headers 信息的元祖, opener 将附带 headers 信息
opener.addheaders = [("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")]
# 5. 需要登录的账户和密码
data = {"email":"123@163.com", "password":"123"}
# 6. 通过 urlencode()转码
postdata = parse.urlencode(data).encode(encoding='UTF8')
print(postdata)
# 7. 构建 Request 请求对象,包含需要发送的用户名和密码
req = request.Request("http://www.renren.com/PLogin.do", data = postdata)
# 8. 通过 opener 发送这个请求,并获取登录后的 Cookie 值,
opener.open(req)
# 9. opener 包含用户登录后的 Cookie 值,可以直接访问那些登录后才可以访问的页面
response = opener.open("http://www.renren.com/966745694/profile")
# 10. 打印响应内容
print(response.read().decode())
获取AJAX加载的内容
AJAX一般返回的是JSON
,直接对AJAX地址进行post
或get
,就返回JSON
数据了。
from urllib import request
from urllib import parse
url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action"
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"}
# 变动的是这两个参数,从 start 开始往后显示 limit 个
formdata = {'start':'0', 'limit':'10' }
data = parse.urlencode(formdata).encode(encoding='UTF8')
req = request.Request(url, data = data, headers = headers)
response = request.urlopen(req)
print(response.read().decode('utf-8'))
HTTPS
请求 SSL
证书验证
现在随处可见 https
开头的网站,urllib2
可以为 HTTPS
请求验证 SSL
证书,就像 web浏览器一样,如果网站的 SSL
证书是经过 CA
认证的,则能够正常访问,如:https://www.baidu.com/
等…
如果 SSL
证书验证不通过,或者操作系统不信任服务器的安全证书,比如浏览器在访问12306
网站如:https://www.12306.cn/mormhweb/
的时候,会警告用户证书不受信任。
urllib
在访问:
from urllib import request
url = "https://www.12306.cn/mormhweb/"
headers = {"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"}
req = request.Request(url, headers = headers)
response = request.urlopen(req)
print(response.read())
会报出SSLError
如果以后遇到这种网站,我们需要单独处理 SSL
证书,让程序忽略 SSL
证书验证错误,即可正常访问。
from urllib import request
# 1. 导入 Python SSL 处理模块
import ssl
# 2. 表示忽略未经核实的 SSL 证书认证
context = ssl._create_unverified_context()
url = "https://www.12306.cn/mormhweb/"
headers = {"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"}
req = request.Request(url, headers = headers)
response = request.urlopen(req,context=context)
print(response.read().decode('utf8'))
URLError
urllib.error
可以接收有urllib.request
产生的异常。
产生的原因主要有:
1、没有网络连接
2、服务器连接失败
3、找不到指定的服务器
HTTPError
HTTPError
是 URLError
的子类,我们发出一个请求时,服务器上都会对应一个 response
应答对象,其中它包含一个数字"响应状态码"
如果 urlopen
不能处理的,会产生一个 HTTPError
,对应相应的状态码,HTTP
状态码表示HTTP
协议所返回的响应的状态。
混合使用
如果想用 HTTPError
和 URLError
一起捕获异常,那么需要将 HTTPError
放在 URLError
的前面,因为HTTPError
是URLError
的一个子类。如果URLError
放在前面,出现HTTP
异常会先响应URLError
,这样HTTPError
就捕获不到错误信息了。
from urllib import request
from urllib import error
req = request.Request("http://www.douyu.com/tom_kwok.html")
try:
request.urlopen(req)
except error.HTTPError as err:
print(err.code)
except error.URLError as err:
print(err)
else:
print("Good Job")
状态码 :
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息–表示请求已接收,继续处理
2xx:成功–表示请求已被成功接收、理解、接受
3xx:重定向–要完成请求必须进行更进一步的操作
4xx:客户端错误–请求有语法错误或请求无法实现
5xx:服务器端错误–服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200 OK //客户端请求成功
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的 URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常 。