您的位置:首页 > 编程语言 > Python开发

03—小白学Python爬虫之urllib的基本和进阶使用及Get、Post示例

2018-03-06 20:12 1006 查看
urllib库官方文档地址:https://docs.python.org/3/library/urllib.html

urllib简介

概述

urllib是python内置的HTTP请求库。

版本

python2.X —> urllib和urllib2

python3.X —> urllib

变化

在Pytho2.x中使用import urllib2——-对应的,在Python3.x中会使用import urllib.request,urllib.error

在Pytho2.x中使用import urllib——-对应的,在Python3.x中会使用import urllib.request,urllib.error,urllib.parse

在Pytho2.x中使用import urlparse——-对应的,在Python3.x中会使用import urllib.parse

在Pytho2.x中使用import urlopen——-对应的,在Python3.x中会使用import urllib.request.urlopen

在Pytho2.x中使用import urlencode——-对应的,在Python3.x中会使用import urllib.parse.urlencode

在Pytho2.x中使用import urllib.quote——-对应的,在Python3.x中会使用import urllib.request.quote

在Pytho2.x中使用cookielib.CookieJar——-对应的,在Python3.x中会使用http.CookieJar

在Pytho2.x中使用urllib2.Request——-对应的,在Python3.x中会使用urllib.request.Request

urllib基本使用

接下来示例代码都是以python3.X中使用的urllib

模块

urllib.request 请求模块

urllib.error 异常处理模块

urllib.parse url解析模块

urllib.robotparser robots.txt解析模块

urlopen

方法参数:

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)


先来个入门示例:

from urllib import request

def demo():
"""
直接使用urlopen打开网页,返回结果可以作为一个文件对象进行操作
:return:
"""
with request.urlopen('http://www.baidu.com') as f:
# 读取返回的数据
data = f.read()
# 请求code码 200 404 500 403
print('code:', f.getcode())
# 返回实际数据的URL
print('url:', f.geturl())
# 服务器返回的http报头信息
print('info:', f.info())
print('Status:', f.status, f.reason)
for k, v in f.getheaders():
print('%s: %s' % (k, v))
print('Data:', data.decode('utf-8'))

if __name__ == '__main__':
demo()


如上,短短几行代码,就可以把baidu首页面的信息抓取下来。

构建urlrequest.Request对象

除了上面直接使用urlopen传入url打开网页,还可以通过构造Request对象来完成

def demo2():
"""
构建一个request对象,URL作为构造参数传入
:return:
"""
req = request.Request('http://www.baidu.com')
resp = request.urlopen(req)
print('resp:', resp.read().decode('utf-8'))


Header

上篇文章说过request header相关信息,在header中,使用最多的就是User-Agent,有些网页为了防止别人恶意采集信息而做了一些反爬虫的设置,而我们在爬取时可以通过设置UA来模拟成浏览器来访问。

def demo3():
headers = {
'User-Agent': 'Mozilla/5.0'
}
req = request.Request('http://www.baidu.com', headers=headers)
resp = request.urlopen(req)
print(resp.read().decode('utf-8'))


data参数-区别post和get

urlopen中,data参数如果为空,代表get请求,如果不为空,则代表post请求。

其中post数据,需要进行urlencode,如data=bytes(parse.urlencode(data)

示例:

def demo3():
"""
urlopen方法中增加data参数代表是一个post请求
设置headers,比如UA
:return:
"""
req = request.Request('http://www.baidu.com')
req.add_header('User-Agent', 'Mozilla/5.0')
data = {
'name': 'wangcai'
}
resp = request.urlopen(req, data=bytes(parse.urlencode(data), encoding='utf-8'))
print(resp.read().decode('utf-8'))


timeout

有时候,网络不好或者服务器响应比较慢 请求异常等,需要给请求设置一个超时时间,及时做出反应,而不是让程序无限制等待。

def demo4():
resp = request.urlopen("http://www.baidu.com", timeout=0.01)
print(resp.read().decode('utf-8'))


执行如上代码,会抛出如下异常:

File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/urllib/request.py", line 1321, in do_open
r = h.getresponse()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", line 1331, in getresponse
response.begin()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/http/client.py", line 258, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/socket.py", line 586, in readinto
return self._sock.recv_into(b)
socket.timeout: timed out


so,需要对异常进行处理,修改代码如下:

def demo4():
try:
resp = request.urlopen("http://www.baidu.com", timeout=0.001)
print(resp.read().decode('utf-8'))
except error.URLError as e:
if isinstance(e.reason, socket.timeout):
print('timeout , stop')


输入结果为:

timeout , stop


如上,超时异常就拦截了,可以做自己的相关处理。

自定义opener

opener是 urllib2.OpenerDirector 的实例,我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构建好的)。

