第一个Python程序——博客自动访问脚本
第一個(gè)Python程序——博客自動訪問腳本
動機(jī)
今天有朋友寫信說他認(rèn)為自己的wordpress博客內(nèi)顯示的訪問統(tǒng)計(jì)信息不正常,希望我能為他制造一些訪問信息,供他對比。朋友提出的請求是在短時(shí)間內(nèi)快速打開100個(gè)不同的博客頁面,以便他從產(chǎn)生的訪問量變化中理解博客訪問數(shù)據(jù)。
本人作為一個(gè)搞計(jì)算機(jī)的人,有把任何重復(fù)性勞動自動化的沖動,所以雖然點(diǎn)開100個(gè)網(wǎng)頁的任務(wù)手工做并不復(fù)雜,但還是從一開始就徹底否定了。剛好想學(xué)Python很久了,于是就拿這次的小機(jī)會來學(xué)習(xí)一把,順便記錄下第一次的Python學(xué)習(xí)成果。
本文使用Python 2.7.3實(shí)現(xiàn)了一個(gè)自動訪問博客的腳本,涉及以下技術(shù)點(diǎn):
語言基礎(chǔ)
容器(線性表、字典)
邏輯分支、循環(huán)
控制臺格式化輸出
HTTP客戶端網(wǎng)絡(luò)編程
處理HTTP請求
使用HTTP代理服務(wù)器
Python正則表達(dá)式
總覽
自動訪問博客頁面這個(gè)操作實(shí)際上和網(wǎng)絡(luò)爬蟲做的事情差不多,基本流程如下:
圖1 博客自動訪問器工作原理
給訪問器一個(gè)開始位置(例如博客首頁URL)
訪問器將URL指向的網(wǎng)頁爬回(爬回網(wǎng)頁這個(gè)操作本身相當(dāng)于在瀏覽器中打開頁面)
2中爬回的網(wǎng)頁交給分析器分析。分析器分析后提取出其中的URL加入待訪問URL列表,即URL庫。然后從URL庫中取出下一個(gè)要訪問的頁面URL
循環(huán)2、3步,直到達(dá)到某一終止條件程序退出
剛開始編程時(shí),我們什么都沒有,只有一個(gè)博客首頁的URL。緊跟著這個(gè)URL需要解決的問題就是如何編程爬取URL指向的頁面。爬取到了頁面,才能算是訪問了博客,也才能獲得頁面的內(nèi)容,從而提取更多的URL,進(jìn)行更進(jìn)一步的爬取。
這樣一來就帶來了如何根據(jù)URL獲取頁面信息的問題,要解決這個(gè)問題,需要用到HTTP客戶端編程,這也是接下來一節(jié)解決的問題。
urllib2:HTTP客戶端編程
Python中可以實(shí)現(xiàn)HTTP客戶端編程的庫有好幾個(gè),例如httplib, urllib, urllib2等。使用urllib2是因?yàn)樗δ軓?qiáng)大,使用簡單,并且可以很容易地使用HTTP代理。
使用urllib2創(chuàng)建一個(gè)HTTP連接并獲取頁面非常簡單,只需要3步:
import urllib2
opener = urllib2.build_opener()
file = opener.open(url)
content = file.read()
content即為HTTP請求響應(yīng)的報(bào)文體,即網(wǎng)頁的HTML代碼。如果需要設(shè)置代理,在調(diào)用build_opener()時(shí)傳入一個(gè)參數(shù)即可:
opener =urllib2.build_opener(urllib2.ProxyHandler({‘http’: “l(fā)ocalhost:8580”}))
ProxyHandler函數(shù)接受一個(gè)字典類型的參數(shù),其中key為協(xié)議名稱,value為host與端口號。也支持帶驗(yàn)證的代理,更多細(xì)節(jié)見官方文檔。
接下來要解決的問題就是從頁面中分離出URL. 這就需要正則表達(dá)式。
正則表達(dá)式
正則表達(dá)式相關(guān)的函數(shù)位于Python的re模塊中,使用前需import re
findall函數(shù)返回字符串中所有滿足給定正則式的子串:
aelems = re.findall(‘‘, content)
findall的第一個(gè)參數(shù)是正則式,第二個(gè)參數(shù)是字符串,返回值是字符串?dāng)?shù)組,包含content中所有滿足給定正則式的子串。上述代碼返回所有以結(jié)尾的子串,即所有的標(biāo)簽。對網(wǎng)頁HTML代碼應(yīng)用此過濾可獲取所有超鏈接。如果需要進(jìn)一步提高過濾的精確度,例如只需要鏈接指向本博客(假設(shè)當(dāng)前博客是http://myblog.wordpress.com),且URL為絕對地址,則可以使用更精確的正則式,例如’‘.
獲取到了標(biāo)簽,就需要進(jìn)一步提取其中的URL,這里推薦match函數(shù)。match函數(shù)的作用是將滿足指定正則式的子串的其中一部分返回。例如對于以下字符串(假設(shè)存于aelem元素中):
RSS Feed
如果需要提取出其中的URL(即http://myblog.wordpress.com/rss),則可以如下的match調(diào)用:
matches = re.match(‘
0號元素為’
這里的正則式對應(yīng)改變
matches = re.match(‘href=”(.*)”’, aelem)
當(dāng)然,這種方法不能保證100%正確。最好的做法應(yīng)該還是用HTML Parser. 這里懶得搞了。
提取出URL之后,就要將URL加入U(xiǎn)RL庫。為了避免重復(fù)訪問,需要對URL去重復(fù),這就引出了下一節(jié)中字典的使用。
字典
字典,一種存儲key-value對的關(guān)聯(lián)容器,對應(yīng)C++里的stl::hash_map,Java里的java.util.HashMap以及C#中的Dictionary. 由于key具有唯一性,因此字典可以用來去重。當(dāng)然,也可以用set,很多set就是將map簡單包裝一下,例如java.util.HashSet和stl::hash_set.
要使用字典構(gòu)建一個(gè)URL庫,首先我們需要考慮一下URL庫需要做什么:
URL去重:URL從HTML代碼中抽取出來后,如果是已經(jīng)加入U(xiǎn)RL庫的URL,則不加入U(xiǎn)RL庫
取新URL:從URL庫中取一個(gè)還沒訪問過的URL進(jìn)行下一次爬取
為了同時(shí)做到1、2,有以下兩種直觀的做法:
用一個(gè)url字典,其中URL作為key,是否已訪問(True/False)作為value;
用兩個(gè)字典,其中一個(gè)用來去重,另一個(gè)用來存放還沒訪問過的URL.
這里簡單起見,用的是第2種方法:
起始URL
starturl = ‘http://myblog.wordpress.com/‘;
全部URL,用于URL去重
totalurl[starturl] = True
未訪問URL,用于維護(hù)未訪問URL列表
unusedurl[starturl] = True
中間省略若干代碼
取下一個(gè)未用的URL
nexturl = unusedurl.keys()[0];
將該URL從unusedurl中刪除
del unusedurl[nexturl]
獲取頁面內(nèi)容
content = get_file_content(nexturl)
抽取頁面中的URL
urllist = extract_url(content)
對于抽取出的每個(gè)URL
for url in urllist:
如果該URL不存在于totalurl中
ifnot totalurl.has_key(url):
那么它一定是不重復(fù)的,將其加入totalurl中
totalurl[url] = True并且加入為訪問列表中
unusedurl[url] = True結(jié)束
最后貼上完整的代碼:
import urllib2
import time
import re
totalurl = {}
unusedurl = {}
生成ProxyHandler對象
def get_proxy():
return urllib2.ProxyHandler({‘http’: “l(fā)ocalhost:8580”})
生成指向代理的url_opener
def get_proxy_http_opener(proxy):
return urllib2.build_opener(proxy)
獲取指定URL指向的網(wǎng)頁,調(diào)用了前兩個(gè)函數(shù)
def get_file_content(url):
opener = get_proxy_http_opener(get_proxy())
content = opener.open(url).read()
opener.close()
為方便正則匹配,將其中的換行符消掉
return content.replace(‘\r’, ”).replace(‘\n’, ”)
根據(jù)網(wǎng)頁HTML代碼抽取網(wǎng)頁標(biāo)題
def extract_title(content):
titleelem = re.findall(‘.*<\/title>’, content)[0]
return re.match(‘(.*)<\/title>’, titleelem).group(1).strip()
根據(jù)網(wǎng)頁HTML代碼抽取所有標(biāo)簽中的URL
def extract_url(content):
urllist = []
aelems = re.findall(‘’, content)
for aelem in aelems:
splits = aelem.split(”)
if len(splits) != 1:
aelem = splits[1]
print aelem
matches = re.match('href="(.*)"', aelem)if matches isnot None:
url = matches.group(1)
if re.match(‘http:\/\/myblog.wordpress.com.*’, url) isnot None:
urllist.append(url)
return urllist
獲取字符串格式的時(shí)間
def get_localtime():
return time.strftime(“%H:%M:%S”, time.localtime())
主函數(shù)
def begin_access():
starturl = 'http://myblog.wordpress.com/'; totalurl[starturl] = True unusedurl[starturl] = Trueprint’seq\ttime\ttitle\turl’
i = 0while i < 150:
nexturl = unusedurl.keys()[0];del unusedurl[nexturl]
content = get_file_content(nexturl)
for url in urllist:
ifnot totalurl.has_key(url):
totalurl[url] = True
unusedurl[url] = True
print’%d\t%s\t%s\t%s’ %(i, get_localtime(), title, nexturl)
i = i + 1time.sleep(2)調(diào)用主函數(shù)
begin_access()
總結(jié)
以上是生活随笔為你收集整理的第一个Python程序——博客自动访问脚本的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python程序调用函数的过程_Pyth
- 下一篇: python网络安全工具箱界面_Pyth