2009-09-26 24 views

回答

25

作爲概述,您將需要執行四項主要任務:

  • 提交請求(S)的網站,
  • 檢索從網站的響應(S)
  • 解析這些反應
  • 有一定的邏輯在上面的任務進行迭代,與導航相關的(在結果列表「下一個」頁)
參數

http請求和響應處理是使用Python標準庫的urlliburllib2中的方法和類完成的。 HTML頁面的解析可以用Python的標準庫的HTMLParser或與其他模塊,如Beautiful Soup

下面的代碼片段來完成演示請求和接收在這個問題表示該網站的搜索。這個網站是ASP驅動的,因此我們需要確保我們發送多個表單字段,其中一些表單字段具有'可怕的'值,因爲ASP邏輯使用它們來維護狀態並在一定程度上驗證請求。確實提交。這些請求必須使用http POST方法發送,因爲這是ASP應用程序所期望的。主要困難在於識別ASP期望的表單域和相關值(使用Python獲取頁面是很容易的部分)。

此代碼是功能性的,或者更確切地說,功能,直到我刪除了大部分VSTATE值,並且可能通過添加註釋來引入一個或多個錯字。

import urllib 
import urllib2 

uri = 'http://legistar.council.nyc.gov/Legislation.aspx' 

#the http headers are useful to simulate a particular browser (some sites deny 
#access to non-browsers (bots, etc.) 
#also needed to pass the content type. 
headers = { 
    'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13', 
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8', 
    'Content-Type': 'application/x-www-form-urlencoded' 
} 

# we group the form fields and their values in a list (any 
# iterable, actually) of name-value tuples. This helps 
# with clarity and also makes it easy to later encoding of them. 

formFields = (
    # the viewstate is actualy 800+ characters in length! I truncated it 
    # for this sample code. It can be lifted from the first page 
    # obtained from the site. It may be ok to hardcode this value, or 
    # it may have to be refreshed each time/each day, by essentially 
    # running an extra page request and parse, for this specific value. 
    (r'__VSTATE', r'7TzretNIlrZiKb7EOB3AQE ... ...2qd6g5xD8CGXm5EftXtNPt+H8B'), 

    # following are more of these ASP form fields 
    (r'__VIEWSTATE', r''), 
    (r'__EVENTVALIDATION', r'/wEWDwL+raDpAgKnpt8nAs3q+pQOAs3q/pQOAs3qgpUOAs3qhpUOAoPE36ANAve684YCAoOs79EIAoOs89EIAoOs99EIAoOs39EIAoOs49EIAoOs09EIAoSs99EI6IQ74SEV9n4XbtWm1rEbB6Ic3/M='), 
    (r'ctl00_RadScriptManager1_HiddenField', ''), 
    (r'ctl00_tabTop_ClientState', ''), 
    (r'ctl00_ContentPlaceHolder1_menuMain_ClientState', ''), 
    (r'ctl00_ContentPlaceHolder1_gridMain_ClientState', ''), 

    #but then we come to fields of interest: the search 
    #criteria the collections to search from etc. 
                 # Check boxes 
    (r'ctl00$ContentPlaceHolder1$chkOptions$0', 'on'), # file number 
    (r'ctl00$ContentPlaceHolder1$chkOptions$1', 'on'), # Legislative text 
    (r'ctl00$ContentPlaceHolder1$chkOptions$2', 'on'), # attachement 
                 # etc. (not all listed) 
    (r'ctl00$ContentPlaceHolder1$txtSearch', 'york'), # Search text 
    (r'ctl00$ContentPlaceHolder1$lstYears', 'All Years'), # Years to include 
    (r'ctl00$ContentPlaceHolder1$lstTypeBasic', 'All Types'), #types to include 
    (r'ctl00$ContentPlaceHolder1$btnSearch', 'Search Legislation') # Search button itself 
) 

# these have to be encoded  
encodedFields = urllib.urlencode(formFields) 

req = urllib2.Request(uri, encodedFields, headers) 
f= urllib2.urlopen(req)  #that's the actual call to the http site. 

# *** here would normally be the in-memory parsing of f 
#  contents, but instead I store this to file 
#  this is useful during design, allowing to have a 
#  sample of what is to be parsed in a text editor, for analysis. 

try: 
    fout = open('tmp.htm', 'w') 
except: 
    print('Could not open output file\n') 

fout.writelines(f.readlines()) 
fout.close() 

這就是爲了獲得最初的頁面。如上所述,那麼需要解析該頁面,即找到感興趣的部分並且適當地收集它們,並將它們存儲到文件/數據庫/無論哪個。這項工作可以通過很多方式完成:使用html解析器或XSLT類型的技術(實際上是在將html解析爲xml之後),或者甚至用於簡單的正則表達式。而且,人們通常提取的項目之一是「下一個信息」,即排序鏈接,其可以用於對服務器的新請求以獲得後續頁面。