但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高级功能。所以要支持这些功能:

使用相关的 Handler处理器 来创建特定功能的处理器对象;

然后通过 urllib.request.build_opener()方法使用这些处理器对象,创建自定义opener对象;

使用自定义的opener对象,调用open()方法发送请求。

如果程序里所有的请求都使用自定义的opener,可以使用urllib.request.install_opener() 将自定义的 opener 对象 定义为 全局opener,表示如果之后凡是调用urlopen,都将使用这个opener(根据自己的需求来选择)

示例:

from urllib import request

def demo():
# 构建一个HTTPHandler处理器对象,支持HTTP请求
http_handler = request.HTTPHandler()
# 调用urllib.request的build_opener方法,创建支持http请求的opener对象
opener = request.build_opener(http_handler)
# 构建request请求
req = request.Request("http://www.baidu.com")
# 使用自定义opener对象的open方法,发送request请求
response = opener.open(req)
# 获取server响应
print(response.read().decode('utf-8'))

if __name__ == '__main__':
demo()


如果在 HTTPHandler()增加 debuglevel=1参数,还会将 Debug Log 打开,这样程序在执行的时候,会把收包和发包的报头在屏幕上自动打印出来,方便调试,有时可以省去抓包的工作。

# 构建一个HTTPHandler 处理器对象,支持处理HTTP请求,同时开启Debug Log,debuglevel 值默认 0
http_handler = urllib2.HTTPHandler(debuglevel=1)

# 构建一个HTTPSHandler 处理器对象,支持处理HTTPS请求,同时开启Debug Log,debuglevel 值默认 0
https_handler = urllib2.HTTPSHandler(debuglevel=1)


Handler处理器

很多网站会检测某一段时间某个IP的访问次数(通过流量统计 系统日志等方法),如果监测不正常的话,那就会封掉这个IP的访问。这个时候,我们可以通过设置代理服务器,定时更换IP进行爬取解决。

示例一:

def handler_demo():
# 构建两个Handler,一个有代理IP,一个无
httpproxy_handler = request.ProxyHandler({"http": "192.168.1.1:80"})
nullproxy_handler = request.ProxyHandler()
# 通过build_opener方法使用Handler代理对象,创建自定义opener
opener = request.build_opener(httpproxy_handler)
# 构建request请求
req = request.Request("http://www.baidu.com")
# 使用代理,只有opener.open才会生效,urlopen无效
response = opener.open(req)
print(response.read().decode('utf-8'))
# 如果使用request.install_opener(opener),那么不管使用opener.open()还是urlopen()都将使用代理


示例二:

def handler_demo2():
"""
代理足够多,可以随机选择一个去访问
:return:
"""
proxy_list = [
{"http": "101.200.24.123:80"},
{"http": "101.200.24.123:80"},
{"http": "101.200.24.123:80"},
{"http": "101.200.24.123:80"},
]
proxy = random.choice(proxy_list)
handler = request.ProxyHandler(proxy)
opener = request.build_opener(handler)
req = request.Request("http://www.baidu.com")
response = opener.open(req)
print(response.read().decode('utf-8'))


HTTPPasswordMgrWithDefaultRealm

HTTPPasswordMgrWithDefaultRealm

创建一个密码管理对象,用来保存HTTP请求相关的用户名和密码。

主要两个场景:

1. 验证代理授权的用户名和密码(proxyBasicAuthHandler)

2. 验证web客户端的用户名和密码(HttpBasicAuthHandler)

ProxyBasicAuthHandler(代理授权验证)

