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

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'))

上篇文章说过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'>
连接超时
概念
  • 指某些网站为了辨别用户身份、进行 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库的基本使用和参数介绍就到这了,休息,休息一下~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值