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入門教程可以在網站上找到