如果我们使用之前的代码来使用私密代理,会报 HTTP 407 错误,表示代理没有通过身份验证:

HTTPError: HTTP Error 407: Proxy Authentication Required


所以我们需要改写代码,通过:

HTTPPasswordMgrWithDefaultRealm():来保存私密代理的用户密码

ProxyBasicAuthHandler():来处理代理的身份验证。

示例:

def pwd_demo():
# 私密代理授权的账户 密码
user = 'demo'
pwd = '1234'
# 私密代理IP
proxy_server = '119.129.99.29:1231'
# 构建一个密码管理对象,保存用户名和密码
pwdmgr = request.HTTPPasswordMgrWithDefaultRealm()
# 添加账户信息
pwdmgr.add_password(None, proxy_server, user=user, passwd=pwd)
# 构建一个代理基础用户名/密码验证的ProxyBasicAuthHandler对象,参数是创建的密码管理对象
proxy_handler = request.ProxyBasicAuthHandler(pwdmgr)
# 通过build_opener方法使用Handler创建自定义opener
opener = request.build_opener(proxy_handler)
# 构造request请求
req = request.Request("http://www.baidu.com")
# 发送请求并打印响应
response = opener.open(req)
print(response.read().decode('utf-8'))


HTTPBasicAuthHandler处理器(Web客户端授权验证)

有些Web服务器(包括HTTP/FTP等)访问时,需要进行用户身份验证,爬虫直接访问会报HTTP 401 错误,表示访问身份未经授权:

HTTPError: HTTP Error 401: Unauthorized


如果我们有客户端的用户名和密码,我们可以通过下面的方法去访问爬取:

def pwd_demo2():
# 私密代理授权的账户 密码
user = 'wangcai'
pwd = '12345qwertyui'
# web客户端IP
web_server = '10.0.2.101:80'
# 构建一个密码管理对象,保存用户名和密码
pwdmgr = request.HTTPPasswordMgrWithDefaultRealm()
# 添加账户信息
pwdmgr.add_password(None, web_server, user=user, passwd=pwd)
# 构建一个代理基础用户名/密码验证的HttpBasicAuthHandler对象,参数是创建的密码管理对象
proxy_handler = request.HTTPBasicAuthHandler(pwdmgr)
# 通过build_opener方法使用Handler创建自定义opener
opener = request.build_opener(proxy_handler)
# 构造request请求
req = request.Request("http://www.baidu.com")
# 发送请求并打印响应(requesr.install_opener()执行后,全局生效)
response = opener.open(req)
print(response.read().decode('utf-8'))


异常处理

概念

在访问网页的过程中,可能会出现一些异常,比如404 500 超时等,程序需要对这些信息做异常处理

分类

URLError 只有一个属性reason,即异常发生的时候只能打印错误信息

HTTPError 是URLError的子类,有三个属性,code reason headers三个信息

示例

示例一(URLError)

def demo():
try:
resp = request.urlopen("http://www.baiduasw.com")
print(resp.read().decode('utf-8'))
except error.URLError as e:
print(e.reason)


输出为:

[Errno 8] nodename nor servname provided, or not known


示例二(HTTPError)

def demo2():
try:
resp = request.urlopen("http://www.csdn.net/abcs.html")
print(resp.read().decode('utf-8'))
except error.HTTPError as e:
print(e.reason, e.code, e.headers)
except error.URLError as e:
print(e.reason)
else:
print('success')


输出结果为:

Not Found 404 Server: openresty
Date: Tue, 06 Mar 2018 12:06:58 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 4144
Connection: close
Vary: Accept-Encoding
Set-Cookie: uuid_tt_dd=10_18754355060-1520338018722-773220; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1520338018722.227796; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
ETag: "5a324c78-1030"


通过reason做异常分析

可以通过捕获到的异常reason进行分析,比如超时,示例如下 :

def demo3():
try:
request.urlopen("http://www.baidu.com", timeout=0.001)
except error.URLError as e:
print(type(e.reason))
if isinstance(e.reason, socket.timeout):
print('连接超时')


输出结果为:

<class 'socket.timeout'>
连接超时


Cookie

概念

指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据

可以保持用户登录信息,与服务端进行通信

