Python爬虫之urllib库—进阶篇

urllib库除了一些基础的用法外,还有很多高级的功能,可以更加灵活的适用在爬虫应用中,比如,用HTTP的POST请求方法向服务器提交数据实现用户登录、当服务器检测出频繁使用同一IP而发出禁令时,如何使用代理IP来应对,如何设置超时,以及解析URL方法上的一些处理,本次将会对这些内容进行详细的分析和讲解。

  • POST请求

POST是HTTP协议的请求方法之一,也是比较常用到的一种方法,用于向服务器提交数据。博主先介绍进行post请求的一些准备工作,然后举一个例子,对其使用以及更深层概念进行详细的的剖析。

  • POST请求的准备工作

既然要提交信息给服务器,我们就需要知道信息往哪填,填什么,填写格式是什么?带这些问题,我们往下看。

同样提交用户登录信息(用户名和密码),不同网站可能需要的东西不一样,比如淘宝反爬机制较复杂,会有其它一大串的额外信息。这里,我们以豆瓣为例(相对简单),目标是弄清楚POST是如何使用的,复杂内容会在后续实战部分与大家继续分享。

抛出上面像淘宝一样需要的复杂信息,如果仅考虑用户名和密码的话,我们的准备工作其实就是要弄明白用户名和密码标签的属性name是什么,这可以通过浏览器F12查看element获取,也可以通过抓包工具Fiddler获取,fiddler的下载地址 https://www.telerik.com/download/fiddler

通过浏览器F12元素查看到,邮箱/手机号标签的name="form_email", 密码的标签name="form_email",如下图红框所示。但要说明的是,两个标签的name名称并不是固定的,上面查看的name名称只是豆瓣网站定义的,不代表所有。其它的网站可能有会有不同的名称,比如name="username", name="password"之类的。因此,针对不同网站的登录,需要每次查看name是什么。

通过fiddler抓包工具也能抓到想要的内容。

博主推荐使用fiddler工具,因为爬虫就是模拟浏览器工作,fiddler会帮助我们抓取正常使用浏览器时POST请求的内容,这样我们只要把抓到的浏览器POST的信息填到爬虫程序里就ok了。另外,也可以通过fiddler抓到浏览器请求的headers,非常方便。

安装fiddler的小伙伴们注意:fiddler证书问题的坑(无法抓取HTTPs包),可以通过Tools —> Options —>HTTPS里面打勾Decrypt HTTPS traffic修改证书来解决。否则会一直显示抓取 Tunnel 信息包...

好了,完成了准备工作,我们直接上一段代码理解下。

  • POST请求的使用
# coding: utf-8
import urllib.request
import urllib.error
import urllib.parse

# headers 信息,从fiddler上或你的浏览器上可复制下来
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',
           'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; 
                          Win64; x64) AppleWebKit/537.36 
                          (KHTML, like Gecko)Chrome/48.0
                          .2564.48 Safari/537.36'
           }
# POST请求的信息,填写你的用户名和密码
value = {'source': 'index_nav',
         'form_password': 'your password',
         'form_email': 'your username'
         }
try:
    data = urllib.parse.urlencode(value).encode('utf8')
    response = urllib.request.Request(
    'https://www.douban.com/', data=data, headers=headers)
    html = urllib.request.urlopen(response)
    result = html.read().decode('utf8')
    print(result)
except urllib.error.URLError as e:
    if hasattr(e, 'reason'):
        print('错误原因是' + str(e.reason))
except urllib.error.HTTPError as e:
    if hasattr(e, 'code'):
        print('错误编码是' + str(e.code))
else:
    print('请求成功通过。')

正常运行结果:

<!DOCTYPE HTML>
<html lang="zh-cmn-Hans" class="ua-windows ua-webkit">
<head>
<meta charset="UTF-8">
<meta name="description" content="提供图书、电影、音乐唱片的
推荐、评论和价格比较,以及城市独特的文化生活。">
.....
    window.attachEvent('onload', _ga_init);
}
</script>
</body>
</html>

注意:复制header的时候请去掉 这一项'Accept-Encoding':' gzip, deflate, 否则会提示decode的错误。

  • POST请求代码分析

我们来分析一下上面的代码,与urllib库request的使用基本一致,urllib库request的基本用法可参考上篇文章Python爬虫之urllib库这里多出了post的data参数和一些解析的内容(红色标识),着重讲解一下。

 data = urllib.parse.urlencode(value).encode('utf8')

这句的意思是利用了urllib库的parse来对post内容解析,为什么要解析呢?

这是因为post内容需要进行一定的编码格式处理后才能发送,而编码的规则需要遵从RFC标准,百度了一下RFC定义,供大家参考:

