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