Cookie原理

HTTP是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie机制 Cookie是http消息头中的一种属性,包括:

Cookie名字(Name)
Cookie的值(Value)
Cookie的过期时间(Expires/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


Cookie应用

Cookies在爬虫方面最典型的应用是判定用户是否已经登录,如果登录,在后续访问网站其他页面,需要携带server返回的cookie数据,这样就不需要重复登录了。

示例:

from urllib import request
import http.cookiejar
import ssl

def demo():
# 通过Chrome开发者工具获取到的headers信息
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': ':zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
# 如果没有这个数据,会获取不到内容
'Cookie': 'SINAGLOBAL=14451465401860.67.1489663918038; UOR=,,www.cidu.com.cn; ULV=15126380274353:39:2:1:654449358996.8848.15312638074260:1512122654080; YF-Page-G0=3d55e256bde550ac7b0d32a2ad7d6fa53; __ln4krntdmcvrd=-1; login_sid_t=764ees242ec6c2c671008097fe81d9776; cross_origin_praoto=SSL; YF-Ugrow-G0=ad8a3bc19dc1269e709f753b172bddb094; YF-V5-G0=020421dd53s5a1c903e89d913fb8a2988; WBStorage=c5ff51a335af29d81|undefined; WBtopGlobal_register_version=d7a77880fa9c5f84; SCF=ApB-0Hdxe1pH2EVUwj6T7lnFDhHVmpTrGutTGUCBqAxNpYokCZKUXDXjFb7jnhhuX52vPbRLRqP7Q1qC5LF6HDE.; SUB=_2A253mhxmDeRhGeVG71AR-SjEzz-IHXVU7gqurDV8asdasPUNbmtANLUOjkW9NT7lHG3qdhhmQlBxLcF_jvJiQRxBGY_6P; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9Wh1676ff2MDLHTsnQwjrekN9-b5JpX5KzhUgL.FoeRShadaz71KqRShe2dJLoIEXLxK-LBo5L12235qLxKqLBoBL12zLxKBLB.2LB.2LxKqL1-eL1hWQqPW7wKz7eKnt; SUHB=0FQbL0-bhmNpOg; ALF=1551867826; SSOLoginState=1520331826; wvr=6; wb_timefeed_3842096843=1',
'Host': 'weibo.com',
'Referer': 'https://weibo.com/u/3842096843/home?leftnav=1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'ozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36',
}
url = 'https://weibo.com/fav?leftnav=1&pids=plc_main&ajaxpagelet=1&ajaxpagelet_v6=1&__ref=%2Fu%2F3842096843%2Fhome%3Fleftnav%3D1&_t=FM_152033182791241'
# 构建request对象
req = request.Request(url, headers=headers)
# 不验证https
context = ssl._create_unverified_context()
# 发送请求
response = request.urlopen(req, context=context)
# 打印响应内容
print(response.read().decode('utf-8'))

if __name__ == '__main__':
demo()


如上,通过查看控制台,就能发现确实返回我的收藏内容,此处略。

但是这样过于复杂,需要先登录页面,通过工具抓包才能拿到cookie信息,那么有木有简单一点的呢?当然是有喽~

cookielib库和HTTPCookiePorcessor处理器

在Python处理Cookie,一般是通过cookielib模块和 urllib模块的HTTPCookieProcessor处理器类一起使用。

cookielib模块:主要作用是提供用于存储cookie的对象

HTTPCookieProcessor处理器:主要作用是处理这些cookie对象,并构建handler对象。


cookielib库简介

该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。

CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。

FileCookieJar (filename,delayload=None,policy=None):从CookieJar派生而来,用来创建FileCookieJar实例,检索cookie信息并将cookie存储到文件中。filename是存储cookie的文件名。delayload为True时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。

MozillaCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与Mozilla浏览器 cookies.txt兼容的FileCookieJar实例。

LWPCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生而来,创建与libwww-perl标准的 Set-Cookie3 文件格式兼容的FileCookieJar实例。

大多数情况下,我们只用CookieJar(),如果需要和本地文件交互,就用 MozillaCookjar() 或 LWPCookieJar()

示例一(获取Cookie保存到CookieJar对象中)

