首页 > 程序开发 > Web开发 > Python >

python3爬虫之旅

2016-10-19

1 首先我们来完成一个简单的爬虫。importurllib requestresponse=urllib request urlopen( http: baidu com )html=response read();print(html)2 发送请求的同时用post发送表单数据。

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之后是字符&rsquo;0&rsquo;,只能使用\g<1>0。
下面来用一个py实例输出所有的内容加深理

import re  
# 匹配如下内容:单词+空格+单词+任意字符  
m = re.match(r&#39;(\w+) (\w+)(?P.*)&#39;, &#39;hello world!&#39;)  

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&#39;\g<2> \g<1>\g<3>&#39;):", m.expand(r&#39;\2 \1\3&#39;)  

### 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): (&#39;hello&#39;, &#39;world&#39;)  
# m.groups(): (&#39;hello&#39;, &#39;world&#39;, &#39;!&#39;)  
# m.groupdict(): {&#39;sign&#39;: &#39;!&#39;}  
# m.start(2): 6  
# m.end(2): 11  
# m.span(2): (6, 11)  
# m.expand(r&#39;\2 \1\3&#39;): 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 = &#39;Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)&#39;   
        headers = { &#39;User-Agent&#39; : 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(&#39;(.*?)&#39;,unicodePage,re.S)
        myItems = re.findall(&#39;.*?(.*?)&#39;,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( &#39;无法链接糗事百科!&#39; )   
            else:    
                time.sleep(1)    

    def ShowPage(self,nowPage,page):    
        for items in nowPage:    
            print( u&#39;第%d页&#39; % 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&#39;正在加载中请稍候......&#39;)    

        # 新建一个线程在后台加载段子并存储    
        _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&#39;请按下回车浏览今日的糗百内容:&#39;)    
input(&#39; &#39;)    
myModel = Spider_Model()    
myModel.Start()    

上面的代码可以运行,但是不能爬取内容,原因是正则表达式有问题,楼主现在还不是很明白,以后熟悉了再更新

相关文章
最新文章
热点推荐