Request For Comments(RFC),是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件。目前RFC文件是由Internet Society(ISOC)赞助发行。基本的互联网通信协议都有在RFC文件内详细说明。RFC文件还额外加入许多的论题在标准内,例如对于互联网新开发的协议及发展中所有的记录。因此几乎所有的互联网标准都有收录在RFC文件之中。

parse的urlencode方法功能是将一个字典或者有顺序的二元素元组转换成为URL的查询字符串,说白了就是按照RFC标准转换了一下格式。然后再将转换好的字符串进行utf-8的编码(encode(utf-8))转换成为二进制格式才能使用。

注:Python3.x中编码解码规则为 byte—>string—>byte的模式,其中byte—>string为解码,string—>byte为编码

  • 代理IP

为什么要使用代理IP?因为各种反爬机制会检测同一IP爬取网页的频率速度,如果速度过快,就会被认定为机器人,但是速度过慢又会影响爬取的速度,因此,我们将使用代理IP取代我们自己的IP,这样不断更换新的IP地址就可以达到快速爬取网页而降低被检测为机器人的目的了。

同样利用urllib的request就可以完成代理IP的使用,但是与之前用到的urlopen不同,我们需要自己创建订制化的opener。什么意思呢?

urlopen就好像是opener的通用版本,当我们需要特殊功能(例如代理IP)的时候,urlopen满足不了我们的需求,我们就不得不自己定义并创建特殊的opener了。

request里面正好有处理各种功能的处理器方法,如下:

ProxyHandler, UnknownHandler, HTTPHandler,
HTTPDefaultErrorHandler, HTTPRedirectHandler,
FTPHandler, FileHandler, HTTPErrorProcessor, DataHandler

我们要用的是第一个ProxyHandler来处理代理问题

让我们看一段代码如何使用。

# coding:utf-8

import urllib.request
import urllib.error
import urllib.parse

# headers信息,从fiddler上或浏览器上可复制下来
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',
           'User-Agent': 'Mozilla/5.0 (Windows NT 6.3;
                          Win64;
                          x64) AppleWebKit/537.36 (KHTML, 
                          like Gecko)Chrome/48.0.2564.48 
                          Safari/537.36'
           }
# POST请求的信息
value = {'source': 'index_nav',
         'form_password': 'your password',
         'form_email': 'your username'
         }
# 代理IP信息为字典格式,key为'http',value为'代理ip:端口号'
proxy = {'http': '115.193.101.21:61234'}
try:
    data = urllib.parse.urlencode(value).encode('utf8')
    response = urllib.request.Request(
    'https://www.douban.com/', data=data, headers=headers)
    # 使用ProxyHandler方法生成处理器对象
    proxy_handler = urllib.request.ProxyHandler(proxy)
    # 创建代理IP的opener实例
    opener = urllib.request.build_opener(proxy_handler)
    # 将设置好的post信息和headers的response作为参数
    html = opener.open(response)
    result = html.read().decode('utf8')
    print(result)
except urllib.error.URLError as e:
    if hasattr(e, 'reason'):
        print('错误原因是' + str(e.reason))
except urllib.error.HTTPError as e:
    if hasattr(e, 'code'):
        print('错误编码是' + str(e.code))
else:
    print('请求成功通过。')

在上面post请求代码的基础上,用自己创建的opener替换urlopen即可完成代理IP的操作,红色为变化的部分和需要自己填写的部分,代理ip可以到一些免费的代理IP网站上查找,如西刺http://www.xicidaili.com/等有很多。

运行得到的结果与使用本机IP一样。

proxy = {'http': '115.193.101.21:61234'}

这个代理IP数据类型为字典,如果是http协议,key值就为"http",value值应为"代理IP:端口号"的格式。

proxy_handler = urllib.request.ProxyHandler(proxy)

使用ProxyHandler方法创建proxy处理器对象

opener = urllib.request.build_opener(proxy_handler)

创建代理IP的opener实例,参数为proxy处理器对象

html = opener.open(response)

用代理IP的opener打开指定状态的URL信息

  • 超时

设置超时的目的是为了防止爬取网站的时候,等待时间过长而导致效率的降低。有效的超时设置可以强制结束等待而进行下一次的爬取,下面来一段代码看如何使用。

# coding:utf-8

import urllib.request
import urllib.error
import urllib.parse
import socket

# headers信息,从fiddler上或浏览器上可复制下来
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',
           'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; 
                          Win64;
                          x64) AppleWebKit/537.36
                          (KHTML, like Gecko)Chrome/48.0
                          .2564.48 Safari/537.36'
           }