from urllib import request
import http.cookiejar
import ssl

def demo2():
"""
获取cookie,并保存在cookieJar对象中
:return:
"""
# 构建cookieJar对象来保存cookie
cookie_jar = http.cookiejar.CookieJar()
# 使用httpCookiePorcessor创建Cookie处理器对象,参数为cookieJar
http_cookie_processor = request.HTTPCookieProcessor(cookiejar=cookie_jar)
# 构建opener
opener = request.build_opener(http_cookie_processor)
# 构建request
req = request.Request("http://www.baidu.com")
# 发送请求
response = opener.open(req)
# 打印cookie信息
for item in cookie_jar:
print(item.name + '=' + item.value)
print('done')


输出结果为:

BAIDUID=88BA4BF20ED5C2838A1C96CE468B767B:FG=1
BIDUPSID=88BA4BF20ED5C2838A1C96CE468B767B
H_PS_PSSID=1435_21106_17001_20718
PSTM=1520334302
BDSVRTM=0
BD_HOME=0
done


示例二(获取Cookie,并保存到本地文件中)

def demo3():
"""
获取cookie,并保存到cookie文件中
:return:
"""
# 文件名
cookie_filename = 'cookie_file.txt'
# 构建cookieJar对象来保存cookie
cookie_jar = http.cookiejar.MozillaCookieJar(filename=cookie_filename)
# 使用httpCookiePorcessor创建Cookie处理器对象,参数为cookieJar
http_cookie_processor = request.HTTPCookieProcessor(cookiejar=cookie_jar)
# 构建opener
opener = request.build_opener(http_cookie_processor)
# 构建request
req = request.Request("http://www.baidu.com")
# 发送请求
response = opener.open(req)
# 保存cookie信息
cookie_jar.save()
print('done')


执行完成后,会发现在当前目录下创建cookie_file.txt,内容为:

# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html # This is a generated file!  Do not edit.

.baidu.com  TRUE    /   FALSE   3667818295  BAIDUID E06113B9C530BDC0B53043B8D3E8E964:FG=1
.baidu.com  TRUE    /   FALSE   3667818295  BIDUPSID    E06113B9C530BDC0B53043B8D3E8E964
.baidu.com  TRUE    /   FALSE   3667818295  PSTM    1520334648


示例三(从文件中获取Cookie,并作为请求的一部分去访问)

def demo4():
"""
从文件中获取Cookie,并作为请求的一部分
:return:
"""
# 创建MozillaCookieJar对象
cookie_jar = http.cookiejar.MozillaCookieJar()
# 文件名
cookie_filename = 'cookie_file.txt'
# 读取cookie内容
cookie_jar.load(cookie_filename)
# 使用httpCookiePorcessor创建Cookie处理器对象,参数为cookieJar
http_cookie_processor = request.HTTPCookieProcessor(cookiejar=cookie_jar)
# 构建opener
opener = request.build_opener(http_cookie_processor)
# 构建request
req = request.Request("http://www.baidu.com")
# 发送请求
response = opener.open(req)
print('done')


使用Cookielib和post登录人人网

示例

登录人人网,然后再获取某人的页面数据信息

import http.cookiejar
from urllib import request, parse

def demo():
# 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": "xxx@163.com", "password": "123456opmhsa"}
# 6. 通过urlencode()转码
postdata = bytes(parse.urlencode(data), encoding='utf-8')
# 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/410043129/profile")
# 10. 打印响应内容
print(response.read().decode('utf-8'))


如上,当用户名密码输入错误时,会返回如下信息:

<a class="close" href="javascript:closeError();"></a>
<p class="wrong">您的用户名和密码不匹配</p>
<p class="worp">为了账号安全,已向您的邮箱: <strong id="sendemail"></strong>发送了一封确认信,请通过邮件内链接登录。</p>
<p class="m-26"><a id="gotoEmail" href="#" target="_blank">打开邮箱查收确认信</a></p>
<p class="m-26"><a href="javascript:closeError();">重新输入</a></p>


当不登录直接访问目标页面时,会跳转到登录页面,提示需要登录才可以访问。

