1.首先我们来完成一个简单的爬虫。
import urllib.request
response=urllib.request.urlopen('http://baidu.com')
html=response.read();
print(html)
2.发送请求的同时用post发送表单数据
import urllib
import urllib.request
url="http://baidu.com"
values={"name":"Why",
"location":"SDU",
"language":"python"
}
data=urllib.parse.urlencode(values).encode(encoding='utf_8', errors='strict')#编码工作
req=urllib.request.Request(url,data)#发送请求同时传data表单
response=urllib.request.urlopen(req)#接受反馈的信息
the_page=response.read()#读取反馈的内容
print(the_page)
但是相较于get方式,post方式存在着副作用
3.get方式发送数据请求
import urllib
import urllib.request
url="http://www.imooc.com/"
data = {}
data['name'] = 'WHY'
data['location'] = 'SDU'
data['language'] = 'Python'
data1=urllib.parse.urlencode(data)
print(data1)
full_url=url+"?"+data1
print(full_url)
response=urllib.request.urlopen(full_url)
the_page=response.read()
print(the_page)
4.设置headers头的http请求(楼主说这个demo不能工作了)
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {'name' : 'WHY',
'location' : 'SDU',
'language' : 'Python' }
headers = { 'User-Agent' : user_agent }
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
the_page = response.read()
5.URLError
import urllib
import urllib.request
from _testcapi import exception_print
req=urllib.request.Request("http://www.baibai.com")
print(req)
try:urllib.request.urlopen(req)
except urllib.error.URLError as e:
print(e.reason)
6.HTTPError
服务器上每一个HTTP 应答对象response包含一个数字”状态码”。
有时状态码指出服务器无法完成请求。默认的处理器会为你处理一部分这种应答。
例如:假如response是一个”重定向”,需要客户端从别的地址获取文档,urllib2将为你处理。
其他不能处理的,urlopen会产生一个HTTPError。
典型的错误包含”404”(页面无法找到),”403”(请求禁止),和”401”(带验证请求)。
HTTP状态码表示HTTP协议所返回的响应的状态。
比如客户端向服务器发送请求,如果成功地获得请求的资源,则返回的状态码为200,表示响应成功。
如果请求的资源不存在, 则通常返回404错误。
HTTP状态码通常分为5种类型,分别以1~5五个数字开头,由3位整数组成:
200:请求成功 处理方式:获得响应的内容,进行处理
201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到
202:请求被接受,但处理尚未完成 处理方式:阻塞等待
204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃
300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃
301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL
302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL
304 请求的资源未更新 处理方式:丢弃
400 非法请求 处理方式:丢弃
401 未授权 处理方式:丢弃
403 禁止 处理方式:丢弃
404 没有找到 处理方式:丢弃
5XX 回应代码以“5”开头的状态码表示服务器端发现自己出现错误,不能继续执行请求 处理方式:丢弃
HTTPError实例产生后会有一个整型’code’属性,是服务器发送的相关错误号。
Error Codes错误码
因为默认的处理器处理了重定向(300以外号码),并且100-299范围的号码指示成功,所以你只能看到400-599的错误号码。
BaseHTTPServer.BaseHTTPRequestHandler.response是一个很有用的应答号码字典,显示了HTTP协议使用的所有的应答号。
当一个错误号产生后,服务器返回一个HTTP错误号,和一个错误页面。
你可以使用HTTPError实例作为页面返回的应答对象response。
这表示和错误属性一样,它同样包含了read,geturl,和info方法。
import random
import socket
import urllib.request
import http.cookiejar
ERROR = {
'0':'Can not open the url,checck you net',
'1':'Creat download dir error',
'2':'The image links is empty',
'3':'Download faild',
'4':'Build soup error,the html is empty',
'5':'Can not save the image to your disk',
}
class BrowserBase(object):
def __init__(self):
socket.setdefaulttimeout(20)
def speak(self,name,content):
print('[%s]%s' %(name,content))
def openurl(self,url):
cookie_support= urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar())
self.opener = urllib.request.build_opener(cookie_support,urllib.request.HTTPHandler)
urllib.request.install_opener(self.opener)
user_agents = [
'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11',
'Opera/9.25 (Windows NT 5.1; U; en)',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
'Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12',
'Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9',
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7",
"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 ",
]
agent = random.choice(user_agents)
self.opener.addheaders = [("User-agent",agent),("Accept","*/*"),('Referer','http://www.google.com')]
try:
res = self.opener.open(url)
print(res.read())
except Exception as e:
self.speak(str(e)+url)
raise Exception
else:
return res
if __name__=='__main__':
splider=BrowserBase()
splider.openurl('http://blog.csdn.net/v_JULY_v/archive/2010/11/27/6039896.aspx')
由于有些网页是禁止爬虫的,所以要以正常的形式来获取,上述代码就是一个正规访问的方式
7.wrapping
import urllib.request
req=urllib.request.Request('http://baibai.com/')
try:response=urllib.request.urlopen(req)
except urllib.error.URLError as e:
if hasattr(e, 'code'):
print("The server couldn't fulfill the request")
print("Error code",e.code)
elif hasattr(e, 'reason'):
print('we failed to reach a server.')
print('Reason',e.reason)
else:
print('No exception was raised')
8.geturl()和getInfo()
import urllib.request
req=urllib.request.Request('http://baidu.com')
response=urllib.request.urlopen(req)
print("real url"+response.geturl())
print(response.info())
运行结果:
real urlhttp://baidu.com
Date: Sat, 15 Oct 2016 15:12:06 GMT
Server: Apache
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
ETag: “51-47cf7e6ee8400”
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sun, 16 Oct 2016 15:12:06 GMT
Connection: Close
Content-Type: text/html
9.opener和handler
# -*- coding: utf-8 -*-
import urllib.request
# 创建一个密码管理者
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
# 添加用户名和密码
top_level_url = "http://example.com/foo/"
# 如果知道 realm, 我们可以使用他代替 ``None``.
# password_mgr.add_password(None, top_level_url, username, password)
password_mgr.add_password(None, top_level_url,'why', '1223')
# 创建了一个新的handler
handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
# 创建 "opener" (OpenerDirector 实例)
opener = urllib.request.build_opener(handler)
a_url = 'http://www.baidu.com/'
# 使用 opener 获取一个URL
opener.open(a_url)
# 安装 opener.
# 现在所有调用 urllib.request.urlopen 将用我们的 opener.
urllib.request.install_opener(opener)
注意:以上的例子我们仅仅提供我们的HHTPBasicAuthHandler给build_opener。
默认的openers有正常状况的handlers:ProxyHandler,UnknownHandler,HTTPHandler,HTTPDefaultErrorHandler, HTTPRedirectHandler,FTPHandler, FileHandler, HTTPErrorProcessor。
代码中的top_level_url 实际上可以是完整URL(包含”http:”,以及主机名及可选的端口号)。
例如:http://example.com/。
也可以是一个“authority”(即主机名和可选的包含端口号)。
例如:“example.com” or “example.com:8080”。
后者包含了端口号。
10.urllib细节
proxy_handler=urllib.request.ProxyHandler({“http”:”http://some-proxy.com:8080})
opener=urllib.request.build_opener(proxy_handler)
urllib.request.install_opener(opener)
request=urllib.request.Request(“”)
request.add_header(“User_Agent”,’fake-client’)
cookie=http.cookiejar.CookieJar()
opener=urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie))
for item in cookie:
print ‘Name=’+item.name
print ‘Value’+item.value
httpHandler=urllib.request.HTTPHandler(debuglevel=1)
httpsHandler=urllib.request.HTTPSHandler(debuglevel=1)
opener=urllib.request.build_opener(httpHandler,httpsHandler)
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 设置错误会导致服务器拒绝服务
判断重定向:response.geturl==mu_url
11.一个简单的百度小爬虫
import urllib.request as request
import urllib.parse as parse
import string
def baidu_tieba(url, begin_page, end_page):
for i in range(begin_page, end_page + 1):
sName = 'd:/test/'+str(i).zfill(5)+'.html'
print('正在下载第'+str(i)+'个页面, 并保存为'+sName)
m = request.urlopen(url+str(i)).read()
with open(sName,'wb') as file:
file.write(m)
file.close()
if __name__ == "__main__":
url = "http://tieba.baidu.com/p/"
begin_page = 1
end_page = 3
baidu_tieba(url, begin_page, end_page)
12.介绍re模块
re模块的操作步骤:
step1将正则表达式转化为Pattern实例
step2用Pattern实例进行匹配,得到Match实例
step3通过Match实例对得到的结果进行操作处理
import re
#将正则表达式编译成为Pattren对象,注意hello前面的r的意思是“原生字符串"
pattern=re.compile(r'hello')
#使用Pattren匹配文本,获得匹配结果,无法匹配时将返回none
match1=pattern.match('hello world!')
match2=pattern.match('helloo world!')
match3=pattern.match("helllo world!")
#如果match1匹配成功
if match1:
print(match1.group())
else:
print("match1匹配失败")
if match2:
print(match2.group())
else:
print("match2匹配失败")
if match3:
print(match3.group())
else:
print("match3匹配失败")
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
属性:
string: 匹配时使用的文本。
re: 匹配时使用的Pattern对象。
pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。
方法:
group([group1, …]):
获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。
groups([default]):
以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。
groupdict([default]):
返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。
start([group]):
返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。
end([group]):
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。
span([group]):
返回(start(group), end(group))。
expand(template):
将匹配到的分组代入template中然后返回。template中可以使用\id或\g、\g引用分组,但不能使用编号0。\id与\g是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符’0’,只能使用\g<1>0。
下面来用一个py实例输出所有的内容加深理
import re
# 匹配如下内容:单词+空格+单词+任意字符
m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello world!')
print "m.string:", m.string
print "m.re:", m.re
print "m.pos:", m.pos
print "m.endpos:", m.endpos
print "m.lastindex:", m.lastindex
print "m.lastgroup:", m.lastgroup
print "m.group():", m.group()
print "m.group(1,2):", m.group(1, 2)
print "m.groups():", m.groups()
print "m.groupdict():", m.groupdict()
print "m.start(2):", m.start(2)
print "m.end(2):", m.end(2)
print "m.span(2):", m.span(2)
print r"m.expand(r'\g<2> \g<1>\g<3>'):", m.expand(r'\2 \1\3')
### output ###
# m.string: hello world!
# m.re: <_sre.SRE_Pattern object at 0x016E1A38>
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!
下面重点介绍一下pattern实例及方法
match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
split(string[, maxsplit]) | re.split(pattern, string[, maxsplit])
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags])
sub(repl, string[, count]) | re.sub(pattern, repl, string[, count])
subn(repl, string[, count]) |re.sub(pattern, repl, string[, count])
12.糗事百科的爬虫代码
# -*- coding: utf-8 -*-
import urllib.request
import urllib
import re
import time
import threading
import _thread
#----------- 加载处理糗事百科 -----------
class Spider_Model:
def __init__(self):
self.page = 1
self.pages = []
self.enable = False
# 将所有的段子都扣出来,添加到列表中并且返回列表
def GetPage(self,page):
myUrl = "http://m.qiushibaike.com/hot/page/" + page
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
req = urllib.request.Request(myUrl, headers = headers)
myResponse = urllib.request.urlopen(req)
myPage = myResponse.read()
#encode的作用是将unicode编码转换成其他编码的字符串
#decode的作用是将其他编码的字符串转换成unicode编码
unicodePage = myPage.decode("utf-8")
# 找出所有class="content"的div标记
#re.S是任意匹配模式,也就是.可以匹配换行符
# myItems = re.findall('<div.*?class="content".*?title="(.*?)">(.*?)</div>',unicodePage,re.S)
myItems = re.findall('<div.*?class="(.*?)">.*?<span>(.*?)</span></div>',unicodePage,re.S)
items = []
for item in myItems:
# item 中第一个是div的标题,也就是时间
# item 中第二个是div的内容,也就是内容
print(item[1])
items.append([item[0].replace("\n",""),item[1].replace("\n","")])
return items
# 用于加载新的段子
def LoadPage(self):
# 如果用户未输入quit则一直运行
while self.enable:
# 如果pages数组中的内容小于2个
if len(self.pages) < 2:
try:
# 获取新的页面中的段子们
myPage = self.GetPage(str(self.page))
self.page += 1
self.pages.append(myPage)
except:
print( '无法链接糗事百科!' )
else:
time.sleep(1)
def ShowPage(self,nowPage,page):
for items in nowPage:
print( u'第%d页' % page , items[0] , items[1] )
myInput = input()
if myInput == "quit":
self.enable = False
break
def Start(self):
self.enable = True
page = self.page
print (u'正在加载中请稍候......')
# 新建一个线程在后台加载段子并存储
_thread.start_new_thread(self.LoadPage,())
#thread.start
#----------- 加载处理糗事百科 -----------
while self.enable:
# 如果self的page数组中存有元素
if self.pages:
nowPage = self.pages[0]
del self.pages[0]
self.ShowPage(nowPage,page)
page += 1
#----------- 程序的入口处 -----------
print (u'请按下回车浏览今日的糗百内容:')
input(' ')
myModel = Spider_Model()
myModel.Start()
上面的代码可以运行,但是不能爬取内容,原因是正则表达式有问题,楼主现在还不是很明白,以后熟悉了再更新。
13.Scrapy框架学习
由于windows系统还不支持python3.x+Scrapy框架,所以这里需要我使用了python2.7+Scrapy版本进行程序代码的编译调试。
Scrapy入门教程可以在网站上找到