python3.0 网络爬虫 5

urllib的使用细节


1.Proxy 的设置

urllib 默认会使用环境变量 http_proxy 来设置 HTTP Proxy。

如果想在程序中明确控制 Proxy 而不受环境变量的影响,可以使用代理。

新建test14来实现一个简单的代理Demo:

# -*- coding: utf-8 -*-
from urllib.request import Request,ProxyHandler,urlopen,HTTPBasicAuthHandler,build_opener,install_opener
from urllib.error import URLError,HTTPError
enable_proxy = 1
proxy_handler = ProxyHandler({"http" : 'http://www.baidu.com:8087'})
null_proxy_handler = ProxyHandler({})
if enable_proxy:
	opener = build_opener(proxy_handler)
else:
	opener = build_opener(null_proxy_handler)
	
install_opener(opener)

这里要注意的一个细节,使用 install_opener() 会设置 全局 opener 。

这样后面的使用会很方便,但不能做更细致的控制,比如想在程序中使用两个不同的 Proxy 设置等。

比较好的做法是不使用 install_opener 去更改全局的设置,而只是直接调用 opener 的 open 方法代替全局的 urlopen 方法。


2.Timeout 设置
在老版 Python 中(Python2.6前),urllib 的 API 并没有暴露 Timeout 的设置,要设置 Timeout 值,只能更改 Socket 的全局 Timeout 值。
from urllib.request import Request,ProxyHandler,urlopen,HTTPBasicAuthHandler,build_opener,install_opener
from urllib.error import URLError,HTTPError
import socket
enable_proxy = 1
proxy_handler = ProxyHandler({"http" : 'http://www.baidu.com:8087'})
null_proxy_handler = ProxyHandler({})
if enable_proxy:
	opener = build_opener(proxy_handler)
else:
	opener = build_opener(null_proxy_handler)
install_opener(opener)
socket.setdefaulttimeout(10)
#或者另外一种:
# socket.setdefaulttimeout(10)


在 Python 2.6 以后,超时可以通过 urlopen() 的 timeout 参数直接设置。
from urllib.request import Request,ProxyHandler,urlopen,HTTPBasicAuthHandler,build_opener,install_opener
from urllib.error import URLError,HTTPError
import socket

respon = urlopen('http://www.baidu.com',timeout=10)


3.在 HTTP Request 中加入特定的 Header

要加入 header,需要使用 Request 对象, 对有些 header 要特别留意,服务器会针对这些 header 做检查
User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
Content-Type : 在使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析。常见的取值有:
application/xml : 在 XML RPC,如 RESTful/SOAP 调用时使用
application/json : 在 JSON RPC 调用时使用
application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用
在使用服务器提供的 RESTful 或 SOAP 服务时, Content-Type 设置错误会导致服务器拒绝服务



4.Redirect
urllib2 默认情况下会针对 HTTP 3XX 返回码自动进行 redirect 动作,无需人工配置。要检测是否发生了 redirect 动作,只要检查一下 Response 的 URL 和 Request 的 URL 是否一致就可以了。
from urllib.request import Request,ProxyHandler,urlopen,HTTPBasicAuthHandler,build_opener,install_opener
from urllib.error import URLError,HTTPError
 
a_url = 'http://www.baidu.com'
response = urlopen(a_url) 
redirected = response.geturl()==a_url
print (redirected)  


如果不想自动 redirect,除了使用更低层次的 httplib 库之外,还可以自定义HTTPRedirectHandler 类
import urllib.request
class RedirectHandler(urllib.request.HTTPRedirectHandler):
 	def http_error_301(self, req,fp,code,msg,header):
 		print("301")
 		pass
 	def http_error_302(self, req,fp,code,msg,header):
 		print("302")
 		pass
a_url = 'http://www.baidu.com'
opener = urllib.request.build_opener(RedirectHandler)
opener.open(a_url)

5.Cookie