這應該給你一個關於什麼「長手」的HTML抓取粗糙的味道。還有很多其他的方法來此,如專門的工具程序,在Mozilla的(火狐)腳本的GreaseMonkey插件,XSLT ...

+0

如果我使用Google Chrome,那麼我應該如何替換「HTTP_USER_AGENT」的值?我很抱歉,如果這個問題是愚蠢的,因爲我沒有做很多網絡的東西。謝謝! – taocp 2013-09-17 01:14:06

+0

@taocp,一個簡單的方法來知道什麼'HTTP_USER_AGENT'字符串用於給定的瀏覽器是訪問http://www.all-nettools.com/toolbox/environmental-variables-test.php此頁面將告訴你瀏覽器發送的頭部值,查找「HTTP_USER_AGENT」。實際的字符串取決於操作系統以及Chrome的特定版本和內部版本,但應該看起來像Mozilla/5.0(Windows NT 6.1; WOW64)AppleWebKit/537.36(KHTML,如Gecko)Chrome/29.0.1547.66 Safari/537.36' – mjv 2013-09-17 04:26:02

+0

非常感謝您的回覆。我試着用適當的值設置到我的Chrome瀏覽器的代碼。結果tmp.htm文件顯示「找不到結果」,而當我在網站上放置「約克」時,它會返回很多結果。你知道爲什麼嗎? – taocp 2013-09-19 00:29:41

0

」假設我們需要從相應的下拉菜單中選擇「所有年份」和「所有類型」。「

做這些選擇做與在最終提交的URL。

畢竟,它相當於通過urllib2發送的HTTP請求。

知道如何「從各自的下拉菜單‘所有年份’和‘所有類型’」你做以下事情。

  1. 選擇「從各自的下拉菜單‘所有年份’和‘所有類型的’」

  2. 注意這實際上是提交的URL。

  3. 使用此URL中urllib2

+0

顯然,網頁是需要POST形式,但這個想法是一樣的:注意的表單字段名稱以及與「所有年份」相關的值以及所有類型的值,並使用urlib2.Request獲取數據。 – mjv 2009-09-26 03:38:36

+0

我使用查爾斯網絡調試代理來觀看所有的http流量,當我衝浪這個網站並提交查詢時,這個網址是完全靜態的。它根本不包含任何參數。有一些表單數據會以某種方式傳遞 - 我猜 - ajax - 但我不知道如何將表單數據提交給服務器。這一切對我來說都是無法理解的。我無法通過操作url來提交查詢這一事實令我困惑。 – twneale 2009-09-26 03:39:43

+0

一旦你從這個頁面得到結果,如果你希望得到結果,你可以使用python模塊HTMLParser或Beautifulsoup來解析html頁面。此外,抓取可能會涉及更多的urlib2調用,以導航到結果的下一頁。 – mjv 2009-09-26 03:41:06

4

大多數ASP.NET網站(包括您引用的網站)實際上都會使用HTTP POST動詞而不是GET動詞將其查詢發回給自己。這就是爲什麼這個URL沒有像你指出的那樣變化。

你需要做的是看看生成的HTML並捕獲所有的表單值。一定要捕獲所有的表單值,因爲它們中的一些用於頁面驗證,沒有它們,您的POST請求將被拒絕。

除了驗證之外,關於抓取和發佈的ASPX頁面與其他網絡技術沒有區別。

5

Selenium是使用這類任務的一大利器。您可以指定想要輸入的表單值,並在幾行python代碼中以字符串形式檢索響應頁面的html。 使用Selenium可能不需要手動模擬一個有效的發佈請求及其所有隱藏變量,因爲我經過多次試驗和錯誤發現。

+1

你能提供一些代碼片段嗎? – taocp 2013-09-12 02:19:48

+0

我成功地連接,登錄和點擊使用硒的鏈接我被困在你想從頁面抓取數據的部分。由於即使點擊後URI也保持不變,這會產生問題。 – 2017-01-12 10:47:31

4

其他答案中的代碼很有用;如果沒有它,我永遠不會寫我的抓取工具。

我遇到的一個問題是cookies。我在地上爬來爬去,該網站使用Cookies來記錄會話ID /安全性的東西,所以我不得不添加代碼讓我的履帶式工作:

添加此導入:

import cookielib    

初始化cookie的東西:

COOKIEFILE = 'cookies.lwp'   # the path and filename that you want to use to save your cookies in 
    cj = cookielib.LWPCookieJar()  # This is a subclass of FileCookieJar that has useful load and save methods 

安裝CookieJar使得它用作默認打開處理器默認CookieProcessor

cj.load(COOKIEFILE) 
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) 
    urllib2.install_opener(opener) 

要查看該網站是否使用什麼餅乾:

print 'These are the cookies we have received so far :' 

    for index, cookie in enumerate(cj): 
     print index, ' : ', cookie   

這節省了餅乾:

cj.save(COOKIEFILE)      # save the cookies