Urllib库与URLError异常
用Urllib快速爬取一个网页:
import urllib.request
if __name__=='__main__':
file=urllib.request.urlopen("http://www.baidu.com") #用urllib.request.urlopen打开并爬取一个网页
data=file.read() #将爬取的网页的内容全部读出
dataline=file.readline() #读取爬取的网页的一行内容
print(data)
print("------------分割线------------")
print(dataline)
输出如下:
注:读取内容的方式一般有如下三种
- file.read()读取全部内容,并将其赋值给一个字符串变量
- file.readlines()读取全部内容,但将读取的内容赋值给一个列表变量
- file.readline()读取一行的内容
存在本地:
fhandle=open("D:\\PyCharm\\workplace\\Crawler\\test1.html","wb") #将爬取的内容存在本地文件,wb以二进制方式存储
fhandle.write(data)
fhandle.close()
除了以上的存储本地的方法,还可以直接使用urllib,request.urlretrieve()将对应的信息存入本地文件:
urllib.request.urlretrieve("http://www.baidu.com",filename="D:\\PyCharm\\workplace\\Crawler\\test2.html")
urlretrieve执行时会产生一些缓存,利用urlcleanup()清除缓存:
urllib.request.urlcleanup()
此外urllib中还有一些常见的用法:
file.info()#返回当前环境信息
file.getcode()#获取当前爬取网页的状态码,若返回200则正确,否则不正确
file.geturl()#获取当前爬取的URL地址
urllib.request.quote("http://www.sina.com.cn")#对当前URL进行编码,防止出现不符合标准URL的字符
模拟浏览器
有些时候,爬取的网页会出现403错误,因为这些网页为了防止被恶意采集信息而进行了反爬虫的设置。这个时候我们可以将爬虫模拟成浏览器去访问这些网站——通过设置UserAgent信息。
方法一:使用build_opener()修改报头
由于urlopen()不支持一些HTTP的高级功能,所以如果我们要修改报头可以使用urllib.request.build_opener()来完成。
url="https://blog.csdn.net/qq_29599907/article/details/80734574"
headers=("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36")
opener=urllib.request.build_opener()
opener.addheaders=[headers] #将元组列表化
data=opener.open(url).read()
fhandle = open("D:\\PyCharm\\workplace\\Crawler\\test3.html", "wb")
fhandle.write(data)
fhandle.close()
方法二:使用add_header()添加报头
使用urllib.request.Request()下的add_header()来实现浏览器的模拟
url = "https://blog.csdn.net/qq_29599907/article/details/80734574"
req=urllib.request.Request(url)
req.add_header("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36")
data=urllib.request.urlopen(req).read()
fhandle = open("D:\\PyCharm\\workplace\\Crawler\\test4.html", "wb")
fhandle.write(data)
fhandle.close()
超时设置
访问一个网站时,如果该网页长时间未响应,那么系统会判断该网页超时,无法打开网页。很多时候,我们在用爬虫爬取时要根据自己的需要来设置超时的时长。即在urlopen中加入参数timeout
urllib.request.urlopen("https://blog.csdn.net/qq_29599907/article/details/80734574",timeout=30)
HTTP协议请求
如果要进行客户端与服务器之间的消息传递,可以使用HTTP协议请求进行。
HTTP协议请求主要分为6种类型:GET,POST,PUT,DELETE,HEAD,OPTIONS
GET请求实例:
GET请求会通过URL传递信息,可以直接在URL中写上要传递的信息,也可以由表单进行传递。如果使用表单进行传递,这表单中的信息会自动转为URL地址中的数据,通过URL地址传递。
我们在百度上查询一个关键词时,会使用GET请求进行,其中关键词字段是wd,网址格式是:"https://www.baid.com/s?wd=关键词"。因此可以根据这个规律,通过构造GET请求,用爬虫实现在百度上自动查询某个关键词。
keyword="hello" #查询的关键词时hello
url="http://www.baidu.com/s?wd="+keyword
req=urllib.request.Request(url)
data=urllib.request.urlopen(req).read()
fhandle=open("D:\\PyCharm\\workplace\\Crawler\\test5.html","wb")
fhandle.write(data)
fhandle.close()
对于中文,以上程序会因编码问题而报错,因此要用urllib.request.quote进行编码
keyword="我们LGD是不可战胜的"
keyword=urllib.request.quote(keyword) #编码
url="http://www.baidu.com/s?wd="+keyword
req=urllib.request.Request(url)
data=urllib.request.urlopen(req).read()
fhandle=open("D:\\PyCharm\\workplace\\Crawler\\test6.html","wb")
fhandle.write(data)
fhandle.close()
由此我们可知,要使用GET请求,思路如下:
- 构建对应的URL,该URL包含GET请求的字段名和字段内容等信息,并且URL地址满足GET请求的格式,即“http://网址?字段名1=字段内容1&字段名2=字段内容2&...&字段名n&字段内容n”
- 以对应的URL为参数,构建Request对象
- 通过urlopen()打开构建的Request对象
- 按需求进行后续的处理操作
POST请求实例:
POST请求可以向服务器提交数据,是一种比较主流也比较安全的数据传递方式,一般用于注册、登陆等操作。
这里有一个用于测试登陆的网址“http://www.iqianyue.com/mypost”,我们通过爬虫自动实现登陆思路如下:
- 设置好URL
- 构建表单数据,并使用urllib.parse.urlencode对数据进行编码处理
- 创建Request对象,参数包括URL地址和要传递的数据
- 使用add_header()添加信息头,以模拟浏览器
- 使用urllib.request.urlopen()打开对应的Request对象,完成信息的传递
- 后续处理,例如读取网页内容等
由于点击提交后,仍然是传递到当前页面进行处理,故处理的页面仍然是http://www.iqianyue.com/mypost,URL应该设置为http://www.iqianyue.com/mypost。然后构建表单数据。查看网页源代码,找到form部分
<html>
<head>
<title>Post Test Page</title>
</head>
<body>
<form action="" method="post">
name:<input name="name" type="text" /><br>
passwd:<input name="pass" type="text" /><br>
<input name="" type="submit" value="submit" />
<br />
</body>
</html>
发现form中包含两个字段name和pass,故我们构造的数据中包含两个字段,字段值设置为我们要传递的值。格式用字典形式:
{字段名1:字段值1,字段名2:字段值2,...}
这里我们将数据设置为{"name":"Messi","pass":"messi123123"},然后用urllib.parse.urlencode对数据进行编码。然后创建Request对象,参数包括URL和要传递的数据,并用add_header()添加头信息,模拟浏览器。
url="http://www.iqianyue.com/mypost"
postdata=urllib.parse.urlencode({"name":"Messi","pass":"messi123123"}).encode('utf-8') #将数据使用urlencode处理后,使用encode设置为utf-8
req=urllib.request.Request(url,postdata)
req.add_header("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36")
data=urllib.request.urlopen(req).read()
fhandle=open("D:\\PyCharm\\workplace\\Crawler\\test7.html","wb")
fhandle.write(data)
fhandle.close()
代理服务器设置
使用同一个IP去爬取同一个网站上的网页,久了之后会被该网站服务器屏蔽,解决这个问题的办法就是使用代理服务器,在对方的服务器上显示别人的IP地址。完整的代理服务器IP格式为:"网址:端口号"
import urllib.request
import urllib.parse
def use_proxy(proxy_addr,url):
proxy=urllib.request.ProxyHandler({'http':proxy_addr}) #设置对应的代理服务器信息
opener=urllib.request.build_opener(proxy,urllib.request.HTTPHandler) #创建一个自定义opener对象,第一个参数为代理信息,第二个参数是urllib.request.HTTPHandler类
urllib.request.install_opener(opener) #创建全局默认的opener,必须install后才可以直接用urlopen打开相应网页并读取
data=urllib.request.urlopen(url).read().decode('utf-8')
return data
if __name__=='__main__':
proxy_addr="43.247.70.151:81"
data=use_proxy(proxy_addr,"http://www.baidu.com")
print(len(data))
DebugLog
DebugLog的目的是在爬虫程序运行的过程中边运行边打印调试日志。开启DebugLog的思路如下:
- 分别使用urllib.request.HTTPHandler()和urllib.request.HTTPSHandler()将debuglevel设置为1
- 使用urllib.request.build_opener()创建自定义的opener对象,并使用第一步中设置的值作为参数
- 用urllib.request.install_opener()创建全局默认的opener对象,这样在使用urlopen()是,也会使用我们安装的opener对象
- 进行后续相应操作
httphd=urllib.request.HTTPHandler(debuglevel=1)
httpshd=urllib.request.HTTPSHandler(debuglevel=1)
opener=urllib.request.build_opener(httphd,httpshd)
urllib.request.install_opener(opener)
data=urllib.request.urlopen("https://blog.csdn.net/qq_29599907/article/details/80734574")
异常处理——URLError
首先导入urllib.error模块。主要使用两个类,URLError类和URLError的一个子类HTTPError类。
一般来说产生URLError的原因有如下几种可能:
- 连接不上服务器
- 远程URL不存在
- 无网络
- 触发了HTTPError
通常错误原因都是HTTPError,故只要用HTTPError处理就可以,但HTTPError不能处理前三种异常,因此更鲁棒的做法是先让其用HTTPError处理,若无法处理再用URLError处理。
try:
urllib.request.urlopen("https://blog.csdn.net/qq_29599907/article/details/80734574")
except urllib.error.HTTPError as e:
print(e.code)
print(e.reason)
except urllib.error.URLError as e:
print(e.reason)
注意URLError错误的前三种原因是没有e.code的,只有e.reason,因此不能直接用父类URLError来完全替代子类HTTPError。可以做如下整合:
我们用URLError进行异常处理,然后做一个判断,如果有e.code则输出相应信息,如果没有则自动忽略。同理如果有e.reason就输出e.reason,没有则自动忽略。因为HTTPError和URLError均是类,故可以用类属性判断函数hasattr()来判断是否有这些属性。
try:
urllib.request.urlopen("https://blog.csdn.net/qq_29599907/article/details/80734574")
except urllib.error.URLError as e:
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)