当输入正确用户名密码,通过cookieJar保存Cookie信息,再去获取主页面信息时,会发现可以拿到数据了,如下:

<head>
<meta name="Description" content="人人网 校内是一个真实的社交网络,联络你和你周围的朋友。 加入人人网校内你可以:联络朋友,了解他们的最新动态;和朋友分享相片、音乐和电影;找到老同学,结识新朋友;用照片和日志记录生活,展示自我。"/>
<meta name="Keywords" content="Xiaonei,Renren,校内,大学,同学,同事,白领,个人主页,博客,相册,群组,社区,交友,聊天,音乐,视频,校园,人人,人人网"/>
<title>人人网 - 邓**❤</title>
<meta charset="utf-8"/>


so,基于CookieJar和post模拟登录就搞定了。

注意事项

登录一般都会先有一个HTTP GET,用于拉取一些信息及获得Cookie,然后再HTTP POST登录。

HTTP POST登录的链接有可能是动态的,从GET返回的信息中获取。

password 有些是明文发送,有些是加密后发送。有些网站甚至采用动态加密的,同时包括了很多其他数据的加密信息,只能通过查看JS源码获得加密算法,再去破解加密,非常困难。

大多数网站的登录整体流程是类似的,可能有些细节不一样,所以不能保证其他网站登录成功。

当然,我们也可以直接发送账号密码到登录界面模拟登录,但是当网页采用JavaScript动态技术以后,想封锁基于 HttpClient 的模拟登录就太容易了,甚至可以根据你的鼠标活动的特征准确地判断出是不是真人在操作。

想做通用的模拟登录还得选别的技术,比如用内置浏览器引擎的爬虫(关键词:Selenium ,PhantomJS),这个我们将在后续说到。

get示例

贴吧爬取程序功能:

输入要抓取的贴吧名以及起始页和结束页,然后对页面进行爬取,并下载到本地。

from urllib import request, parse
import ssl

"""
通过浏览器抓取URL发现,其真实URL为:http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50
kw为关键词
ie为编码格式
pn为查询列表起始位置,计算公式为:(current_page -1) * 50
"""

def spiderHtml(page, fullurl):
print('*' * 15 + '正在爬取第' + str(page) + '页数据')
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36'
}
context = ssl._create_unverified_context()
req = request.Request(fullurl, headers=headers)
response = request.urlopen(req, context=context)
print('*' * 15 + '第' + str(page) + '页数据爬取完成')
return response.read()

def savehtml(page, html):
"""
保存抓取的页面数据
:param page: 当前页
:param html: 页面数据
:return:
"""
with open('num' + str(page) + '.html', mode='wb+') as f:
print('正在保存第%d页信息' % page)
f.write(html)
print('第%d页信息保存成功' % page)

def mainEntrance(url, startpage, endpage):
"""
爬虫主入口
:param url: 基础URL
:param startpage: 起始页
:param endpage: 结束页
:return:
"""
for page in range(startpage, endpage + 1):
pn = (page - 1) * 50
fullurl = url + '&' + str(pn)
html = spiderHtml(page, fullurl)
savehtml(page, html)

if __name__ == '__main__':
keyword = input("请输入贴吧名:")
startpage = int(input("请输入起始页:"))
endpage = int(input("请输入结束页:"))
data = {
'kw': keyword
}
encodeData = parse.urlencode(data)
url = 'http://tieba.baidu.com/f?' + encodeData
mainEntrance(url, startpage, endpage)


post示例

以有道字典,翻译”hello”为例。

通过Chrome浏览器开发者工具,监控发现,其Request Header, Response Header以及form data如下:

"""
General:
Request URL:http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule
Request Method:POST
Status Code:200 OK
Remote Address:220.181.76.84:80
Referrer Policy:no-referrer-when-downgrade

Response Headers:
Connection:keep-alive
Content-Encoding:gzip
Content-Type:application/json; charset=utf-8
Date:Fri, 02 Mar 2018 02:18:25 GMT
Server:nginx
Set-Cookie:YOUDAO_MOBILE_ACCESS_TYPE=0; domain=.youdao.com; expires=Sat, 02-Mar-2019 02:18:25 GMT
Transfer-Encoding:chunked
Vary:Accept-Encoding

Request Headers:
Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.9
Connection:keep-alive
Content-Length:205
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:OUTFOX_SEARCH_USER_ID_NCOO=901526923.248523; _ntes_nnid=d2c9f3c0ad87edc2a18d8baf7493468a,1489816879253; P_INFO=sijipingzaojia@126.com|1507340542|0|other|00&99|shd&1506756788&other#shd&370100#10#0#0|&0||sijipingzaojia@126.com; OUTFOX_SEARCH_USER_ID=1202025429@123.58.182.244; _ga=GA1.2.1223743331.1511144818; _ym_uid=1517990399796278715; DICT_UGC=be3af0da19b5c5e6aa4e17bd8d90b28a|; JSESSIONID=abce35wgNPW3r7TqipGhw; __lnkrntdmcvrd=-1; ___rl__test__cookies=1519957105235
Host:fanyi.youdao.com
Origin:http://fanyi.youdao.com
Referer:http://fanyi.youdao.com/?keyfrom=dict2.top
User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36
X-Requested-With:XMLHttpRequest

Quert String Parameyters:
smartresult:dict
smartresult:rule

Form Data:
i:hello
from:AUTO
to:AUTO
smartresult:dict
client:fanyideskweb
salt:1519957105239
sign:8576918dd13c8c792aabc6986380a8bb
doctype:json
version:2.1
keyfrom:fanyi.web
action:FY_BY_CLICKBUTTION
typoResult:false
"""


根据上面拦截到的信息,拼装自己的请求,然后发送:

def doDict(keyword):
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
"Connection": "keep - alive",
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36',
'Host': 'fanyi.youdao.com',
'Origin': 'http: // fanyi.youdao.com',
'Referer': 'http://fanyi.youdao.com/?keyfrom=dict2.top',
'X-Requested-With': 'XMLHttpRequest',
'Cookie': 'OUTFOX_SEARCH_USER_ID_NCOO=901526923.248523; _ntes_nnid=d2c9f3c0ad87edc2a18d8baf7493468a,1489816879253; P_INFO=sijipingzaojia@126.com|1507340542|0|other|00&99|shd&1506756788&other#shd&370100#10#0#0|&0||sijipingzaojia@126.com; OUTFOX_SEARCH_USER_ID=1202025429@123.58.182.244; _ga=GA1.2.1223743331.1511144818; _ym_uid=1517990399796278715; DICT_UGC=be3af0da19b5c5e6aa4e17bd8d90b28a|; JSESSIONID=abce35wgNPW3r7TqipGhw; __lnkrntdmcvrd=-1; YOUDAO_MOBILE_ACCESS_TYPE=0; ___rl__test__cookies=1519960368231',
}
req = request.Request("http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule", headers=headers)
data = {
'i': keyword,
'from': 'AUTO',
'to': 'AUTO',
'smartresult': 'dict',
'client': 'fanyideskweb',
'salt': '1519960368238',
'sign': '1078dab49931bac7081851a7da7320b2',
'doctype': 'json',
'version': '2.1',
'keyfrom': 'fanyi.web',
'action': 'FY_BY_CLICKBUTTION',
'typoResult': 'false',
}
encode_data = bytes(parse.urlencode(data), encoding='utf-8')
response = request.urlopen(req, data=encode_data)
print(response.read().decode('utf-8'))

if __name__ == '__main__':
keyword = input("请输入要翻译的单词:")
doDict(keyword)


输入”hello”,运行结果为:

{"translateResult":[[{"tgt":"你好","src":"hello"}]],"errorCode":0,"type":"en2zh-CHS","smartResult":{"entries":["","n. 表示问候, 惊奇或唤起注意时的用语\r\n","int. 喂;哈罗\r\n","n. (Hello)人名;(法)埃洛\r\n"],"type":1}}


so,这样就完成了一个简单的post请求。

不过上述代码只对”hello”有效,因为form参数中有sign签名校验,目前还不知道怎么破解,不过这也难不倒我们,后续我们会介绍如何从爬到的HTML中获取我们关心的数据信息。

urllib库的基本使用和参数介绍就到这了,休息,休息一下~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息