# POST请求的信息
value = {'source': 'index_nav',
         'form_password': 'your password',
         'form_email': 'your username'
         }
# 代理IP为字典格式,key为'http',value为'代理ip:端口号'
proxy = {'http': '115.193.101.21:61234'}
# 设置超时为2秒,单位为秒
timeout = 2
try:
    # 设置socket超时
    socket.setdefaulttimeout(timeout)
    
    data = urllib.parse.urlencode(value).encode('utf8')
    response = urllib.request.Request(
    'https://www.douban.com/', data=data, headers=headers)

    # 使用ProxyHandler方法生成处理器对象
    proxy_handler = urllib.request.ProxyHandler(proxy)
    # 创建代理IP的opener实例
    opener = urllib.request.build_opener(proxy_handler)
    # 将设置好的post信息和headers的response作为参数
    html = opener.open(response)
    result = html.read().decode('utf8')
    print(result)
except urllib.error.URLError as e:
    if hasattr(e, 'reason'):
        print('错误原因是' + str(e.reason))
except urllib.error.HTTPError as e:
    if hasattr(e, 'code'):
        print('错误编码是' + str(e.code))
except socket.timeout:
    print('socket超时')
else:
    print('请求成功通过。')

在post和代理IP使用的基础上又增加了超时的使用,红色为变化部分。

socket.setdefaulttimeout(timeout)

设置socket超时时间,如果不设置,则会使用默认时间。

except socket.timeout:
    print('socket超时')

同时对socket超时timeout的错误设置了异常,timeout错误属于OSerror的子类,时间超出指定timeout就会提示socket超时。

  • urllib库parse

除了上面提到的urlencode方法,urllib库的parse中还有很多其它的方法可以使用,如:

urlparse:把URL解析成6个部分

<scheme>://<netloc>/<path>;<params>?<query>#<fragment>

urlsplit:把URL解析成5个部分

<scheme>://<netloc>/<path>?<query>#<fragment>

urlunsplit,urlunparse:进行URL的重组

还有urljoin,urldefrag等。

更多用法可以查找官方request源码,也会在后续实战例子中陆续使用介绍。

  • 总结

主要介绍了urllib库的一些高级用法:

  1. POST请求的准备和使用方法
  2. 代理IP的使用
  3. 超时的使用
  4. parse解析

最后,感谢大家最近的支持和鼓励,博主会继续努力与大家分享更多实用和有趣的内容。

其实,与人分享的过程也是在提高自己,充实自己,因此博主非常喜欢与大家共同学习和进步,如果在过程中有不对或者不合理的地方,欢迎大家随时提出宝贵的意见!

原文发布于微信公众号 - Python数据科学(Python_Spiderman)

原文发表时间:2018-01-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

accept()返回的套接字绑定哪个端口 新旧套接字的联系

摘要:对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把...

2523
来自专栏Golang语言社区

Go语言异步服务器框架原理和实现

Go语言类库中,有两个官方的服务器框架,一个HTTP,一个是RPC。使用这个两个框架,已经能解决大部分的问题,但是,也有一些需求,这些框架是不够的,这篇文章,我...

5807
来自专栏蓝天

揭开Linux的Swap之谜

为什么选择Linux?因为Linux能让你掌握你所做的一切!   为什么痛恨Windows?因为Windows让你不知道自己在做什么!   这就是我喜欢Linu...

1553
来自专栏技术记录

rabbitMQ教程(二)一篇文章看懂rabbitMQ

一、rabbitMQ是什么:   RabbitMQ,遵循AMQP协议,由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上。   学过...

3187
来自专栏漏斗社区

CTF内存取证入坑指南!稳!

最近,斗哥在刷CTF题目。突然刷到了内存取证类,了解到了一款牛逼的工具——Volatility,在kali linux也默认安装好了这个工具,正好可以好好学习一...

5417
来自专栏玄魂工作室

Kali Linux Web渗透测试手册(第二版) - 2.3 - 使用Nmap进行扫描和识别应用服务

thr0cyte,Gr33k,花花,MrTools,R1ght0us,7089bAt,

2313
来自专栏Debian社区

HAProxy用法详解 最详细中文文档

(1)HAProxy 是一款提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,它是免费、快速并且可靠的一种解决方案...

3223
来自专栏卡少编程之旅

在Windows上切换node版本的实践

35612
来自专栏大数据和云计算技术

HBase运维实践-聊聊RIT的那点事

相信长时间运维HBase集群的童鞋肯定都会对RIT(Region-In-Transition,很多参考资料误解为Region-In-Transaction,需要...

2574
来自专栏耕耘实录

使用awk命令批量删除指定范围的账号

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

1013

扫码关注云+社区

领取腾讯云代金券