一、寫在前面
我寫爬蟲已經(jīng)寫了一段時間了,對于那些使用GET請求或者POST請求的網(wǎng)頁,爬取的時候都還算得心應(yīng)手。不過最近遇到了一個有趣的網(wǎng)站,雖然爬取的難度不大,不過因為表單提交的存在,所以一開始還是有點(diǎn)摸不著頭腦。至于最后怎么解決的,請慢慢往下看。
?
二、頁面分析
這次爬取的網(wǎng)站是:https://www.ctic.org/crm?tdsourcetag=s_pctim_aiomsg,該網(wǎng)站提供了美國的一些農(nóng)田管理的數(shù)據(jù)。要查看具體的數(shù)據(jù),需要選擇年份、單位、地區(qū)、作物種類等,如下圖:
根據(jù)以往的經(jīng)驗,這種表單提交都是通過ajax來完成的,所以熟練地按F12打開開發(fā)者工具,選擇XHR選項,然后點(diǎn)擊“View Summary”,結(jié)果卻什么都沒有......
這是怎么回事?不急,切換到All看一下有沒有什么可疑的東西。果然就找到了下面這個,可以看到在Form Data中包含了很多參數(shù),而且可以很明顯看出來是一些年份、地區(qū)等信息,這就是表單提交的內(nèi)容:
可以注意到在這些參數(shù)中有一個_csrf,很明顯是一個加密參數(shù),那么要怎么得到這個參數(shù)呢?返回填寫表單的網(wǎng)頁,在開發(fā)者工具中切換到Elements,然后搜索_csrf看看,很快就找到了如下信息:
其余參數(shù)都是表單中所選擇的內(nèi)容,只要對應(yīng)填寫就行了。不過這個請求返回的狀態(tài)碼是302,為什么會是302呢?302狀態(tài)碼的使用場景是請求的資源暫時駐留在不同的URI下,因此還需要繼續(xù)尋找。
通過進(jìn)一步查找可知,最終的URL是:https://www.ctic.org/crm/?action=result。
三、主要步驟?
1.爬取郡縣信息
可以看到表單中包含了地區(qū)、州、郡縣選項,在填寫表單的時候,這一部分都是通過JS來實現(xiàn)的。打開開發(fā)者工具,然后在頁面上點(diǎn)選County,選擇Region和State,就能在開發(fā)者工具中找到相應(yīng)的請求。主要有兩個請求,如下:
https://www.ctic.org/admin/custom/crm/getstates/
https://www.ctic.org/admin/custom/crm/getcounties/
這兩個請求返回的結(jié)果格式如下圖:
這里可以使用正則匹配,也可以使用lxml來解析,我選擇使用后者。示例代碼如下:
1 from lxml import etree 2 3 4 html = ' "Autauga<\/option>Baldwin<\/option>Barbour<\/option>Bibb<\/option>Blount<\/option>Bullock<\/option>Butler<\/option>Calhoun<\/option>Chambers<\/option>Cherokee<\/option>Chilton<\/option>Choctaw<\/option>Clarke<\/option>Clay<\/option>Cleburne<\/option>Coffee<\/option>Colbert<\/option>Conecuh<\/option>Coosa<\/option>Covington<\/option>Crenshaw<\/option>Cullman<\/option>Dale<\/option>Dallas<\/option>Dekalb<\/option>Elmore<\/option>Escambia<\/option>Etowah<\/option>Fayette<\/option>Franklin<\/option>Geneva<\/option>Greene<\/option>Hale<\/option>Henry<\/option>Houston<\/option>Jackson<\/option>Jefferson<\/option>Lamar<\/option>Lauderdale<\/option>Lawrence<\/option>Lee<\/option>Limestone<\/option>Lowndes<\/option>Macon<\/option>Madison<\/option>Marengo<\/option>Marion<\/option>Marshall<\/option>Mobile<\/option>Monroe<\/option>Montgomery<\/option>Morgan<\/option>Perry<\/option>Pickens<\/option>Pike<\/option>Randolph<\/option>Russell<\/option>Shelby<\/option>St Clair<\/option>Sumter<\/option>Talladega<\/option>Tallapoosa<\/option>Tuscaloosa<\/option>Walker<\/option>Washington<\/option>Wilcox<\/option>Winston<\/option>" ' 5 et = etree.HTML(html) 6 result = et.xpath( ' //option/text() ' ) 7 result = [i.rstrip( ' " ' ) for i in result] 8 print (result)
上面代碼輸出的結(jié)果為:
['Autauga', 'Baldwin', 'Barbour', 'Bibb', 'Blount', 'Bullock', 'Butler', 'Calhoun', 'Chambers', 'Cherokee', 'Chilton', 'Choctaw', 'Clarke', 'Clay', 'Cleburne', 'Coffee', 'Colbert', 'Conecuh', 'Coosa', 'Covington', 'Crenshaw', 'Cullman', 'Dale', 'Dallas', 'Dekalb', 'Elmore', 'Escambia', 'Etowah', 'Fayette', 'Franklin', 'Geneva', 'Greene', 'Hale', 'Henry', 'Houston', 'Jackson', 'Jefferson', 'Lamar', 'Lauderdale', 'Lawrence', 'Lee', 'Limestone', 'Lowndes', 'Macon', 'Madison', 'Marengo', 'Marion', 'Marshall', 'Mobile', 'Monroe', 'Montgomery', 'Morgan', 'Perry', 'Pickens', 'Pike', 'Randolph', 'Russell', 'Shelby', 'St Clair', 'Sumter', 'Talladega', 'Tallapoosa', 'Tuscaloosa', 'Walker', 'Washington', 'Wilcox', 'Winston']
獲取所有郡縣信息的思路為分別選擇四個地區(qū),然后遍歷每個地區(qū)下面的州,再遍歷每個州所包含的郡縣,最終得到所有郡縣信息。
2.爬取農(nóng)田數(shù)據(jù)
? 在得到郡縣信息之后,就可以構(gòu)造獲取農(nóng)田數(shù)據(jù)的請求所需要的參數(shù)了。在獲取農(nóng)田數(shù)據(jù)之前,需要向服務(wù)器發(fā)送一個提交表單的請求,不然是得不到數(shù)據(jù)的。在我測試的時候,發(fā)送提交表單的請求的時候,返回的狀態(tài)碼并不是302,不過這并不影響之后的操作,所以可以忽略掉。
需要注意的是,參數(shù)中是有一個年份信息的,前面我一直是默認(rèn)用的2011,不過要爬取更多信息的話,還需要改變這個年份信息。而通過選擇頁面元素可以知道,這個網(wǎng)站提供了16個年份的農(nóng)田數(shù)據(jù)信息,這16個年份為:
[1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,2002,2004,2006,2007,2008,2011]
得到這些年份信息之后,就可以和前面的郡縣信息進(jìn)行排列組合得到所有提交表單的請求所需要的參數(shù)。說道排列組合,一般會用for循環(huán)來實現(xiàn),不過這里推薦一種方法,就是使用itertools.product,使用示例如下:
1 from itertools import product 2 3 a = [1, 2, 3 ] 4 b = [2, 4 ] 5 result = product(a, b) 6 for i in result: 7 print (i, end= " " ) 8 9 10 # (1, 2) (1, 4) (2, 2) (2, 4) (3, 2) (3, 4)
下面是農(nóng)田數(shù)據(jù)的部分截圖,其中包含了很多種類的作物,還有對應(yīng)的耕地面積信息,不過在這個表中有些我們不需要的信息,比如耕地面積總量信息,還有空白行,這都是干擾數(shù)據(jù),在解析的時候要清洗掉。
解析農(nóng)田數(shù)據(jù)部分的代碼如下:
1
et =
etree.HTML(html)
2
crop_list = et.xpath(
'
//*[@id="crm_results_eight"]/tbody/tr/td[1]/text()
'
) # 作物名稱
3
area_list = et.xpath(
'
//*[@id="crm_results_eight"]/tbody/tr/td[2]/text()
'
) # 耕地面積
4
conservation_list = et.xpath(
'
//*[@id="crm_results_eight"]/tbody/tr/td[6]/text()
'
) # 受保護(hù)耕地面積
5
crop_list = crop_list[:-3
]
6
area_list = area_list[:-3
]
7
conservation_list = conservation_list[:-3]
?
?完整代碼已上傳到GitHub!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
