第一個Python程式——博客自動訪問腳本

来源:http://www.cnblogs.com/shouce/archive/2016/06/03/5555073.html
-Advertisement-
Play Games

動機 今天有朋友寫信說他認為自己的wordpress博客內顯示的訪問統計信息不正常,希望我能為他製造一些訪問信息,供他對比。朋友提出的請求是在短時間內快速打開100個不同的博客頁面,以便他從產生的訪問量變化中理解博客訪問數據。 本人作為一個搞電腦的人,有把任何重覆性勞動自動化的衝動,所以雖然點開1 ...


動機

今天有朋友寫信說他認為自己的wordpress博客內顯示的訪問統計信息不正常,希望我能為他製造一些訪問信息,供他對比。朋友提出的請求是在短時間內快速打開100個不同的博客頁面,以便他從產生的訪問量變化中理解博客訪問數據。

本人作為一個搞電腦的人,有把任何重覆性勞動自動化的衝動,所以雖然點開100個網頁的任務手工做並不複雜,但還是從一開始就徹底否定了。剛好想學Python很久了,於是就拿這次的小機會來學習一把,順便記錄下第一次的Python學習成果。

本文使用Python 2.7.3實現了一個自動訪問博客的腳本,涉及以下技術點:

  • 語言基礎
    • 容器(線性表、字典)
    • 邏輯分支、迴圈
    • 控制台格式化輸出
  • HTTP客戶端網路編程
    • 處理HTTP請求
    • 使用HTTP代理伺服器
  • Python正則表達式

總覽

自動訪問博客頁面這個操作實際上和網路爬蟲做的事情差不多,基本流程如下:

圖1 博客自動訪問器工作原理

  1. 給訪問器一個開始位置(例如博客首頁URL)
  2. 訪問器將URL指向的網頁爬回(爬回網頁這個操作本身相當於在瀏覽器中打開頁面)
  3. 2中爬回的網頁交給分析器分析。分析器分析後提取出其中的URL加入待訪問URL列表,即URL庫。然後從URL庫中取出下一個要訪問的頁面URL
  4. 迴圈2、3步,直到達到某一終止條件程式退出

剛開始編程時,我們什麼都沒有,只有一個博客首頁的URL。緊跟著這個URL需要解決的問題就是如何編程爬取URL指向的頁面。爬取到了頁面,才能算是訪問了博客,也才能獲得頁面的內容,從而提取更多的URL,進行更進一步的爬取。

這樣一來就帶來瞭如何根據URL獲取頁面信息的問題,要解決這個問題,需要用到HTTP客戶端編程,這也是接下來一節解決的問題。

urllib2:HTTP客戶端編程

Python中可以實現HTTP客戶端編程的庫有好幾個,例如httplib, urllib, urllib2等。使用urllib2是因為它功能強大,使用簡單,並且可以很容易地使用HTTP代理。

使用urllib2創建一個HTTP連接並獲取頁面非常簡單,只需要3步:

import urllib2
opener = urllib2.build_opener()
file = opener.open(url)
content = file.read()

content即為HTTP請求響應的報文體,即網頁的HTML代碼。如果需要設置代理,在調用build_opener()時傳入一個參數即可:

?
opener = urllib2.build_opener(urllib2.ProxyHandler({'http': "localhost:8580"}))

ProxyHandler函數接受一個字典類型的參數,其中key為協議名稱,value為host與埠號。也支持帶驗證的代理,更多細節見官方文檔

接下來要解決的問題就是從頁面中分離出URL. 這就需要正則表達式。

正則表達式

正則表達式相關的函數位於Python的re模塊中,使用前需import re

findall函數返回字元串中所有滿足給定正則式的子串:

aelems = re.findall('<a href=".*<\/a>', content)