urllib 对 Cookie 的处理也是自动的。如果需要得到某个 Cookie 项的值,可以这么做:
import urllib.request
import http.cookiejar
cookie = http.cookiejar.CookieJar()
a_url = 'http://www.baidu.com'
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie))
resp = opener.open(a_url)
for item in cookie:
	print("Name = ",item.name)
	print("Value = ",item.value)


运行之后就会输出访问百度的Cookie值:

Name =  BAIDUID
Value =  F5F6E404730823534286965EEC3669B3:FG=1
Name =  BIDUPSID
Value =  F5F6E404730823534286965EEC3669B3
Name =  H_PS_PSSID
Value =  1440_21113_17001_21615
Name =  PSTM
Value =  1484186252
Name =  BDSVRTM
Value =  0
Name =  BD_HOME
Value =  0


6.使用 HTTP 的 PUT 和 DELETE 方法

urllib 只支持 HTTP 的 GET 和 POST 方法,如果要使用 HTTP PUT 和 DELETE ,只能使用比较低层的 httplib 库。虽然如此,我们还是能通过下面的方式,使 urllib2 能够发出 PUT 或DELETE 的请求:

import urllib.request
a_url = 'http://www.baidu.com'
request1=urllib.request.Request(a_url)
request1.get_method = lambda: 'PUT' #or 'DELETE'
response = urllib.request.urlopen(request1)


7.得到 HTTP 的返回码

对于 200 OK 来说,只要使用 urlopen 返回的 response 对象的 getcode() 方法就可以得到 HTTP 的返回码。但对其它返回码来说,urlopen 会抛出异常。这时候,就要检查异常对象的 code 属性了:
import urllib.request
import urllib.error 
a_url = 'http://www.qq.com/unknown'
try:
	response = urllib.request.urlopen(a_url)
except urllib.error.HTTPError as e:
	print(e.code)



8.Debug Log

使用 urllib2 时,可以通过下面的方法把 debug Log 打开,这样收发包的内容就会在屏幕上打印出来,方便调试,有时可以省去抓包的工作
from urllib.request import Request,HTTPHandler,HTTPSHandler,urlopen,build_opener,install_opener
from urllib.error import URLError,HTTPError

a_url = 'http://www.google.com/unknown'
httpHandler = HTTPHandler(debuglevel = 1)
httpsHandler = HTTPSHandler(debuglevel = 1)
opener = build_opener(httpHandler,httpsHandler)
install_opener(opener)
response = urlopen(a_url)

这样就可以看到传输的数据包内容了:

Traceback (most recent call last):
  File "D:\Python3_5_1\lib\urllib\request.py", line 1240, in do_open
    h.request(req.get_method(), req.selector, req.data, headers)
  File "D:\Python3_5_1\lib\http\client.py", line 1083, in request
    self._send_request(method, url, body, headers)
  File "D:\Python3_5_1\lib\http\client.py", line 1128, in _send_request
    self.endheaders(body)
  File "D:\Python3_5_1\lib\http\client.py", line 1079, in endheaders
    self._send_output(message_body)
  File "D:\Python3_5_1\lib\http\client.py", line 911, in _send_output
    self.send(msg)
  File "D:\Python3_5_1\lib\http\client.py", line 854, in send
    self.connect()
  File "D:\Python3_5_1\lib\http\client.py", line 826, in connect
    (self.host,self.port), self.timeout, self.source_address)
  File "D:\Python3_5_1\lib\socket.py", line 711, in create_connection
    raise err
  File "D:\Python3_5_1\lib\socket.py", line 702, in create_connection
    sock.connect(sa)
TimeoutError: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。


During handling of the above exception, another exception occurred:


Traceback (most recent call last):
  File "C:\Users\123\Documents\t1.py", line 9, in <module>
    response = urlopen(a_url)
  File "D:\Python3_5_1\lib\urllib\request.py", line 162, in urlopen
    return opener.open(url, data, timeout)
  File "D:\Python3_5_1\lib\urllib\request.py", line 465, in open
    response = self._open(req, data)
  File "D:\Python3_5_1\lib\urllib\request.py", line 483, in _open
    '_open', req)
  File "D:\Python3_5_1\lib\urllib\request.py", line 443, in _call_chain
    result = func(*args)
  File "D:\Python3_5_1\lib\urllib\request.py", line 1268, in http_open
    return self.do_open(http.client.HTTPConnection, req)
  File "D:\Python3_5_1\lib\urllib\request.py", line 1242, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。>


9.表单的处理

登录必要填表,表单怎么填?

首先利用工具截取所要填表的内容。
比如我一般用firefox+httpfox插件来看看自己到底发送了些什么包。
以verycd为例,先找到自己发的POST请求,以及POST表单项。
可以看到verycd的话需要填username,password,continueURI,fk,login_submit这几项,其中fk是随机生成的(其实不太随机,看上去像是把epoch时间经过简单的编码生成的),需要从网页获取,也就是说得先访问一次网页,用正则表达式等工具截取返回数据中的fk项。continueURI顾名思义可以随便写,login_submit是固定的,这从源码可以看出。还有username,password那就很显然了:

from urllib.request import Request,HTTPHandler,HTTPSHandler,urlopen,build_opener,install_opener
from urllib.parse import urlencode
from urllib.error import URLError,HTTPError

a_url = 'http://mail.163.com/'
postdata = urlencode({
	'usrname':'hhh',
	'psw':'123456',
	'continueURL':'http://www.163.com/',
	'fk':'',
	'login_submit':'登录'
	}).encode(encoding = 'UTF8')
req = Request(url=a_url,data=postdata)
result = urlopen(req)
print(result.read())

10.伪装成浏览器访问
某些网站反感爬虫的到访,于是对爬虫一律拒绝请求
这时候我们需要伪装成浏览器,这可以通过修改http包中的header来实现

from urllib.request import Request,HTTPHandler,HTTPSHandler,urlopen,build_opener,install_opener
from urllib.parse import urlencode
from urllib.error import URLError,HTTPError

a_url = 'http://mail.163.com/'
headers = {'User-Agent':'Mozilla/5.0 (Windows;U;Windows NT 6.1;en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
postdata = urlencode({
	'usrname':'ttt',
	'psw':'123',
	'continueURL':'http://www.163.com/',
	'fk':'',
	'login_submit':'登录'
	}).encode(encoding = 'UTF8')
req = Request(url=a_url,data=postdata,headers = headers)
result = urlopen(req)
print(result.read())


11.对付"反盗链"
某些站点有所谓的反盗链设置,其实说穿了很简单,

就是检查你发送请求的header里面,referer站点是不是他自己,

所以我们只需要像把headers的referer改成该网站即可,以cnbeta为例:

#...
headers = {
    'Referer':'http://www.cnbeta.com/articles'
}
#...

from urllib.request import Request,HTTPHandler,HTTPSHandler,urlopen,build_opener,install_opener
from urllib.parse import urlencode
from urllib.error import URLError,HTTPError

a_url = 'http://mail.163.com/'
headers = {'User-Agent':'Mozilla/5.0 (Windows;U;Windows NT 6.1;en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6',
		   'Referer':'http://mail.163.com'}
postdata = urlencode({
	'usrname':'ttt',
	'psw':'123',
	'continueURL':'http://www.163.com/',
	'fk':'',
	'login_submit':'登录'
	}).encode(encoding = 'UTF8')
req = Request(url=a_url,data=postdata,headers = headers)
result = urlopen(req)
print(result.read())


headers是一个dict数据结构,你可以放入任何想要的header,来做一些伪装。

例如,有些网站喜欢读取header中的X-Forwarded-For来看看人家的真实IP,可以直接把X-Forwarde-For改了。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值