findall的第一個參數是正則式,第二個參數是字元串,返回值是字元串數組,包含content中所有滿足給定正則式的子串。上述代碼返回所有以<a href="開頭,</a>結尾的子串,即所有的<a>標簽。對網頁HTML代碼應用此過濾可獲取所有超鏈接。如果需要進一步提高過濾的精確度,例如只需要鏈接指向本博客(假設當前博客是http://myblog.wordpress.com),且URL為絕對地址,則可以使用更精確的正則式,例如'<a href="http\:\/\/myblog\.wordpress\.com.*<\/a>'.

獲取到了<a>標簽,就需要進一步提取其中的URL,這裡推薦match函數。match函數的作用是將滿足指定正則式的子串的其中一部分返回。例如對於以下字元串(假設存於aelem元素中):

<a href="http://myblog.wordpress.com/rss">RSS Feed</a>

如果需要提取出其中的URL(即http://myblog.wordpress.com/rss),則可以如下的match調用:

matches = re.match('<a href="(.*)"', aelem)

匹配成功時,match返回MatchObject對象,否則返回None. 對於MatchObject,可以使用groups()方法獲取其中包含的所有元素,也可以通過group(下標)獲取,註意group()方法的下標是從1開始的。

以上示例僅對只含有href一個屬性的<a>元素有效,如果<a>元素中href屬性後還有別的屬性,則按照最長匹配原則,上面的match調用會返回不正確的值:

<a href="http://myblog.wordpress.com/rss" alt="RSS Feed - Yet Another Wordpress Blog">RSS Feed</a>

將會匹配為:http://myblog.wordpress.com/rss" alt="RSS Feed - Yet Another Wordpress Blog

目前對於這種情況還沒有特別好的解決方法,我的做法是先按照空格split一下再匹配。由於href通常都是<a>中第一個出現的屬性,所以可以簡單地如下處理:

splits = aelem.split(' ')
#0號元素為'<a',1號元素為'href="http://myblog.wordpress.com/"'
aelem = splits[1]
#這裡的正則式對應改變
matches = re.match('href="(.*)"', aelem)

當然,這種方法不能保證100%正確。最好的做法應該還是用HTML Parser. 這裡懶得搞了。

提取出URL之後,就要將URL加入URL庫。為了避免重覆訪問,需要對URL去重覆,這就引出了下一節中字典的使用。

字典

字典,一種存儲key-value對的關聯容器,對應C++里的stl::hash_map,Java里的java.util.HashMap以及C#中的Dictionary. 由於key具有唯一性,因此字典可以用來去重。當然,也可以用set,很多set就是將map簡單包裝一下,例如java.util.HashSet和stl::hash_set.

要使用字典構建一個URL庫,首先我們需要考慮一下URL庫需要做什麼:

  1. URL去重:URL從HTML代碼中抽取出來後,如果是已經加入URL庫的URL,則不加入URL庫
  2. 取新URL:從URL庫中取一個還沒訪問過的URL進行下一次爬取

為了同時做到1、2,有以下兩種直觀的做法:

  1. 用一個url字典,其中URL作為key,是否已訪問(True/False)作為value;
  2. 用兩個字典,其中一個用來去重,另一個用來存放還沒訪問過的URL.

這裡簡單起見,用的是第2種方法:

複製代碼
#起始URL
starturl = 'http://myblog.wordpress.com/';
#全部URL,用於URL去重
totalurl[starturl] = True
#未訪問URL,用於維護未訪問URL列表
unusedurl[starturl] = True

#中間省略若幹代碼

#取下一個未用的URL
nexturl = unusedurl.keys()[0];
#將該URL從unusedurl中刪除
del unusedurl[nexturl]
#獲取頁面內容
content = get_file_content(nexturl)
#抽取頁面中的URL
urllist = extract_url(content)
#對於抽取出的每個URL
for url in urllist:
#如果該URL不存在於totalurl中
if not totalurl.has_key(url):
#那麼它一定是不重覆的,將其加入totalurl中
totalurl[url] = True
#並且加入為訪問列表中
unusedurl[url] = True
複製代碼

結束

最後貼上完整的代碼:

複製代碼
import urllib2
import time
import re

totalurl = {}
unusedurl = {}

#生成ProxyHandler對象
def get_proxy():
return urllib2.ProxyHandler({'http': "localhost:8580"})

#生成指向代理的url_opener
def get_proxy_http_opener(proxy):
return urllib2.build_opener(proxy)

#獲取指定URL指向的網頁,調用了前兩個函數
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', '')

#根據網頁HTML代碼抽取網頁標題
def extract_title(content):
titleelem = re.findall('<title>.*<\/title>', content)[0]
return re.match('<title>(.*)<\/title>', titleelem).group(1).strip()

#根據網頁HTML代碼抽取所有<a>標簽中的URL
def extract_url(content):
urllist = []
aelems = re.findall('<a href=".*?<\/a>', content)
for aelem in aelems:
splits = aelem.split(' ')
if len(splits) != 1:
aelem = splits[1]
##print aelem
matches = re.match('href="(.*)"', aelem)
if matches is not None:
url = matches.group(1)
if re.match('http:\/\/myblog\.wordpress\.com.*', url) is not None:
urllist.append(url)
return urllist

#獲取字元串格式的時間
def get_localtime():
return time.strftime("%H:%M:%S", time.localtime())

#主函數
def begin_access():

starturl = 'http://myblog.wordpress.com/';
totalurl[starturl] = True
unusedurl[starturl] = True
print 'seq\ttime\ttitle\turl'

i = 0
while i < 150:

nexturl = unusedurl.keys()[0];
del unusedurl[nexturl]
content = get_file_content(nexturl)

title = extract_title(content)
urllist = extract_url(content)

for url in urllist:
if not 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 + 1
time.sleep(2)

#調用主函數
begin_access()
複製代碼
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 創建自定義視圖引擎 一般情況下直接使用MVC框架自帶的內建視圖引擎即可,但如果想知道視圖引擎是如何工作的,就需要從建立一個自定義視圖引擎開始了。通過之前的學習我們都知道了內建視圖引擎包括Razor和ASPX兩種,ASPX是針對舊版本MVC程式的,他主要是維護舊版本MVC應用程式,保持系統的相容性而保 ...
  • 我們可以使用jdk內置的 Locale 類來實現java語言的國際化。使用方法很簡單: 命名格式為: xxx_語言代碼_國家代碼 我們這裡 用到了 中英文名稱為: RetrievingRequestXMLError=Error retrieving requestXML from HTTP POST ...
  • #include <iostream>#include <algorithm>#include <map> using namespace std; int main() { int n, i, *arr1, *arr2; map<int, int> match; while(cin>>n) { a ...
  • Java容器可以說是增強程式員編程能力的基本工具,本系列將帶您深入理解容器類。 容器的用途 如果對象的數量與生命周期都是固定的,自然我們也就不需要很複雜的數據結構。 我們可以通過創建引用來持有對象,如 也可以通過數組來持有多個對象,如 然而,一般情況下,我們並不知道要創建多少對象,或者以何種方式創建 ...
  • 項目在變,需求在變,不變的永遠是敲擊鍵盤的程式員..... PDF 生成後,有時候需要在PDF上面添加一些其他的內容,比如文字,圖片.... 經歷幾次失敗的嘗試,終於獲取到了正確的代碼書寫方式。 在此記錄總結,方便下次以不變應萬變,需要的 jar 請移步:生成PDF全攻略 上述的這段代碼算是在原有 ...
  • 摘自aardio培訓群 www.aardio.com ...
  • 概述 之前學習的 Agent,GenSever以及GenEvent,都是用來管理狀態或者處理消息的。 但是在很多時候,我們需要的是執行某個任務,這時如果使用 GenSever 或者 GenEvent,就會顯得比較笨重。 這時,我們就可以使用 Task 模塊,使用 Task 模塊時註意以下幾點: 1. ...
  • 1、Tesseract介紹 tesseract 是一個google支持的開源ocr項目,其項目地址:https://github.com/tesseract-ocr/tesseract,目前最新的源碼可以在這裡下載。 實際使用tesseract ocr也有兩種方式:1- 動態庫方式 libtesse ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...